@@ -22,6 +22,101 @@ use crate::spanned::Spanned;
22
22
use crate :: utils:: { contains_skip, mk_sp} ;
23
23
use crate :: visitor:: FmtVisitor ;
24
24
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
+
25
120
/// Choose the ordering between the given two items.
26
121
fn compare_items ( a : & ast:: Item , b : & ast:: Item ) -> Ordering {
27
122
match ( & a. kind , & b. kind ) {
@@ -264,3 +359,28 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
264
359
}
265
360
}
266
361
}
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