@@ -21,6 +21,101 @@ use crate::spanned::Spanned;
21
21
use crate :: utils:: { contains_skip, mk_sp} ;
22
22
use crate :: visitor:: FmtVisitor ;
23
23
24
+ /// Compare strings according to version sort (roughly equivalent to `strverscmp`)
25
+ pub ( crate ) fn compare_as_versions ( left : & str , right : & str ) -> Ordering {
26
+ let mut left = left. chars ( ) . peekable ( ) ;
27
+ let mut right = right. chars ( ) . peekable ( ) ;
28
+
29
+ loop {
30
+ // The strings are equal so far and not inside a number in both sides
31
+ let ( l, r) = match ( left. next ( ) , right. next ( ) ) {
32
+ // Is this the end of both strings?
33
+ ( None , None ) => return Ordering :: Equal ,
34
+ // If for one, the shorter one is considered smaller
35
+ ( None , Some ( _) ) => return Ordering :: Less ,
36
+ ( Some ( _) , None ) => return Ordering :: Greater ,
37
+ ( Some ( l) , Some ( r) ) => ( l, r) ,
38
+ } ;
39
+ let next_ordering = match ( l. to_digit ( 10 ) , r. to_digit ( 10 ) ) {
40
+ // If neither is a digit, just compare them
41
+ ( None , None ) => Ord :: cmp ( & l, & r) ,
42
+ // The one with shorter non-digit run is smaller
43
+ // For `strverscmp` it's smaller iff next char in longer is greater than digits
44
+ ( None , Some ( _) ) => Ordering :: Greater ,
45
+ ( Some ( _) , None ) => Ordering :: Less ,
46
+ // If both start numbers, we have to compare the numbers
47
+ ( Some ( l) , Some ( r) ) => {
48
+ if l == 0 || r == 0 {
49
+ // Fraction mode: compare as if there was leading `0.`
50
+ let ordering = Ord :: cmp ( & l, & r) ;
51
+ if ordering != Ordering :: Equal {
52
+ return ordering;
53
+ }
54
+ loop {
55
+ // Get next pair
56
+ let ( l, r) = match ( left. peek ( ) , right. peek ( ) ) {
57
+ // Is this the end of both strings?
58
+ ( None , None ) => return Ordering :: Equal ,
59
+ // If for one, the shorter one is considered smaller
60
+ ( None , Some ( _) ) => return Ordering :: Less ,
61
+ ( Some ( _) , None ) => return Ordering :: Greater ,
62
+ ( Some ( l) , Some ( r) ) => ( l, r) ,
63
+ } ;
64
+ // Are they digits?
65
+ match ( l. to_digit ( 10 ) , r. to_digit ( 10 ) ) {
66
+ // If out of digits, use the stored ordering due to equal length
67
+ ( None , None ) => break Ordering :: Equal ,
68
+ // If one is shorter, it's smaller
69
+ ( None , Some ( _) ) => return Ordering :: Less ,
70
+ ( Some ( _) , None ) => return Ordering :: Greater ,
71
+ // If both are digits, consume them and take into account
72
+ ( Some ( l) , Some ( r) ) => {
73
+ left. next ( ) ;
74
+ right. next ( ) ;
75
+ let ordering = Ord :: cmp ( & l, & r) ;
76
+ if ordering != Ordering :: Equal {
77
+ return ordering;
78
+ }
79
+ }
80
+ }
81
+ }
82
+ } else {
83
+ // Integer mode
84
+ let mut same_length_ordering = Ord :: cmp ( & l, & r) ;
85
+ loop {
86
+ // Get next pair
87
+ let ( l, r) = match ( left. peek ( ) , right. peek ( ) ) {
88
+ // Is this the end of both strings?
89
+ ( None , None ) => return same_length_ordering,
90
+ // If for one, the shorter one is considered smaller
91
+ ( None , Some ( _) ) => return Ordering :: Less ,
92
+ ( Some ( _) , None ) => return Ordering :: Greater ,
93
+ ( Some ( l) , Some ( r) ) => ( l, r) ,
94
+ } ;
95
+ // Are they digits?
96
+ match ( l. to_digit ( 10 ) , r. to_digit ( 10 ) ) {
97
+ // If out of digits, use the stored ordering due to equal length
98
+ ( None , None ) => break same_length_ordering,
99
+ // If one is shorter, it's smaller
100
+ ( None , Some ( _) ) => return Ordering :: Less ,
101
+ ( Some ( _) , None ) => return Ordering :: Greater ,
102
+ // If both are digits, consume them and take into account
103
+ ( Some ( l) , Some ( r) ) => {
104
+ left. next ( ) ;
105
+ right. next ( ) ;
106
+ same_length_ordering = same_length_ordering. then ( Ord :: cmp ( & l, & r) ) ;
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+ } ;
113
+ if next_ordering != Ordering :: Equal {
114
+ return next_ordering;
115
+ }
116
+ }
117
+ }
118
+
24
119
/// Choose the ordering between the given two items.
25
120
fn compare_items ( a : & ast:: Item , b : & ast:: Item ) -> Ordering {
26
121
match ( & a. node , & b. node ) {
@@ -265,3 +360,28 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
265
360
}
266
361
}
267
362
}
363
+
364
+ #[ cfg( test) ]
365
+ mod tests {
366
+ #[ test]
367
+ fn test_compare_as_versions ( ) {
368
+ use super :: compare_as_versions;
369
+ use std:: cmp:: Ordering ;
370
+ let mut strings: & [ & ' static str ] = & [
371
+ "9" , "i8" , "ia32" , "u009" , "u08" , "u08" , "u080" , "u8" , "u8" , "u16" , "u32" , "u128" ,
372
+ ] ;
373
+ while !strings. is_empty ( ) {
374
+ let ( first, tail) = strings. split_first ( ) . unwrap ( ) ;
375
+ for second in tail {
376
+ if first == second {
377
+ assert_eq ! ( compare_as_versions( first, second) , Ordering :: Equal ) ;
378
+ assert_eq ! ( compare_as_versions( second, first) , Ordering :: Equal ) ;
379
+ } else {
380
+ assert_eq ! ( compare_as_versions( first, second) , Ordering :: Less ) ;
381
+ assert_eq ! ( compare_as_versions( second, first) , Ordering :: Greater ) ;
382
+ }
383
+ }
384
+ strings = tail;
385
+ }
386
+ }
387
+ }
0 commit comments