@@ -22,18 +22,133 @@ 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
+
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
+
25
140
/// Choose the ordering between the given two items.
26
141
fn compare_items ( a : & ast:: Item , b : & ast:: Item ) -> Ordering {
27
142
match ( & a. kind , & b. kind ) {
28
143
( & 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 ( ) )
30
145
}
31
146
( & ast:: ItemKind :: ExternCrate ( ref a_name) , & ast:: ItemKind :: ExternCrate ( ref b_name) ) => {
32
147
// `extern crate foo as bar;`
33
148
// ^^^ Comparing this.
34
149
let a_orig_name = a_name. map_or_else ( || a. ident . as_str ( ) , rustc_span:: Symbol :: as_str) ;
35
150
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) ;
37
152
if result != Ordering :: Equal {
38
153
return result;
39
154
}
@@ -44,7 +159,7 @@ fn compare_items(a: &ast::Item, b: &ast::Item) -> Ordering {
44
159
( Some ( ..) , None ) => Ordering :: Greater ,
45
160
( None , Some ( ..) ) => Ordering :: Less ,
46
161
( 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 ( ) ) ,
48
163
}
49
164
}
50
165
_ => unreachable ! ( ) ,
@@ -264,3 +379,37 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
264
379
}
265
380
}
266
381
}
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