1 | #[cfg (test)] |
2 | use strum_macros::EnumIter; |
3 | |
4 | use crate::pattern::Pattern; |
5 | use crate::Boundary; |
6 | |
7 | /// Defines the type of casing a string can be. |
8 | /// |
9 | /// ``` |
10 | /// use convert_case::{Case, Casing}; |
11 | /// |
12 | /// let super_mario_title: String = "super_mario_64" .to_case(Case::Title); |
13 | /// assert_eq!("Super Mario 64" , super_mario_title); |
14 | /// ``` |
15 | /// |
16 | /// A case is the pair of a [pattern](enum.Pattern.html) and a delimeter (a string). Given |
17 | /// a list of words, a pattern describes how to mutate the words and a delimeter is how the mutated |
18 | /// words are joined together. These inherantly are the properties of what makes a "multiword |
19 | /// identifier case", or simply "case". |
20 | /// |
21 | /// This crate provides the ability to convert "from" a case. This introduces a different feature |
22 | /// of cases which are the [word boundaries](Boundary) that segment the identifier into words. For example, a |
23 | /// snake case identifier `my_var_name` can be split on underscores `_` to segment into words. A |
24 | /// camel case identifier `myVarName` is split where a lowercase letter is followed by an |
25 | /// uppercase letter. Each case is also associated with a list of boundaries that are used when |
26 | /// converting "from" a particular case. |
27 | #[cfg_attr (test, derive(EnumIter))] |
28 | #[derive (Eq, PartialEq, Hash, Clone, Copy, Debug)] |
29 | pub enum Case { |
30 | /// Uppercase strings are delimited by spaces and all characters are uppercase. |
31 | /// * Boundaries: [Space](`Boundary::Space`) |
32 | /// * Pattern: [Uppercase](`Pattern::Uppercase`) |
33 | /// * Delimeter: Space |
34 | /// |
35 | /// ``` |
36 | /// use convert_case::{Case, Casing}; |
37 | /// assert_eq!("MY VARIABLE NAME" , "My variable NAME" .to_case(Case::Upper)) |
38 | /// ``` |
39 | Upper, |
40 | |
41 | /// Lowercase strings are delimited by spaces and all characters are lowercase. |
42 | /// * Boundaries: [Space](`Boundary::Space`) |
43 | /// * Pattern: [Lowercase](`Pattern::Lowercase`) |
44 | /// * Delimeter: Space |
45 | /// |
46 | /// ``` |
47 | /// use convert_case::{Case, Casing}; |
48 | /// assert_eq!("my variable name" , "My variable NAME" .to_case(Case::Lower)) |
49 | /// ``` |
50 | Lower, |
51 | |
52 | /// Title case strings are delimited by spaces. Only the leading character of |
53 | /// each word is uppercase. No inferences are made about language, so words |
54 | /// like "as", "to", and "for" will still be capitalized. |
55 | /// * Boundaries: [Space](`Boundary::Space`) |
56 | /// * Pattern: [Capital](`Pattern::Capital`) |
57 | /// * Delimeter: Space |
58 | /// |
59 | /// ``` |
60 | /// use convert_case::{Case, Casing}; |
61 | /// assert_eq!("My Variable Name" , "My variable NAME" .to_case(Case::Title)) |
62 | /// ``` |
63 | Title, |
64 | |
65 | /// Toggle case strings are delimited by spaces. All characters are uppercase except |
66 | /// for the leading character of each word, which is lowercase. |
67 | /// * Boundaries: [Space](`Boundary::Space`) |
68 | /// * Pattern: [Toggle](`Pattern::Toggle`) |
69 | /// * Delimeter: Space |
70 | /// |
71 | /// ``` |
72 | /// use convert_case::{Case, Casing}; |
73 | /// assert_eq!("mY vARIABLE nAME" , "My variable NAME" .to_case(Case::Toggle)) |
74 | /// ``` |
75 | Toggle, |
76 | |
77 | /// Camel case strings are lowercase, but for every word _except the first_ the |
78 | /// first letter is capitalized. |
79 | /// * Boundaries: [LowerUpper](Boundary::LowerUpper), [DigitUpper](Boundary::DigitUpper), |
80 | /// [UpperDigit](Boundary::UpperDigit), [DigitLower](Boundary::DigitLower), |
81 | /// [LowerDigit](Boundary::LowerDigit), [Acronym](Boundary::Acronym) |
82 | /// * Pattern: [Camel](`Pattern::Camel`) |
83 | /// * Delimeter: No delimeter |
84 | /// |
85 | /// ``` |
86 | /// use convert_case::{Case, Casing}; |
87 | /// assert_eq!("myVariableName" , "My variable NAME" .to_case(Case::Camel)) |
88 | /// ``` |
89 | Camel, |
90 | |
91 | /// Pascal case strings are lowercase, but for every word the |
92 | /// first letter is capitalized. |
93 | /// * Boundaries: [LowerUpper](Boundary::LowerUpper), [DigitUpper](Boundary::DigitUpper), |
94 | /// [UpperDigit](Boundary::UpperDigit), [DigitLower](Boundary::DigitLower), |
95 | /// [LowerDigit](Boundary::LowerDigit), [Acronym](Boundary::Acronym) |
96 | /// * Pattern: [Capital](`Pattern::Capital`) |
97 | /// * Delimeter: No delimeter |
98 | /// |
99 | /// ``` |
100 | /// use convert_case::{Case, Casing}; |
101 | /// assert_eq!("MyVariableName" , "My variable NAME" .to_case(Case::Pascal)) |
102 | /// ``` |
103 | Pascal, |
104 | |
105 | /// Upper camel case is an alternative name for [Pascal case](Case::Pascal). |
106 | UpperCamel, |
107 | |
108 | /// Snake case strings are delimited by underscores `_` and are all lowercase. |
109 | /// * Boundaries: [Underscore](Boundary::Underscore) |
110 | /// * Pattern: [Lowercase](Pattern::Lowercase) |
111 | /// * Delimeter: Underscore `_` |
112 | /// |
113 | /// ``` |
114 | /// use convert_case::{Case, Casing}; |
115 | /// assert_eq!("my_variable_name" , "My variable NAME" .to_case(Case::Snake)) |
116 | /// ``` |
117 | Snake, |
118 | |
119 | /// Upper snake case strings are delimited by underscores `_` and are all uppercase. |
120 | /// * Boundaries: [Underscore](Boundary::Underscore) |
121 | /// * Pattern: [Uppercase](Pattern::Uppercase) |
122 | /// * Delimeter: Underscore `_` |
123 | /// |
124 | /// ``` |
125 | /// use convert_case::{Case, Casing}; |
126 | /// assert_eq!("MY_VARIABLE_NAME" , "My variable NAME" .to_case(Case::UpperSnake)) |
127 | /// ``` |
128 | UpperSnake, |
129 | |
130 | /// Screaming snake case is an alternative name for [upper snake case](Case::UpperSnake). |
131 | ScreamingSnake, |
132 | |
133 | /// Kebab case strings are delimited by hyphens `-` and are all lowercase. |
134 | /// * Boundaries: [Hyphen](Boundary::Hyphen) |
135 | /// * Pattern: [Lowercase](Pattern::Lowercase) |
136 | /// * Delimeter: Hyphen `-` |
137 | /// |
138 | /// ``` |
139 | /// use convert_case::{Case, Casing}; |
140 | /// assert_eq!("my-variable-name" , "My variable NAME" .to_case(Case::Kebab)) |
141 | /// ``` |
142 | Kebab, |
143 | |
144 | /// Cobol case strings are delimited by hyphens `-` and are all uppercase. |
145 | /// * Boundaries: [Hyphen](Boundary::Hyphen) |
146 | /// * Pattern: [Uppercase](Pattern::Uppercase) |
147 | /// * Delimeter: Hyphen `-` |
148 | /// |
149 | /// ``` |
150 | /// use convert_case::{Case, Casing}; |
151 | /// assert_eq!("MY-VARIABLE-NAME" , "My variable NAME" .to_case(Case::Cobol)) |
152 | /// ``` |
153 | Cobol, |
154 | |
155 | /// Upper kebab case is an alternative name for [Cobol case](Case::Cobol). |
156 | UpperKebab, |
157 | |
158 | /// Train case strings are delimited by hyphens `-`. All characters are lowercase |
159 | /// except for the leading character of each word. |
160 | /// * Boundaries: [Hyphen](Boundary::Hyphen) |
161 | /// * Pattern: [Capital](Pattern::Capital) |
162 | /// * Delimeter: Hyphen `-` |
163 | /// |
164 | /// ``` |
165 | /// use convert_case::{Case, Casing}; |
166 | /// assert_eq!("My-Variable-Name" , "My variable NAME" .to_case(Case::Train)) |
167 | /// ``` |
168 | Train, |
169 | |
170 | /// Flat case strings are all lowercase, with no delimiter. Note that word boundaries are lost. |
171 | /// * Boundaries: No boundaries |
172 | /// * Pattern: [Lowercase](Pattern::Lowercase) |
173 | /// * Delimeter: No delimeter |
174 | /// |
175 | /// ``` |
176 | /// use convert_case::{Case, Casing}; |
177 | /// assert_eq!("myvariablename" , "My variable NAME" .to_case(Case::Flat)) |
178 | /// ``` |
179 | Flat, |
180 | |
181 | /// Upper flat case strings are all uppercase, with no delimiter. Note that word boundaries are lost. |
182 | /// * Boundaries: No boundaries |
183 | /// * Pattern: [Uppercase](Pattern::Uppercase) |
184 | /// * Delimeter: No delimeter |
185 | /// |
186 | /// ``` |
187 | /// use convert_case::{Case, Casing}; |
188 | /// assert_eq!("MYVARIABLENAME" , "My variable NAME" .to_case(Case::UpperFlat)) |
189 | /// ``` |
190 | UpperFlat, |
191 | |
192 | /// Alternating case strings are delimited by spaces. Characters alternate between uppercase |
193 | /// and lowercase. |
194 | /// * Boundaries: [Space](Boundary::Space) |
195 | /// * Pattern: [Alternating](Pattern::Alternating) |
196 | /// * Delimeter: Space |
197 | /// |
198 | /// ``` |
199 | /// use convert_case::{Case, Casing}; |
200 | /// assert_eq!("mY vArIaBlE nAmE" , "My variable NAME" .to_case(Case::Alternating)); |
201 | /// ``` |
202 | Alternating, |
203 | |
204 | /// Random case strings are delimited by spaces and characters are |
205 | /// randomly upper case or lower case. This uses the `rand` crate |
206 | /// and is only available with the "random" feature. |
207 | /// * Boundaries: [Space](Boundary::Space) |
208 | /// * Pattern: [Random](Pattern::Random) |
209 | /// * Delimeter: Space |
210 | /// |
211 | /// ``` |
212 | /// use convert_case::{Case, Casing}; |
213 | /// let new = "My variable NAME".to_case(Case::Random); |
214 | /// ``` |
215 | /// String `new` could be "My vaRIAbLE nAme" for example. |
216 | #[cfg (any(doc, feature = "random" ))] |
217 | Random, |
218 | |
219 | /// Pseudo-random case strings are delimited by spaces and characters are randomly |
220 | /// upper case or lower case, but there will never more than two consecutive lower |
221 | /// case or upper case letters in a row. This uses the `rand` crate and is |
222 | /// only available with the "random" feature. |
223 | /// * Boundaries: [Space](Boundary::Space) |
224 | /// * Pattern: [PseudoRandom](Pattern::PseudoRandom) |
225 | /// * Delimeter: Space |
226 | /// |
227 | /// ``` |
228 | /// use convert_case::{Case, Casing}; |
229 | /// let new = "My variable NAME".to_case(Case::Random); |
230 | /// ``` |
231 | /// String `new` could be "mY vArIAblE NamE" for example. |
232 | #[cfg (any(doc, feature = "random" ))] |
233 | PseudoRandom, |
234 | } |
235 | |
236 | impl Case { |
237 | /// Returns the delimiter used in the corresponding case. The following |
238 | /// table outlines which cases use which delimeter. |
239 | /// |
240 | /// | Cases | Delimeter | |
241 | /// | --- | --- | |
242 | /// | Upper, Lower, Title, Toggle, Alternating, Random, PseudoRandom | Space | |
243 | /// | Snake, UpperSnake, ScreamingSnake | Underscore `_` | |
244 | /// | Kebab, Cobol, UpperKebab, Train | Hyphen `-` | |
245 | /// | UpperFlat, Flat, Camel, UpperCamel, Pascal | Empty string, no delimeter | |
246 | pub const fn delim(&self) -> &'static str { |
247 | use Case::*; |
248 | match self { |
249 | Upper | Lower | Title | Toggle | Alternating => " " , |
250 | Snake | UpperSnake | ScreamingSnake => "_" , |
251 | Kebab | Cobol | UpperKebab | Train => "-" , |
252 | |
253 | #[cfg (feature = "random" )] |
254 | Random | PseudoRandom => " " , |
255 | |
256 | UpperFlat | Flat | Camel | UpperCamel | Pascal => "" , |
257 | } |
258 | } |
259 | |
260 | /// Returns the pattern used in the corresponding case. The following |
261 | /// table outlines which cases use which pattern. |
262 | /// |
263 | /// | Cases | Pattern | |
264 | /// | --- | --- | |
265 | /// | Upper, UpperSnake, ScreamingSnake, UpperFlat, Cobol, UpperKebab | Uppercase | |
266 | /// | Lower, Snake, Kebab, Flat | Lowercase | |
267 | /// | Title, Pascal, UpperCamel, Train | Capital | |
268 | /// | Camel | Camel | |
269 | /// | Alternating | Alternating | |
270 | /// | Random | Random | |
271 | /// | PseudoRandom | PseudoRandom | |
272 | pub const fn pattern(&self) -> Pattern { |
273 | use Case::*; |
274 | match self { |
275 | Upper | UpperSnake | ScreamingSnake | UpperFlat | Cobol | UpperKebab => { |
276 | Pattern::Uppercase |
277 | } |
278 | Lower | Snake | Kebab | Flat => Pattern::Lowercase, |
279 | Title | Pascal | UpperCamel | Train => Pattern::Capital, |
280 | Camel => Pattern::Camel, |
281 | Toggle => Pattern::Toggle, |
282 | Alternating => Pattern::Alternating, |
283 | |
284 | #[cfg (feature = "random" )] |
285 | Random => Pattern::Random, |
286 | #[cfg (feature = "random" )] |
287 | PseudoRandom => Pattern::PseudoRandom, |
288 | } |
289 | } |
290 | |
291 | /// Returns the boundaries used in the corresponding case. That is, where can word boundaries |
292 | /// be distinguished in a string of the given case. The table outlines which cases use which |
293 | /// set of boundaries. |
294 | /// |
295 | /// | Cases | Boundaries | |
296 | /// | --- | --- | |
297 | /// | Upper, Lower, Title, Toggle, Alternating, Random, PseudoRandom | Space | |
298 | /// | Snake, UpperSnake, ScreamingSnake | Underscore `_` | |
299 | /// | Kebab, Cobol, UpperKebab, Train | Hyphen `-` | |
300 | /// | Camel, UpperCamel, Pascal | LowerUpper, LowerDigit, UpperDigit, DigitLower, DigitUpper, Acronym | |
301 | /// | UpperFlat, Flat | No boundaries | |
302 | pub fn boundaries(&self) -> Vec<Boundary> { |
303 | use Boundary::*; |
304 | use Case::*; |
305 | match self { |
306 | Upper | Lower | Title | Toggle | Alternating => vec![Space], |
307 | Snake | UpperSnake | ScreamingSnake => vec![Underscore], |
308 | Kebab | Cobol | UpperKebab | Train => vec![Hyphen], |
309 | |
310 | #[cfg (feature = "random" )] |
311 | Random | PseudoRandom => vec![Space], |
312 | |
313 | UpperFlat | Flat => vec![], |
314 | Camel | UpperCamel | Pascal => vec![ |
315 | LowerUpper, Acronym, LowerDigit, UpperDigit, DigitLower, DigitUpper, |
316 | ], |
317 | } |
318 | } |
319 | |
320 | // Created to avoid using the EnumIter trait from strum in |
321 | // final library. A test confirms that all cases are listed here. |
322 | /// Returns a vector with all case enum variants in no particular order. |
323 | pub fn all_cases() -> Vec<Case> { |
324 | use Case::*; |
325 | vec![ |
326 | Upper, |
327 | Lower, |
328 | Title, |
329 | Toggle, |
330 | Camel, |
331 | Pascal, |
332 | UpperCamel, |
333 | Snake, |
334 | UpperSnake, |
335 | ScreamingSnake, |
336 | Kebab, |
337 | Cobol, |
338 | UpperKebab, |
339 | Train, |
340 | Flat, |
341 | UpperFlat, |
342 | Alternating, |
343 | #[cfg (feature = "random" )] |
344 | Random, |
345 | #[cfg (feature = "random" )] |
346 | PseudoRandom, |
347 | ] |
348 | } |
349 | |
350 | /// Returns a vector with the two "random" feature cases `Random` and `PseudoRandom`. Only |
351 | /// defined in the "random" feature. |
352 | #[cfg (feature = "random" )] |
353 | pub fn random_cases() -> Vec<Case> { |
354 | use Case::*; |
355 | vec![Random, PseudoRandom] |
356 | } |
357 | |
358 | /// Returns a vector with all the cases that do not depend on randomness. This is all |
359 | /// the cases not in the "random" feature. |
360 | pub fn deterministic_cases() -> Vec<Case> { |
361 | use Case::*; |
362 | vec![ |
363 | Upper, |
364 | Lower, |
365 | Title, |
366 | Toggle, |
367 | Camel, |
368 | Pascal, |
369 | UpperCamel, |
370 | Snake, |
371 | UpperSnake, |
372 | ScreamingSnake, |
373 | Kebab, |
374 | Cobol, |
375 | UpperKebab, |
376 | Train, |
377 | Flat, |
378 | UpperFlat, |
379 | Alternating, |
380 | ] |
381 | } |
382 | } |
383 | |
384 | #[cfg (test)] |
385 | mod test { |
386 | |
387 | use super::*; |
388 | use strum::IntoEnumIterator; |
389 | |
390 | #[test ] |
391 | fn all_cases_in_iter() { |
392 | let all = Case::all_cases(); |
393 | for case in Case::iter() { |
394 | assert!(all.contains(&case)); |
395 | } |
396 | } |
397 | } |
398 | |