1#![deny(warnings)]
2#[allow(unknown_lints)]
3#[allow(unused_imports)]
4use std::ascii::*;
5
6pub struct CamelOptions {
7 pub new_word: bool,
8 pub last_char: char,
9 pub first_word: bool,
10 pub injectable_char: char,
11 pub has_seperator: bool,
12 pub inverted: bool,
13}
14
15pub fn to_case_snake_like(convertable_string: &str, replace_with: &str, case: &str) -> String {
16 let mut first_character: bool = true;
17 let mut result: String = String::with_capacity(convertable_string.len() * 2);
18 for char_with_index in trim_right(convertable_string).char_indices() {
19 if char_is_seperator(&char_with_index.1) {
20 if !first_character {
21 first_character = true;
22 result.push(replace_with.chars().nth(0).unwrap_or('_'));
23 }
24 } else if requires_seperator(char_with_index, first_character, &convertable_string) {
25 first_character = false;
26 result = snake_like_with_seperator(result, replace_with, &char_with_index.1, case)
27 } else {
28 first_character = false;
29 result = snake_like_no_seperator(result, &char_with_index.1, case)
30 }
31 }
32 result
33}
34
35pub fn to_case_camel_like(convertable_string: &str, camel_options: CamelOptions) -> String {
36 let mut new_word: bool = camel_options.new_word;
37 let mut first_word: bool = camel_options.first_word;
38 let mut last_char: char = camel_options.last_char;
39 let mut found_real_char: bool = false;
40 let mut result: String = String::with_capacity(convertable_string.len() * 2);
41 for character in trim_right(convertable_string).chars() {
42 if char_is_seperator(&character) && found_real_char {
43 new_word = true;
44 } else if !found_real_char && is_not_alphanumeric(character) {
45 continue;
46 } else if character.is_numeric() {
47 found_real_char = true;
48 new_word = true;
49 result.push(character);
50 } else if last_char_lower_current_is_upper_or_new_word(new_word, last_char, character) {
51 found_real_char = true;
52 new_word = false;
53 result = append_on_new_word(result, first_word, character, &camel_options);
54 first_word = false;
55 } else {
56 found_real_char = true;
57 last_char = character;
58 result.push(character.to_ascii_lowercase());
59 }
60 }
61 result
62}
63
64#[inline]
65fn append_on_new_word(mut result: String, first_word: bool, character: char, camel_options: &CamelOptions) -> String {
66 if not_first_word_and_has_seperator(first_word, camel_options.has_seperator) {
67 result.push(camel_options.injectable_char);
68 }
69 if first_word_or_not_inverted(first_word, camel_options.inverted) {
70 result.push(character.to_ascii_uppercase());
71 } else {
72 result.push(character.to_ascii_lowercase());
73 }
74 result
75}
76
77fn not_first_word_and_has_seperator(first_word: bool, has_seperator: bool) -> bool {
78 has_seperator && !first_word
79}
80
81fn first_word_or_not_inverted(first_word: bool, inverted: bool) -> bool {
82 !inverted || first_word
83}
84
85
86fn last_char_lower_current_is_upper_or_new_word(new_word: bool, last_char: char, character: char) -> bool{
87 new_word ||
88 ((last_char.is_lowercase() && character.is_uppercase()) &&
89 (last_char != ' '))
90}
91
92fn char_is_seperator(character: &char) -> bool {
93 is_not_alphanumeric(*character)
94}
95
96fn trim_right(convertable_string: &str) -> &str {
97 convertable_string.trim_end_matches(is_not_alphanumeric)
98}
99
100fn is_not_alphanumeric(character: char) -> bool {
101 !character.is_alphanumeric()
102}
103
104#[inline]
105fn requires_seperator(char_with_index: (usize, char), first_character: bool, convertable_string: &str) -> bool {
106 !first_character &&
107 char_is_uppercase(char_with_index.1) &&
108 next_or_previous_char_is_lowercase(convertable_string, char_with_index.0)
109}
110
111#[inline]
112fn snake_like_no_seperator(mut accumlator: String, current_char: &char, case: &str) -> String {
113 if case == "lower" {
114 accumlator.push(current_char.to_ascii_lowercase());
115 accumlator
116 } else {
117 accumlator.push(current_char.to_ascii_uppercase());
118 accumlator
119 }
120}
121
122#[inline]
123fn snake_like_with_seperator(mut accumlator: String, replace_with: &str, current_char: &char, case: &str) -> String {
124 if case == "lower" {
125 accumlator.push(replace_with.chars().nth(0).unwrap_or('_'));
126 accumlator.push(current_char.to_ascii_lowercase());
127 accumlator
128 } else {
129 accumlator.push(replace_with.chars().nth(0).unwrap_or('_'));
130 accumlator.push(current_char.to_ascii_uppercase());
131 accumlator
132 }
133}
134
135fn next_or_previous_char_is_lowercase(convertable_string: &str, char_with_index: usize) -> bool {
136 convertable_string.chars().nth(char_with_index + 1).unwrap_or('A').is_lowercase() ||
137 convertable_string.chars().nth(char_with_index - 1).unwrap_or('A').is_lowercase()
138}
139
140fn char_is_uppercase(test_char: char) -> bool {
141 test_char == test_char.to_ascii_uppercase()
142}
143
144#[test]
145fn test_trim_bad_chars() {
146 assert_eq!("abc", trim_right("abc----^"))
147}
148
149#[test]
150fn test_trim_bad_chars_when_none_are_bad() {
151 assert_eq!("abc", trim_right("abc"))
152}
153
154#[test]
155fn test_is_not_alphanumeric_on_is_alphanumeric() {
156 assert!(!is_not_alphanumeric('a'))
157}
158
159#[test]
160fn test_is_not_alphanumeric_on_is_not_alphanumeric() {
161 assert!(is_not_alphanumeric('_'))
162}
163
164
165#[test]
166fn test_char_is_uppercase_when_it_is() {
167 assert_eq!(char_is_uppercase('A'), true)
168}
169
170#[test]
171fn test_char_is_uppercase_when_it_is_not() {
172 assert_eq!(char_is_uppercase('a'), false)
173}
174
175#[test]
176fn test_next_or_previous_char_is_lowercase_true() {
177 assert_eq!(next_or_previous_char_is_lowercase("TestWWW", 3), true)
178}
179
180#[test]
181fn test_next_or_previous_char_is_lowercase_false() {
182 assert_eq!(next_or_previous_char_is_lowercase("TestWWW", 5), false)
183}
184
185#[test]
186fn snake_like_with_seperator_lowers() {
187 assert_eq!(snake_like_with_seperator("".to_owned(), "^", &'c', "lower"), "^c".to_string())
188}
189
190#[test]
191fn snake_like_with_seperator_upper() {
192 assert_eq!(snake_like_with_seperator("".to_owned(), "^", &'c', "upper"), "^C".to_string())
193}
194
195#[test]
196fn snake_like_no_seperator_lower() {
197 assert_eq!(snake_like_no_seperator("".to_owned(), &'C', "lower"), "c".to_string())
198}
199
200#[test]
201fn snake_like_no_seperator_upper() {
202 assert_eq!(snake_like_no_seperator("".to_owned(), &'c', "upper"), "C".to_string())
203}
204
205#[test]
206fn requires_seperator_upper_not_first_wrap_is_safe_current_upper() {
207 assert_eq!(requires_seperator((2, 'C'), false, "test"), true)
208}
209
210#[test]
211fn requires_seperator_upper_not_first_wrap_is_safe_current_lower() {
212 assert_eq!(requires_seperator((2, 'c'), false, "test"), false)
213}
214
215#[test]
216fn requires_seperator_upper_first_wrap_is_safe_current_upper() {
217 assert_eq!(requires_seperator((0, 'T'), true, "Test"), false)
218}
219
220#[test]
221fn requires_seperator_upper_first_wrap_is_safe_current_lower() {
222 assert_eq!(requires_seperator((0, 't'), true, "Test"), false)
223}
224
225#[test]
226fn requires_seperator_upper_first_wrap_is_safe_current_lower_next_is_too() {
227 assert_eq!(requires_seperator((0, 't'), true, "test"), false)
228}
229
230#[test]
231fn test_char_is_seperator_dash() {
232 assert_eq!(char_is_seperator(&'-'), true)
233}
234
235#[test]
236fn test_char_is_seperator_underscore() {
237 assert_eq!(char_is_seperator(&'_'), true)
238}
239
240#[test]
241fn test_char_is_seperator_space() {
242 assert_eq!(char_is_seperator(&' '), true)
243}
244
245#[test]
246fn test_char_is_seperator_when_not() {
247 assert_eq!(char_is_seperator(&'A'), false)
248}
249
250#[test]
251fn test_last_char_lower_current_is_upper_or_new_word_with_new_word() {
252 assert_eq!(last_char_lower_current_is_upper_or_new_word(true, ' ', '-'), true)
253}
254
255#[test]
256fn test_last_char_lower_current_is_upper_or_new_word_last_char_space() {
257 assert_eq!(last_char_lower_current_is_upper_or_new_word(false, ' ', '-'), false)
258}
259
260#[test]
261fn test_last_char_lower_current_is_upper_or_new_word_last_char_lower_current_upper() {
262 assert_eq!(last_char_lower_current_is_upper_or_new_word(false, 'a', 'A'), true)
263}
264
265#[test]
266fn test_last_char_lower_current_is_upper_or_new_word_last_char_upper_current_upper() {
267 assert_eq!(last_char_lower_current_is_upper_or_new_word(false, 'A', 'A'), false)
268}
269
270#[test]
271fn test_last_char_lower_current_is_upper_or_new_word_last_char_upper_current_lower() {
272 assert_eq!(last_char_lower_current_is_upper_or_new_word(false, 'A', 'a'), false)
273}
274
275#[test]
276fn test_first_word_or_not_inverted_with_first_word() {
277 assert_eq!(first_word_or_not_inverted(true, false), true)
278}
279
280#[test]
281fn test_first_word_or_not_inverted_not_first_word_not_inverted() {
282 assert_eq!(first_word_or_not_inverted(false, false), true)
283}
284
285#[test]
286fn test_first_word_or_not_inverted_not_first_word_is_inverted() {
287 assert_eq!(first_word_or_not_inverted(false, true), false)
288}
289
290#[test]
291fn test_not_first_word_and_has_seperator_is_first_and_not_seperator() {
292 assert_eq!(not_first_word_and_has_seperator(true, false), false)
293}
294
295#[test]
296fn test_not_first_word_and_has_seperator_not_first_and_not_seperator() {
297 assert_eq!(not_first_word_and_has_seperator(false, false), false)
298}
299
300#[test]
301fn test_not_first_word_and_has_seperator_not_first_and_has_seperator() {
302 assert_eq!(not_first_word_and_has_seperator(false, true), true)
303}