1
+ #[ derive( Debug ) ]
2
+ enum DetectedCase {
3
+ LowerCamelCase ,
4
+ UpperCamelCase ,
5
+ LowerSnakeCase ,
6
+ UpperSnakeCase ,
7
+ Unknown ,
8
+ }
9
+
10
+ fn detect_case ( ident : & str ) -> DetectedCase {
11
+ let trimmed_ident = ident. trim_matches ( '_' ) ;
12
+ let first_lowercase =
13
+ trimmed_ident. chars ( ) . next ( ) . map ( |chr| chr. is_ascii_lowercase ( ) ) . unwrap_or ( false ) ;
14
+ let mut has_lowercase = first_lowercase;
15
+ let mut has_uppercase = false ;
16
+ let mut has_underscore = false ;
17
+
18
+ for chr in trimmed_ident. chars ( ) {
19
+ if chr == '_' {
20
+ has_underscore = true ;
21
+ } else if chr. is_ascii_uppercase ( ) {
22
+ has_uppercase = true ;
23
+ } else if chr. is_ascii_lowercase ( ) {
24
+ has_lowercase = true ;
25
+ }
26
+ }
27
+
28
+ if has_uppercase {
29
+ if !has_lowercase {
30
+ DetectedCase :: UpperSnakeCase
31
+ } else if !has_underscore {
32
+ if first_lowercase {
33
+ DetectedCase :: LowerCamelCase
34
+ } else {
35
+ DetectedCase :: UpperCamelCase
36
+ }
37
+ } else {
38
+ // It has uppercase, it has lowercase, it has underscore.
39
+ // No assumptions here
40
+ DetectedCase :: Unknown
41
+ }
42
+ } else {
43
+ DetectedCase :: LowerSnakeCase
44
+ }
45
+ }
46
+
1
47
pub fn to_camel_case ( ident : & str ) -> Option < String > {
2
- let mut output = String :: new ( ) ;
48
+ let detected_case = detect_case ( ident ) ;
3
49
4
- if is_camel_case ( ident) {
5
- return None ;
50
+ match detected_case {
51
+ DetectedCase :: UpperCamelCase => return None ,
52
+ DetectedCase :: LowerCamelCase => {
53
+ let mut first_capitalized = false ;
54
+ let output = ident
55
+ . chars ( )
56
+ . map ( |chr| {
57
+ if !first_capitalized && chr. is_ascii_lowercase ( ) {
58
+ first_capitalized = true ;
59
+ chr. to_ascii_uppercase ( )
60
+ } else {
61
+ chr
62
+ }
63
+ } )
64
+ . collect ( ) ;
65
+ return Some ( output) ;
66
+ }
67
+ _ => { }
6
68
}
7
69
70
+ let mut output = String :: with_capacity ( ident. len ( ) ) ;
71
+
8
72
let mut capital_added = false ;
9
73
for chr in ident. chars ( ) {
10
74
if chr. is_alphabetic ( ) {
@@ -23,47 +87,37 @@ pub fn to_camel_case(ident: &str) -> Option<String> {
23
87
}
24
88
}
25
89
26
- if output == ident {
27
- None
28
- } else {
29
- Some ( output)
30
- }
90
+ Some ( output)
31
91
}
32
92
33
93
pub fn to_lower_snake_case ( ident : & str ) -> Option < String > {
34
94
// First, assume that it's UPPER_SNAKE_CASE.
35
- if let Some ( normalized) = to_lower_snake_case_from_upper_snake_case ( ident) {
36
- return Some ( normalized) ;
95
+ match detect_case ( ident) {
96
+ DetectedCase :: LowerSnakeCase => return None ,
97
+ DetectedCase :: UpperSnakeCase => {
98
+ return Some ( ident. chars ( ) . map ( |chr| chr. to_ascii_lowercase ( ) ) . collect ( ) )
99
+ }
100
+ _ => { }
37
101
}
38
102
39
103
// Otherwise, assume that it's CamelCase.
40
104
let lower_snake_case = stdx:: to_lower_snake_case ( ident) ;
41
-
42
- if lower_snake_case == ident {
43
- None
44
- } else {
45
- Some ( lower_snake_case)
46
- }
105
+ Some ( lower_snake_case)
47
106
}
48
107
49
- fn to_lower_snake_case_from_upper_snake_case ( ident : & str ) -> Option < String > {
50
- if is_upper_snake_case ( ident) {
51
- let string = ident. chars ( ) . map ( |c| c. to_ascii_lowercase ( ) ) . collect ( ) ;
52
- Some ( string)
53
- } else {
54
- None
108
+ pub fn to_upper_snake_case ( ident : & str ) -> Option < String > {
109
+ match detect_case ( ident) {
110
+ DetectedCase :: UpperSnakeCase => return None ,
111
+ DetectedCase :: LowerSnakeCase => {
112
+ return Some ( ident. chars ( ) . map ( |chr| chr. to_ascii_uppercase ( ) ) . collect ( ) )
113
+ }
114
+ _ => { }
55
115
}
56
- }
57
-
58
- fn is_upper_snake_case ( ident : & str ) -> bool {
59
- ident. chars ( ) . all ( |c| c. is_ascii_uppercase ( ) || c == '_' )
60
- }
61
116
62
- fn is_camel_case ( ident : & str ) -> bool {
63
- // We assume that the string is either snake case or camel case.
64
- // `_` is allowed only at the beginning or in the end of identifier, not between characters.
65
- ident. trim_matches ( '_' ) . chars ( ) . all ( |c| c != '_' )
66
- && ident. chars ( ) . find ( |c| c. is_alphabetic ( ) ) . map ( |c| c. is_ascii_uppercase ( ) ) . unwrap_or ( true )
117
+ // Normalize the string from whatever form it's in currently, and then just make it uppercase.
118
+ let upper_snake_case =
119
+ stdx:: to_lower_snake_case ( ident) . chars ( ) . map ( |c| c. to_ascii_uppercase ( ) ) . collect ( ) ;
120
+ Some ( upper_snake_case)
67
121
}
68
122
69
123
#[ cfg( test) ]
@@ -84,16 +138,27 @@ mod tests {
84
138
check ( to_lower_snake_case, "UPPER_SNAKE_CASE" , expect ! [ [ "upper_snake_case" ] ] ) ;
85
139
check ( to_lower_snake_case, "Weird_Case" , expect ! [ [ "weird_case" ] ] ) ;
86
140
check ( to_lower_snake_case, "CamelCase" , expect ! [ [ "camel_case" ] ] ) ;
141
+ check ( to_lower_snake_case, "lowerCamelCase" , expect ! [ [ "lower_camel_case" ] ] ) ;
87
142
}
88
143
89
144
#[ test]
90
145
fn test_to_camel_case ( ) {
91
146
check ( to_camel_case, "CamelCase" , expect ! [ [ "" ] ] ) ;
92
147
check ( to_camel_case, "CamelCase_" , expect ! [ [ "" ] ] ) ;
93
148
check ( to_camel_case, "_CamelCase" , expect ! [ [ "" ] ] ) ;
149
+ check ( to_camel_case, "lowerCamelCase" , expect ! [ [ "LowerCamelCase" ] ] ) ;
94
150
check ( to_camel_case, "lower_snake_case" , expect ! [ [ "LowerSnakeCase" ] ] ) ;
95
151
check ( to_camel_case, "UPPER_SNAKE_CASE" , expect ! [ [ "UpperSnakeCase" ] ] ) ;
96
152
check ( to_camel_case, "Weird_Case" , expect ! [ [ "WeirdCase" ] ] ) ;
97
153
check ( to_camel_case, "name" , expect ! [ [ "Name" ] ] ) ;
98
154
}
155
+
156
+ #[ test]
157
+ fn test_to_upper_snake_case ( ) {
158
+ check ( to_upper_snake_case, "UPPER_SNAKE_CASE" , expect ! [ [ "" ] ] ) ;
159
+ check ( to_upper_snake_case, "lower_snake_case" , expect ! [ [ "LOWER_SNAKE_CASE" ] ] ) ;
160
+ check ( to_upper_snake_case, "Weird_Case" , expect ! [ [ "WEIRD_CASE" ] ] ) ;
161
+ check ( to_upper_snake_case, "CamelCase" , expect ! [ [ "CAMEL_CASE" ] ] ) ;
162
+ check ( to_upper_snake_case, "lowerCamelCase" , expect ! [ [ "LOWER_CAMEL_CASE" ] ] ) ;
163
+ }
99
164
}
0 commit comments