1
+ use icu_casemap:: CaseMapper ;
1
2
use std:: cmp:: Ordering ;
2
3
use std:: io;
3
4
use std:: path:: { Component , Path , PathBuf } ;
4
5
5
6
// ========================================================================= //
6
7
7
8
const MAX_NAME_LEN : usize = 31 ;
9
+ const CASE_MAPPER : CaseMapper = CaseMapper :: new ( ) ;
8
10
9
11
// ========================================================================= //
10
12
11
- // according to the spec, "For each UTF-16 code point, convert to uppercase by
12
- // using the Unicode Default Case Conversion Algorithm, simple case conversion
13
- // variant (simple case foldings)"
14
- // it's not clear what that means, since neither case folding nor strict upper
15
- // case conversion yields convert('Ö') < convert('ß'), see the test case
16
- fn uppercase ( s : & str ) -> String {
17
- let mut upper = String :: new ( ) ;
18
- for c in s. chars ( ) {
19
- match c {
20
- 'ß' => upper. push ( 'ẞ' ) ,
21
- c => upper. extend ( c. to_uppercase ( ) ) ,
22
- }
23
- }
24
- upper
13
+ /// Converts a char to uppercase as defined in MS-CFB,
14
+ /// using simple capitalization and the ability to add exceptions.
15
+ /// Used when two directory entry names need to be compared.
16
+ fn cfb_uppercase_char ( c : char ) -> char {
17
+ // TODO: Edge cases can be added that appear
18
+ // in the table from Appendix A, <3> Section 2.6.4
19
+
20
+ // Base case, just do a simple uppercase
21
+ CASE_MAPPER . simple_uppercase ( c)
25
22
}
26
23
27
24
/// Compares two directory entry names according to CFB ordering, which is
@@ -35,7 +32,11 @@ pub fn compare_names(name1: &str, name2: &str) -> Ordering {
35
32
// particular way of doing the uppercasing on individual UTF-16 code
36
33
// units, along with a list of weird exceptions and corner cases. But
37
34
// hopefully this is good enough for 99+% of the time.
38
- Ordering :: Equal => uppercase ( name1) . cmp ( & uppercase ( name2) ) ,
35
+ Ordering :: Equal => {
36
+ let n1 = name1. chars ( ) . map ( cfb_uppercase_char) ;
37
+ let n2 = name2. chars ( ) . map ( cfb_uppercase_char) ;
38
+ n1. cmp ( n2)
39
+ }
39
40
other => other,
40
41
}
41
42
}
@@ -100,8 +101,8 @@ pub fn path_from_name_chain(names: &[&str]) -> PathBuf {
100
101
#[ cfg( test) ]
101
102
mod tests {
102
103
use super :: {
103
- compare_names , name_chain_from_path , path_from_name_chain ,
104
- validate_name,
104
+ cfb_uppercase_char , compare_names , name_chain_from_path ,
105
+ path_from_name_chain , validate_name,
105
106
} ;
106
107
use std:: cmp:: Ordering ;
107
108
use std:: path:: { Path , PathBuf } ;
@@ -126,6 +127,21 @@ mod tests {
126
127
) ,
127
128
Ordering :: Less
128
129
) ;
130
+
131
+ let uppercase = "ßQÑ52Ç4ÅÁÔÂFÛCWCÙÂNË5Q=="
132
+ . chars ( )
133
+ . map ( cfb_uppercase_char)
134
+ . collect :: < String > ( ) ;
135
+
136
+ assert_eq ! ( "ßQÑ52Ç4ÅÁÔÂFÛCWCÙÂNË5Q==" , uppercase) ;
137
+
138
+ assert_eq ! (
139
+ compare_names(
140
+ "ÜL43ÁMÆÛÏEKZÅYWÚÓVDÙÄÀ==" ,
141
+ "ßQÑ52Ç4ÅÁÔÂFÛCWCÙÂNË5Q=="
142
+ ) ,
143
+ Ordering :: Less
144
+ ) ;
129
145
}
130
146
131
147
#[ test]
0 commit comments