@@ -22,18 +22,133 @@ use crate::spanned::Spanned;
2222use crate :: utils:: { contains_skip, mk_sp} ;
2323use crate :: visitor:: FmtVisitor ;
2424
25+ /// Compare strings according to version sort (roughly equivalent to `strverscmp`)
26+ pub ( crate ) fn compare_as_versions ( left : & str , right : & str ) -> Ordering {
27+ let mut left = left. chars ( ) . peekable ( ) ;
28+ let mut right = right. chars ( ) . peekable ( ) ;
29+
30+ loop {
31+ // The strings are equal so far and not inside a number in both sides
32+ let ( l, r) = match ( left. next ( ) , right. next ( ) ) {
33+ // Is this the end of both strings?
34+ ( None , None ) => return Ordering :: Equal ,
35+ // If for one, the shorter one is considered smaller
36+ ( None , Some ( _) ) => return Ordering :: Less ,
37+ ( Some ( _) , None ) => return Ordering :: Greater ,
38+ ( Some ( l) , Some ( r) ) => ( l, r) ,
39+ } ;
40+ let next_ordering = match ( l. to_digit ( 10 ) , r. to_digit ( 10 ) ) {
41+ // If neither is a digit, just compare them
42+ ( None , None ) => Ord :: cmp ( & l, & r) ,
43+ // The one with shorter non-digit run is smaller
44+ // For `strverscmp` it's smaller iff next char in longer is greater than digits
45+ ( None , Some ( _) ) => Ordering :: Greater ,
46+ ( Some ( _) , None ) => Ordering :: Less ,
47+ // If both start numbers, we have to compare the numbers
48+ ( Some ( l) , Some ( r) ) => {
49+ if l == 0 || r == 0 {
50+ // Fraction mode: compare as if there was leading `0.`
51+ let ordering = Ord :: cmp ( & l, & r) ;
52+ if ordering != Ordering :: Equal {
53+ return ordering;
54+ }
55+ loop {
56+ // Get next pair
57+ let ( l, r) = match ( left. peek ( ) , right. peek ( ) ) {
58+ // Is this the end of both strings?
59+ ( None , None ) => return Ordering :: Equal ,
60+ // If for one, the shorter one is considered smaller
61+ ( None , Some ( _) ) => return Ordering :: Less ,
62+ ( Some ( _) , None ) => return Ordering :: Greater ,
63+ ( Some ( l) , Some ( r) ) => ( l, r) ,
64+ } ;
65+ // Are they digits?
66+ match ( l. to_digit ( 10 ) , r. to_digit ( 10 ) ) {
67+ // If out of digits, use the stored ordering due to equal length
68+ ( None , None ) => break Ordering :: Equal ,
69+ // If one is shorter, it's smaller
70+ ( None , Some ( _) ) => return Ordering :: Less ,
71+ ( Some ( _) , None ) => return Ordering :: Greater ,
72+ // If both are digits, consume them and take into account
73+ ( Some ( l) , Some ( r) ) => {
74+ left. next ( ) ;
75+ right. next ( ) ;
76+ let ordering = Ord :: cmp ( & l, & r) ;
77+ if ordering != Ordering :: Equal {
78+ return ordering;
79+ }
80+ }
81+ }
82+ }
83+ } else {
84+ // Integer mode
85+ let mut same_length_ordering = Ord :: cmp ( & l, & r) ;
86+ loop {
87+ // Get next pair
88+ let ( l, r) = match ( left. peek ( ) , right. peek ( ) ) {
89+ // Is this the end of both strings?
90+ ( None , None ) => return same_length_ordering,
91+ // If for one, the shorter one is considered smaller
92+ ( None , Some ( _) ) => return Ordering :: Less ,
93+ ( Some ( _) , None ) => return Ordering :: Greater ,
94+ ( Some ( l) , Some ( r) ) => ( l, r) ,
95+ } ;
96+ // Are they digits?
97+ match ( l. to_digit ( 10 ) , r. to_digit ( 10 ) ) {
98+ // If out of digits, use the stored ordering due to equal length
99+ ( None , None ) => break same_length_ordering,
100+ // If one is shorter, it's smaller
101+ ( None , Some ( _) ) => return Ordering :: Less ,
102+ ( Some ( _) , None ) => return Ordering :: Greater ,
103+ // If both are digits, consume them and take into account
104+ ( Some ( l) , Some ( r) ) => {
105+ left. next ( ) ;
106+ right. next ( ) ;
107+ same_length_ordering = same_length_ordering. then ( Ord :: cmp ( & l, & r) ) ;
108+ }
109+ }
110+ }
111+ }
112+ }
113+ } ;
114+ if next_ordering != Ordering :: Equal {
115+ return next_ordering;
116+ }
117+ }
118+ }
119+
120+ /// Compare identifiers, trimming `r#` if present, according to version sort
121+ pub ( crate ) fn compare_ident_as_versions ( left : & str , right : & str ) -> Ordering {
122+ compare_as_versions (
123+ left. trim_start_matches ( "r#" ) ,
124+ right. trim_start_matches ( "r#" ) ,
125+ )
126+ }
127+
128+ pub ( crate ) fn compare_opt_ident_as_versions < S > ( left : & Option < S > , right : & Option < S > ) -> Ordering
129+ where
130+ S : AsRef < str > ,
131+ {
132+ match ( left, right) {
133+ ( None , None ) => Ordering :: Equal ,
134+ ( None , Some ( _) ) => Ordering :: Less ,
135+ ( Some ( _) , None ) => Ordering :: Greater ,
136+ ( Some ( left) , Some ( right) ) => compare_ident_as_versions ( left. as_ref ( ) , right. as_ref ( ) ) ,
137+ }
138+ }
139+
25140/// Choose the ordering between the given two items.
26141fn compare_items ( a : & ast:: Item , b : & ast:: Item ) -> Ordering {
27142 match ( & a. kind , & b. kind ) {
28143 ( & ast:: ItemKind :: Mod ( ..) , & ast:: ItemKind :: Mod ( ..) ) => {
29- a. ident . as_str ( ) . cmp ( & b. ident . as_str ( ) )
144+ compare_as_versions ( & a. ident . as_str ( ) , & b. ident . as_str ( ) )
30145 }
31146 ( & ast:: ItemKind :: ExternCrate ( ref a_name) , & ast:: ItemKind :: ExternCrate ( ref b_name) ) => {
32147 // `extern crate foo as bar;`
33148 // ^^^ Comparing this.
34149 let a_orig_name = a_name. map_or_else ( || a. ident . as_str ( ) , rustc_span:: Symbol :: as_str) ;
35150 let b_orig_name = b_name. map_or_else ( || b. ident . as_str ( ) , rustc_span:: Symbol :: as_str) ;
36- let result = a_orig_name . cmp ( & b_orig_name) ;
151+ let result = compare_as_versions ( & a_orig_name , & b_orig_name) ;
37152 if result != Ordering :: Equal {
38153 return result;
39154 }
@@ -44,7 +159,7 @@ fn compare_items(a: &ast::Item, b: &ast::Item) -> Ordering {
44159 ( Some ( ..) , None ) => Ordering :: Greater ,
45160 ( None , Some ( ..) ) => Ordering :: Less ,
46161 ( None , None ) => Ordering :: Equal ,
47- ( Some ( ..) , Some ( ..) ) => a. ident . as_str ( ) . cmp ( & b. ident . as_str ( ) ) ,
162+ ( Some ( ..) , Some ( ..) ) => compare_as_versions ( & a. ident . as_str ( ) , & b. ident . as_str ( ) ) ,
48163 }
49164 }
50165 _ => unreachable ! ( ) ,
@@ -264,3 +379,37 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
264379 }
265380 }
266381}
382+
383+ #[ cfg( test) ]
384+ mod tests {
385+ #[ test]
386+ fn test_compare_as_versions ( ) {
387+ use super :: compare_as_versions;
388+ use std:: cmp:: Ordering ;
389+ let mut strings: & [ & ' static str ] = & [
390+ "9" , "i8" , "ia32" , "u009" , "u08" , "u08" , "u080" , "u8" , "u8" , "u16" , "u32" , "u128" ,
391+ ] ;
392+ while !strings. is_empty ( ) {
393+ let ( first, tail) = strings. split_first ( ) . unwrap ( ) ;
394+ for second in tail {
395+ if first == second {
396+ assert_eq ! ( compare_as_versions( first, second) , Ordering :: Equal ) ;
397+ assert_eq ! ( compare_as_versions( second, first) , Ordering :: Equal ) ;
398+ } else {
399+ assert_eq ! ( compare_as_versions( first, second) , Ordering :: Less ) ;
400+ assert_eq ! ( compare_as_versions( second, first) , Ordering :: Greater ) ;
401+ }
402+ }
403+ strings = tail;
404+ }
405+ }
406+ #[ test]
407+ fn test_compare_opt_ident_as_versions ( ) {
408+ use super :: compare_opt_ident_as_versions;
409+ use std:: cmp:: Ordering ;
410+ let items: & [ Option < & ' static str > ] = & [ None , Some ( "a" ) , Some ( "r#a" ) , Some ( "a" ) ] ;
411+ for ( p, n) in items[ ..items. len ( ) - 1 ] . iter ( ) . zip ( items[ 1 ..] . iter ( ) ) {
412+ assert ! ( compare_opt_ident_as_versions( p, n) != Ordering :: Greater ) ;
413+ }
414+ }
415+ }
0 commit comments