1#[cfg(test)]
2use strum_macros::EnumIter;
3
4use crate::pattern::Pattern;
5use 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)]
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 /// 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
236impl 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)]
385mod 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