@@ -8,7 +8,11 @@ use diesel::prelude::*;
88/// This struct is used to load all versions of a crate from the database,
99/// without loading all the additional data unnecessary for default version
1010/// resolution.
11- #[ derive( Clone , Debug , Queryable , Selectable ) ]
11+ ///
12+ /// It implements [Ord] in a way that sorts versions by the criteria specified
13+ /// in the [update_default_version] function documentation. The default version
14+ /// will be the "maximum" element in a sorted list of versions.
15+ #[ derive( Clone , Debug , PartialEq , Eq , Queryable , Selectable ) ]
1216#[ diesel( table_name = versions) ]
1317#[ diesel( check_for_backend( diesel:: pg:: Pg ) ) ]
1418struct Version {
@@ -23,6 +27,22 @@ impl Version {
2327 fn is_prerelease ( & self ) -> bool {
2428 !self . num . pre . is_empty ( )
2529 }
30+
31+ fn ord_tuple ( & self ) -> ( bool , bool , & semver:: Version , i32 ) {
32+ ( !self . yanked , !self . is_prerelease ( ) , & self . num , self . id )
33+ }
34+ }
35+
36+ impl PartialOrd for Version {
37+ fn partial_cmp ( & self , other : & Self ) -> Option < std:: cmp:: Ordering > {
38+ Some ( self . cmp ( other) )
39+ }
40+ }
41+
42+ impl Ord for Version {
43+ fn cmp ( & self , other : & Self ) -> std:: cmp:: Ordering {
44+ self . ord_tuple ( ) . cmp ( & other. ord_tuple ( ) )
45+ }
2646}
2747
2848/// Updates the `default_versions` table entry for the specified crate.
@@ -88,6 +108,8 @@ pub fn verify_default_version(crate_id: i32, conn: &mut impl Conn) -> QueryResul
88108}
89109
90110fn calculate_default_version ( crate_id : i32 , conn : & mut impl Conn ) -> QueryResult < Version > {
111+ use diesel:: result:: Error :: NotFound ;
112+
91113 debug ! ( "Loading all versions for the crate…" ) ;
92114 let versions = versions:: table
93115 . filter ( versions:: crate_id. eq ( crate_id) )
@@ -96,26 +118,16 @@ fn calculate_default_version(crate_id: i32, conn: &mut impl Conn) -> QueryResult
96118
97119 debug ! ( "Found {} versions" , versions. len( ) ) ;
98120
99- find_default_version ( & versions)
100- . cloned ( )
101- . ok_or ( diesel:: result:: Error :: NotFound )
102- }
103-
104- fn find_default_version ( versions : & [ Version ] ) -> Option < & Version > {
105- highest ( versions, |v| !v. is_prerelease ( ) && !v. yanked )
106- . or_else ( || highest ( versions, |v| !v. yanked ) )
107- . or_else ( || highest ( versions, |_| true ) )
108- }
109-
110- fn highest ( versions : & [ Version ] , filter : impl FnMut ( & & Version ) -> bool ) -> Option < & Version > {
111- versions. iter ( ) . filter ( filter) . max_by_key ( |v| & v. num )
121+ versions. into_iter ( ) . max ( ) . ok_or ( NotFound )
112122}
113123
114124#[ cfg( test) ]
115125mod tests {
116126 use super :: * ;
117127 use crate :: schema:: crates;
118128 use crate :: test_util:: test_db_connection;
129+ use insta:: assert_snapshot;
130+ use std:: fmt:: Write ;
119131
120132 fn v ( num : & str , yanked : bool ) -> Version {
121133 let num = semver:: Version :: parse ( num) . unwrap ( ) ;
@@ -124,14 +136,10 @@ mod tests {
124136
125137 #[ test]
126138 fn test_find_default_version ( ) {
127- let check = | versions, expected| {
128- let default_version = assert_some ! ( find_default_version ( versions) ) ;
139+ fn check ( versions : & [ Version ] , expected : & str ) {
140+ let default_version = assert_some ! ( versions. iter ( ) . max ( ) ) ;
129141 assert_eq ! ( default_version. num. to_string( ) , expected) ;
130- } ;
131-
132- // No versions
133- let versions = vec ! [ ] ;
134- assert_none ! ( find_default_version( & versions) ) ;
142+ }
135143
136144 // Only a single version
137145 let versions = vec ! [ v( "1.0.0" , false ) ] ;
@@ -179,6 +187,51 @@ mod tests {
179187 check ( & versions, "1.0.0-beta.3" ) ;
180188 }
181189
190+ #[ test]
191+ fn test_ord ( ) {
192+ let mut versions = vec ! [
193+ v( "1.0.0" , false ) ,
194+ v( "1.0.0-beta.1" , false ) ,
195+ v( "1.0.0-beta.2" , false ) ,
196+ v( "1.0.0-beta.3" , false ) ,
197+ v( "1.0.1" , true ) ,
198+ v( "1.0.2" , false ) ,
199+ v( "1.1.0" , false ) ,
200+ v( "1.1.1-beta.1" , true ) ,
201+ v( "1.1.1" , true ) ,
202+ v( "1.0.3" , false ) ,
203+ v( "2.0.0-beta.1" , false ) ,
204+ ] ;
205+
206+ versions. sort ( ) ;
207+
208+ assert_snapshot ! ( format_versions( & versions) , @r#"
209+ 1.1.1-beta.1 (yanked)
210+ 1.0.1 (yanked)
211+ 1.1.1 (yanked)
212+ 1.0.0-beta.1
213+ 1.0.0-beta.2
214+ 1.0.0-beta.3
215+ 2.0.0-beta.1
216+ 1.0.0
217+ 1.0.2
218+ 1.0.3
219+ 1.1.0
220+ "# ) ;
221+ }
222+
223+ fn format_versions ( versions : & [ Version ] ) -> String {
224+ let mut buf = String :: with_capacity ( versions. len ( ) * 20 ) ;
225+ for v in versions {
226+ write ! ( buf, "{}" , v. num) . unwrap ( ) ;
227+ if v. yanked {
228+ buf. push_str ( " (yanked)" ) ;
229+ }
230+ buf. push ( '\n' ) ;
231+ }
232+ buf
233+ }
234+
182235 fn create_crate ( name : & str , conn : & mut impl Conn ) -> i32 {
183236 diesel:: insert_into ( crates:: table)
184237 . values ( crates:: name. eq ( name) )
0 commit comments