convert_case/case.rs
1#[cfg(test)]
2use strum::EnumIter;
3
4use crate::boundary::Boundary;
5use crate::pattern::Pattern;
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)]
29pub 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 /// Sentence case strings are delimited by spaces. Only the leading character of
66 /// the first word is uppercase.
67 /// * Boundaries: [Space](`Boundary::SPACE`)
68 /// * Pattern: [Capital](`Pattern::Sentence`)
69 /// * Delimeter: Space
70 ///
71 /// ```
72 /// use convert_case::{Case, Casing};
73 /// assert_eq!("My variable name", "My variable NAME".to_case(Case::Sentence))
74 /// ```
75 Sentence,
76
77 /// Toggle case strings are delimited by spaces. All characters are uppercase except
78 /// for the leading character of each word, which is lowercase.
79 /// * Boundaries: [Space](`Boundary::SPACE`)
80 /// * Pattern: [Toggle](`Pattern::Toggle`)
81 /// * Delimeter: Space
82 ///
83 /// ```
84 /// use convert_case::{Case, Casing};
85 /// assert_eq!("mY vARIABLE nAME", "My variable NAME".to_case(Case::Toggle))
86 /// ```
87 Toggle,
88
89 /// Camel case strings are lowercase, but for every word _except the first_ the
90 /// first letter is capitalized.
91 /// * Boundaries: [LowerUpper](Boundary::LOWER_UPPER), [DigitUpper](Boundary::DIGIT_UPPER),
92 /// [UpperDigit](Boundary::UPPER_DIGIT), [DigitLower](Boundary::DIGIT_LOWER),
93 /// [LowerDigit](Boundary::LOWER_DIGIT), [Acronym](Boundary::ACRONYM)
94 /// * Pattern: [Camel](`Pattern::Camel`)
95 /// * Delimeter: No delimeter
96 ///
97 /// ```
98 /// use convert_case::{Case, Casing};
99 /// assert_eq!("myVariableName", "My variable NAME".to_case(Case::Camel))
100 /// ```
101 Camel,
102
103 /// Pascal case strings are lowercase, but for every word the
104 /// first letter is capitalized.
105 /// * Boundaries: [LowerUpper](Boundary::LOWER_UPPER), [DigitUpper](Boundary::DIGIT_UPPER),
106 /// [UpperDigit](Boundary::UPPER_DIGIT), [DigitLower](Boundary::DIGIT_LOWER),
107 /// [LowerDigit](Boundary::LOWER_DIGIT), [Acronym](Boundary::ACRONYM)
108 /// * Pattern: [Capital](`Pattern::Capital`)
109 /// * Delimeter: No delimeter
110 ///
111 /// ```
112 /// use convert_case::{Case, Casing};
113 /// assert_eq!("MyVariableName", "My variable NAME".to_case(Case::Pascal))
114 /// ```
115 Pascal,
116
117 /// Upper camel case is an alternative name for [Pascal case](Case::Pascal).
118 UpperCamel,
119
120 /// Snake case strings are delimited by underscores `_` and are all lowercase.
121 /// * Boundaries: [Underscore](Boundary::UNDERSCORE)
122 /// * Pattern: [Lowercase](Pattern::Lowercase)
123 /// * Delimeter: Underscore `_`
124 ///
125 /// ```
126 /// use convert_case::{Case, Casing};
127 /// assert_eq!("my_variable_name", "My variable NAME".to_case(Case::Snake))
128 /// ```
129 Snake,
130
131 /// Constant case strings are delimited by underscores `_` and are all uppercase.
132 /// * Boundaries: [Underscore](Boundary::UNDERSCORE)
133 /// * Pattern: [Uppercase](Pattern::Uppercase)
134 /// * Delimeter: Underscore `_`
135 ///
136 /// ```
137 /// use convert_case::{Case, Casing};
138 /// assert_eq!("MY_VARIABLE_NAME", "My variable NAME".to_case(Case::Constant))
139 /// ```
140 Constant,
141
142 /// Upper snake case is an alternative name for [constant case](Case::Constant).
143 UpperSnake,
144
145 /// Kebab case strings are delimited by hyphens `-` and are all lowercase.
146 /// * Boundaries: [Hyphen](Boundary::HYPHEN)
147 /// * Pattern: [Lowercase](Pattern::Lowercase)
148 /// * Delimeter: Hyphen `-`
149 ///
150 /// ```
151 /// use convert_case::{Case, Casing};
152 /// assert_eq!("my-variable-name", "My variable NAME".to_case(Case::Kebab))
153 /// ```
154 Kebab,
155
156 /// Cobol case strings are delimited by hyphens `-` and are all uppercase.
157 /// * Boundaries: [Hyphen](Boundary::HYPHEN)
158 /// * Pattern: [Uppercase](Pattern::Uppercase)
159 /// * Delimeter: Hyphen `-`
160 ///
161 /// ```
162 /// use convert_case::{Case, Casing};
163 /// assert_eq!("MY-VARIABLE-NAME", "My variable NAME".to_case(Case::Cobol))
164 /// ```
165 Cobol,
166
167 /// Upper kebab case is an alternative name for [Cobol case](Case::Cobol).
168 UpperKebab,
169
170 /// Train case strings are delimited by hyphens `-`. All characters are lowercase
171 /// except for the leading character of each word.
172 /// * Boundaries: [Hyphen](Boundary::HYPHEN)
173 /// * Pattern: [Capital](Pattern::Capital)
174 /// * Delimeter: Hyphen `-`
175 ///
176 /// ```
177 /// use convert_case::{Case, Casing};
178 /// assert_eq!("My-Variable-Name", "My variable NAME".to_case(Case::Train))
179 /// ```
180 Train,
181
182 /// Flat case strings are all lowercase, with no delimiter. Note that word boundaries are lost.
183 /// * Boundaries: No boundaries
184 /// * Pattern: [Lowercase](Pattern::Lowercase)
185 /// * Delimeter: No delimeter
186 ///
187 /// ```
188 /// use convert_case::{Case, Casing};
189 /// assert_eq!("myvariablename", "My variable NAME".to_case(Case::Flat))
190 /// ```
191 Flat,
192
193 /// Upper flat case strings are all uppercase, with no delimiter. Note that word boundaries are lost.
194 /// * Boundaries: No boundaries
195 /// * Pattern: [Uppercase](Pattern::Uppercase)
196 /// * Delimeter: No delimeter
197 ///
198 /// ```
199 /// use convert_case::{Case, Casing};
200 /// assert_eq!("MYVARIABLENAME", "My variable NAME".to_case(Case::UpperFlat))
201 /// ```
202 UpperFlat,
203
204 /// Alternating case strings are delimited by spaces. Characters alternate between uppercase
205 /// and lowercase.
206 /// * Boundaries: [Space](Boundary::SPACE)
207 /// * Pattern: [Alternating](Pattern::Alternating)
208 /// * Delimeter: Space
209 ///
210 /// ```
211 /// use convert_case::{Case, Casing};
212 /// assert_eq!("mY vArIaBlE nAmE", "My variable NAME".to_case(Case::Alternating));
213 /// ```
214 Alternating,
215
216 /// Random case strings are delimited by spaces and characters are
217 /// randomly upper case or lower case. This uses the `rand` crate
218 /// and is only available with the "random" feature.
219 /// * Boundaries: [Space](Boundary::SPACE)
220 /// * Pattern: [Random](Pattern::Random)
221 /// * Delimeter: Space
222 ///
223 /// ```
224 /// use convert_case::{Case, Casing};
225 /// # #[cfg(any(doc, feature = "random"))]
226 /// let new = "My variable NAME".to_case(Case::Random);
227 /// ```
228 /// String `new` could be "My vaRIAbLE nAme" for example.
229 #[cfg(any(doc, feature = "random"))]
230 #[cfg(feature = "random")]
231 Random,
232
233 /// Pseudo-random case strings are delimited by spaces and characters are randomly
234 /// upper case or lower case, but there will never more than two consecutive lower
235 /// case or upper case letters in a row. This uses the `rand` crate and is
236 /// only available with the "random" feature.
237 /// * Boundaries: [Space](Boundary::SPACE)
238 /// * Pattern: [PseudoRandom](Pattern::PseudoRandom)
239 /// * Delimeter: Space
240 ///
241 /// ```
242 /// use convert_case::{Case, Casing};
243 /// # #[cfg(any(doc, feature = "random"))]
244 /// let new = "My variable NAME".to_case(Case::Random);
245 /// ```
246 /// String `new` could be "mY vArIAblE NamE" for example.
247 #[cfg(any(doc, feature = "random"))]
248 #[cfg(feature = "random")]
249 PseudoRandom,
250}
251
252impl Case {
253 /// Returns the delimiter used in the corresponding case. The following
254 /// table outlines which cases use which delimeter.
255 ///
256 /// | Cases | Delimeter |
257 /// | --- | --- |
258 /// | Upper, Lower, Title, Toggle, Alternating, Random, PseudoRandom | Space |
259 /// | Snake, Constant, UpperSnake | Underscore `_` |
260 /// | Kebab, Cobol, UpperKebab, Train | Hyphen `-` |
261 /// | UpperFlat, Flat, Camel, UpperCamel, Pascal | Empty string, no delimeter |
262 pub const fn delim(&self) -> &'static str {
263 use Case::*;
264 match self {
265 Upper | Lower | Title | Sentence | Toggle | Alternating => " ",
266 Snake | Constant | UpperSnake => "_",
267 Kebab | Cobol | UpperKebab | Train => "-",
268
269 #[cfg(feature = "random")]
270 Random | PseudoRandom => " ",
271
272 UpperFlat | Flat | Camel | UpperCamel | Pascal => "",
273 }
274 }
275
276 /// Returns the pattern used in the corresponding case. The following
277 /// table outlines which cases use which pattern.
278 ///
279 /// | Cases | Pattern |
280 /// | --- | --- |
281 /// | Upper, Constant, UpperSnake, UpperFlat, Cobol, UpperKebab | Uppercase |
282 /// | Lower, Snake, Kebab, Flat | Lowercase |
283 /// | Title, Pascal, UpperCamel, Train | Capital |
284 /// | Camel | Camel |
285 /// | Alternating | Alternating |
286 /// | Random | Random |
287 /// | PseudoRandom | PseudoRandom |
288 pub const fn pattern(&self) -> Pattern {
289 use Case::*;
290 match self {
291 Upper | Constant | UpperSnake | UpperFlat | Cobol | UpperKebab => Pattern::Uppercase,
292 Lower | Snake | Kebab | Flat => Pattern::Lowercase,
293 Title | Pascal | UpperCamel | Train => Pattern::Capital,
294 Camel => Pattern::Camel,
295 Toggle => Pattern::Toggle,
296 Alternating => Pattern::Alternating,
297 Sentence => Pattern::Sentence,
298
299 #[cfg(feature = "random")]
300 Random => Pattern::Random,
301 #[cfg(feature = "random")]
302 PseudoRandom => Pattern::PseudoRandom,
303 }
304 }
305
306 /// Returns the boundaries used in the corresponding case. That is, where can word boundaries
307 /// be distinguished in a string of the given case. The table outlines which cases use which
308 /// set of boundaries.
309 ///
310 /// | Cases | Boundaries |
311 /// | --- | --- |
312 /// | Upper, Lower, Title, Toggle, Alternating, Random, PseudoRandom | Space |
313 /// | Snake, Constant, UpperSnake | Underscore `_` |
314 /// | Kebab, Cobol, UpperKebab, Train | Hyphen `-` |
315 /// | Camel, UpperCamel, Pascal | LowerUpper, LowerDigit, UpperDigit, DigitLower, DigitUpper, Acronym |
316 /// | UpperFlat, Flat | No boundaries |
317 pub fn boundaries(&self) -> Vec<Boundary> {
318 use Case::*;
319 match self {
320 Upper | Lower | Title | Sentence | Toggle | Alternating => vec![Boundary::SPACE],
321 Snake | Constant | UpperSnake => vec![Boundary::UNDERSCORE],
322 Kebab | Cobol | UpperKebab | Train => vec![Boundary::HYPHEN],
323
324 #[cfg(feature = "random")]
325 Random | PseudoRandom => vec![Boundary::SPACE],
326
327 UpperFlat | Flat => vec![],
328 Camel | UpperCamel | Pascal => vec![
329 Boundary::LOWER_UPPER,
330 Boundary::ACRONYM,
331 Boundary::LOWER_DIGIT,
332 Boundary::UPPER_DIGIT,
333 Boundary::DIGIT_LOWER,
334 Boundary::DIGIT_UPPER,
335 ],
336 }
337 }
338
339 // Created to avoid using the EnumIter trait from strum in
340 // final library. A test confirms that all cases are listed here.
341 // Why is this needed? If it's only for ccase then I don't see why it's here.
342 /// Returns a vector with all case enum variants in no particular order.
343 pub fn all_cases() -> Vec<Case> {
344 use Case::*;
345 vec![
346 Upper,
347 Lower,
348 Title,
349 Sentence,
350 Toggle,
351 Camel,
352 Pascal,
353 UpperCamel,
354 Snake,
355 Constant,
356 UpperSnake,
357 Kebab,
358 Cobol,
359 UpperKebab,
360 Train,
361 Flat,
362 UpperFlat,
363 Alternating,
364 #[cfg(feature = "random")]
365 Random,
366 #[cfg(feature = "random")]
367 PseudoRandom,
368 ]
369 }
370
371 /// Returns a vector with the two "random" feature cases `Random` and `PseudoRandom`. Only
372 /// defined in the "random" feature.
373 #[cfg(feature = "random")]
374 pub fn random_cases() -> Vec<Case> {
375 use Case::*;
376 vec![Random, PseudoRandom]
377 }
378
379 /// Returns a vector with all the cases that do not depend on randomness. This is all
380 /// the cases not in the "random" feature.
381 pub fn deterministic_cases() -> Vec<Case> {
382 use Case::*;
383 vec![
384 Upper,
385 Lower,
386 Title,
387 Sentence,
388 Toggle,
389 Camel,
390 Pascal,
391 UpperCamel,
392 Snake,
393 Constant,
394 UpperSnake,
395 Kebab,
396 Cobol,
397 UpperKebab,
398 Train,
399 Flat,
400 UpperFlat,
401 Alternating,
402 ]
403 }
404}
405
406#[cfg(test)]
407mod test {
408
409 use super::*;
410 use strum::IntoEnumIterator;
411
412 #[test]
413 fn all_cases_in_iter() {
414 let all = Case::all_cases();
415 for case in Case::iter() {
416 assert!(all.contains(&case));
417 }
418 }
419}