1- use crate :: models;
1+ use crate :: models:: { Crate , CrateVersions , Dependency , Version } ;
2+ use crate :: schema:: { crates, dependencies} ;
23use crate :: util:: diesel:: Conn ;
34use anyhow:: Context ;
5+ use crates_io_index:: features:: split_features;
46use diesel:: prelude:: * ;
57use sentry:: Level ;
68
79#[ instrument( skip_all, fields( krate. name = ?name) ) ]
810pub fn get_index_data ( name : & str , conn : & mut impl Conn ) -> anyhow:: Result < Option < String > > {
911 debug ! ( "Looking up crate by name" ) ;
10- let Some ( krate) : Option < models:: Crate > =
11- models:: Crate :: by_exact_name ( name) . first ( conn) . optional ( ) ?
12- else {
12+ let Some ( krate) : Option < Crate > = Crate :: by_exact_name ( name) . first ( conn) . optional ( ) ? else {
1313 return Ok ( None ) ;
1414 } ;
1515
1616 debug ! ( "Gathering remaining index data" ) ;
17- let crates = krate
18- . index_metadata ( conn)
19- . context ( "Failed to gather index metadata" ) ?;
17+ let crates = index_metadata ( & krate, conn) . context ( "Failed to gather index metadata" ) ?;
2018
2119 // This can sometimes happen when we delete versions upon owner request
2220 // but don't realize that the crate is now left with no versions at all.
@@ -39,3 +37,81 @@ pub fn get_index_data(name: &str, conn: &mut impl Conn) -> anyhow::Result<Option
3937
4038 Ok ( Some ( str) )
4139}
40+
41+ /// Gather all the necessary data to write an index metadata file
42+ pub fn index_metadata (
43+ krate : & Crate ,
44+ conn : & mut impl Conn ,
45+ ) -> QueryResult < Vec < crates_io_index:: Crate > > {
46+ let mut versions: Vec < Version > = krate. all_versions ( ) . load ( conn) ?;
47+
48+ // We sort by `created_at` by default, but since tests run within a
49+ // single database transaction the versions will all have the same
50+ // `created_at` timestamp, so we sort by semver as a secondary key.
51+ versions. sort_by_cached_key ( |k| ( k. created_at , semver:: Version :: parse ( & k. num ) . ok ( ) ) ) ;
52+
53+ let deps: Vec < ( Dependency , String ) > = Dependency :: belonging_to ( & versions)
54+ . inner_join ( crates:: table)
55+ . select ( ( dependencies:: all_columns, crates:: name) )
56+ . load ( conn) ?;
57+
58+ let deps = deps. grouped_by ( & versions) ;
59+
60+ versions
61+ . into_iter ( )
62+ . zip ( deps)
63+ . map ( |( version, deps) | {
64+ let mut deps = deps
65+ . into_iter ( )
66+ . map ( |( dep, name) | {
67+ // If this dependency has an explicit name in `Cargo.toml` that
68+ // means that the `name` we have listed is actually the package name
69+ // that we're depending on. The `name` listed in the index is the
70+ // Cargo.toml-written-name which is what cargo uses for
71+ // `--extern foo=...`
72+ let ( name, package) = match dep. explicit_name {
73+ Some ( explicit_name) => ( explicit_name, Some ( name) ) ,
74+ None => ( name, None ) ,
75+ } ;
76+
77+ crates_io_index:: Dependency {
78+ name,
79+ req : dep. req ,
80+ features : dep. features ,
81+ optional : dep. optional ,
82+ default_features : dep. default_features ,
83+ kind : Some ( dep. kind . into ( ) ) ,
84+ package,
85+ target : dep. target ,
86+ }
87+ } )
88+ . collect :: < Vec < _ > > ( ) ;
89+
90+ deps. sort ( ) ;
91+
92+ let features = version. features ( ) . unwrap_or_default ( ) ;
93+ let ( features, features2) = split_features ( features) ;
94+
95+ let ( features2, v) = if features2. is_empty ( ) {
96+ ( None , None )
97+ } else {
98+ ( Some ( features2) , Some ( 2 ) )
99+ } ;
100+
101+ let krate = crates_io_index:: Crate {
102+ name : krate. name . clone ( ) ,
103+ vers : version. num . to_string ( ) ,
104+ cksum : version. checksum ,
105+ yanked : Some ( version. yanked ) ,
106+ deps,
107+ features,
108+ links : version. links ,
109+ rust_version : version. rust_version ,
110+ features2,
111+ v,
112+ } ;
113+
114+ Ok ( krate)
115+ } )
116+ . collect ( )
117+ }
0 commit comments