@@ -22,6 +22,101 @@ 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+
25120/// Choose the ordering between the given two items.
26121fn compare_items ( a : & ast:: Item , b : & ast:: Item ) -> Ordering {
27122 match ( & a. kind , & b. kind ) {
@@ -264,3 +359,28 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
264359 }
265360 }
266361}
362+
363+ #[ cfg( test) ]
364+ mod tests {
365+ #[ test]
366+ fn test_compare_as_versions ( ) {
367+ use super :: compare_as_versions;
368+ use std:: cmp:: Ordering ;
369+ let mut strings: & [ & ' static str ] = & [
370+ "9" , "i8" , "ia32" , "u009" , "u08" , "u08" , "u080" , "u8" , "u8" , "u16" , "u32" , "u128" ,
371+ ] ;
372+ while !strings. is_empty ( ) {
373+ let ( first, tail) = strings. split_first ( ) . unwrap ( ) ;
374+ for second in tail {
375+ if first == second {
376+ assert_eq ! ( compare_as_versions( first, second) , Ordering :: Equal ) ;
377+ assert_eq ! ( compare_as_versions( second, first) , Ordering :: Equal ) ;
378+ } else {
379+ assert_eq ! ( compare_as_versions( first, second) , Ordering :: Less ) ;
380+ assert_eq ! ( compare_as_versions( second, first) , Ordering :: Greater ) ;
381+ }
382+ }
383+ strings = tail;
384+ }
385+ }
386+ }
0 commit comments