Skip to main content

convert_case/
case.rs

1use crate::boundary::{self, Boundary};
2use crate::pattern::Pattern;
3
4use alloc::string::String;
5use alloc::vec::Vec;
6
7/// Defines the case of an identifier.
8/// ```
9/// use convert_case::ccase;
10/// assert_eq!(ccase!(title, "super_mario_64"), "Super Mario 64");
11///
12/// use convert_case::{Case, Casing};
13/// assert_eq!("super_mario_64".to_case(Case::Title), "Super Mario 64");
14/// ```
15///
16/// A case is the pair of a [pattern](Pattern) and a delimiter (a string).  Given
17/// a list of words, a pattern describes how to mutate the words and a delimiter is how the mutated
18/// words are joined together.
19///
20/// | pattern | underscore `_` | hyphen `-` | empty string | space |
21/// | ---: | --- | --- | --- | --- |
22/// | [lowercase](Pattern::Lowercase) | [snake_case](Case::Snake) | [kebab-case](Case::Kebab) | [flatcase](Case::Flat) | [lower case](Case::Lower) |
23/// | [uppercase](Pattern::Uppercase) | [CONSTANT_CASE](Case::Constant) | [COBOL-CASE](Case::Cobol) | [UPPERFLATCASE](Case::UpperFlat) | [UPPER CASE](Case::Upper) |
24/// | [capital](Pattern::Capital) | [Ada_Case](Case::Ada) | [Train-Case](Case::Train) | [PascalCase](Case::Pascal) | [Title Case](Case::Title) |
25/// | [camel](Pattern::Camel) | | | [camelCase](Case::Camel) |
26///
27/// There are additionally [`Case::Sentence`].
28///
29/// This crate provides the ability to convert "from" a case.  This introduces a different feature
30/// of cases which are the [word boundaries](Boundary) that segment the identifier into words.  For example, a
31/// snake case identifier `my_var_name` can be split on underscores `_` to segment into words.  A
32/// camel case identifier `myVarName` is split where a lowercase letter is followed by an
33/// uppercase letter.  Each case is also associated with a list of boundaries that are used when
34/// converting "from" a particular case.
35#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug)]
36#[non_exhaustive]
37pub enum Case<'b> {
38    /// Custom cases can be delimited by any static string slice and mutate words
39    /// using any pattern.  Further, they can use any list of boundaries for
40    /// splitting identifiers into words.
41    ///
42    /// This flexibility can create cases not present as another variant of the
43    /// Case enum.  For instance, you could create a "dot case" like so.
44    /// ```
45    /// use convert_case::{Case, Casing, separator, Pattern};
46    /// let dot_case = Case::Custom {
47    ///     boundaries: &[separator!(".")],
48    ///     pattern: Pattern::Lowercase,
49    ///     delimiter: ".",
50    /// };
51    ///
52    /// assert_eq!(
53    ///     "myNewCase".to_case(dot_case),
54    ///     "my.new.case",
55    /// );
56    /// assert_eq!(
57    ///     "my.new.case".from_case(dot_case).to_case(Case::Title),
58    ///     "My New Case",
59    /// );
60    /// ```
61    Custom {
62        boundaries: &'b [Boundary],
63        pattern: Pattern,
64        delimiter: &'static str,
65    },
66
67    /// Snake case strings are delimited by underscores `_` and are all lowercase.
68    ///
69    /// * Boundaries : [Underscore](Boundary::Underscore)
70    /// * Pattern : [Lowercase](Pattern::Lowercase)
71    /// * Delimiter : Underscore `"_"`
72    ///
73    /// ```
74    /// use convert_case::ccase;
75    /// assert_eq!(ccase!(snake, "My variable NAME"), "my_variable_name");
76    ///
77    /// use convert_case::{Case, Casing};
78    /// assert_eq!("My variable NAME".to_case(Case::Snake), "my_variable_name");
79    /// ```
80    Snake,
81
82    /// Constant case strings are delimited by underscores `_` and are all uppercase.
83    /// * Boundaries: [Underscore](Boundary::Underscore)
84    /// * Pattern: [Uppercase](Pattern::Uppercase)
85    /// * Delimiter: Underscore `"_"`
86    ///
87    /// ```
88    /// use convert_case::ccase;
89    /// assert_eq!(ccase!(constant, "My variable NAME"), "MY_VARIABLE_NAME");
90    ///
91    /// use convert_case::{Case, Casing};
92    /// assert_eq!("My variable NAME".to_case(Case::Constant), "MY_VARIABLE_NAME");
93    /// ```
94    Constant,
95
96    /// Upper snake case is an alternative name for [constant case](Case::Constant).
97    UpperSnake,
98
99    /// Ada case strings are delimited by underscores `_`.  The leading letter of
100    /// each word is uppercase, while the rest is lowercase.
101    /// * Boundaries: [Underscore](Boundary::Underscore)
102    /// * Pattern: [Capital](Pattern::Capital)
103    /// * Delimiter: Underscore `"_"`
104    ///
105    /// ```
106    /// use convert_case::ccase;
107    /// assert_eq!(ccase!(ada, "My variable NAME"), "My_Variable_Name");
108    ///
109    /// use convert_case::{Case, Casing};
110    /// assert_eq!("My variable NAME".to_case(Case::Ada), "My_Variable_Name");
111    /// ```
112    Ada,
113
114    /// Kebab case strings are delimited by hyphens `-` and are all lowercase.
115    /// * Boundaries: [Hyphen](Boundary::Hyphen)
116    /// * Pattern: [Lowercase](Pattern::Lowercase)
117    /// * Delimiter: Hyphen `"-"`
118    ///
119    /// ```
120    /// use convert_case::ccase;
121    /// assert_eq!(ccase!(kebab, "My variable NAME"), "my-variable-name");
122    ///
123    /// use convert_case::{Case, Casing};
124    /// assert_eq!("My variable NAME".to_case(Case::Kebab), "my-variable-name");
125    /// ```
126    Kebab,
127
128    /// Cobol case strings are delimited by hyphens `-` and are all uppercase.
129    /// * Boundaries: [Hyphen](Boundary::Hyphen)
130    /// * Pattern: [Uppercase](Pattern::Uppercase)
131    /// * Delimiter: Hyphen `"-"`
132    ///
133    /// ```
134    /// use convert_case::ccase;
135    /// assert_eq!(ccase!(cobol, "My variable NAME"), "MY-VARIABLE-NAME");
136    ///
137    /// use convert_case::{Case, Casing};
138    /// assert_eq!("My variable NAME".to_case(Case::Cobol), "MY-VARIABLE-NAME");
139    /// ```
140    Cobol,
141
142    /// Upper kebab case is an alternative name for [Cobol case](Case::Cobol).
143    UpperKebab,
144
145    /// Train case strings are delimited by hyphens `-`.  The leading letter of
146    /// each word is uppercase, while the rest is lowercase.
147    /// * Boundaries: [Hyphen](Boundary::Hyphen)
148    /// * Pattern: [Capital](Pattern::Capital)
149    /// * Delimiter: Hyphen `"-"`
150    ///
151    /// ```
152    /// use convert_case::ccase;
153    /// assert_eq!(ccase!(train, "My variable NAME"), "My-Variable-Name");
154    ///
155    /// use convert_case::{Case, Casing};
156    /// assert_eq!("My variable NAME".to_case(Case::Train), "My-Variable-Name");
157    /// ```
158    Train,
159
160    /// Flat case strings are all lowercase, with no delimiter. Note that word boundaries are lost.
161    /// * Boundaries: No boundaries
162    /// * Pattern: [Lowercase](Pattern::Lowercase)
163    /// * Delimiter: Empty string `""`
164    ///
165    /// ```
166    /// use convert_case::ccase;
167    /// assert_eq!(ccase!(flat, "My variable NAME"), "myvariablename");
168    ///
169    /// use convert_case::{Case, Casing};
170    /// assert_eq!("My variable NAME".to_case(Case::Flat), "myvariablename");
171    /// ```
172    Flat,
173
174    /// Upper flat case strings are all uppercase, with no delimiter. Note that word boundaries are lost.
175    /// * Boundaries: No boundaries
176    /// * Pattern: [Uppercase](Pattern::Uppercase)
177    /// * Delimiter: Empty string `""`
178    ///
179    /// ```
180    /// use convert_case::ccase;
181    /// assert_eq!(ccase!(upper_flat, "My variable NAME"), "MYVARIABLENAME");
182    ///
183    /// use convert_case::{Case, Casing};
184    /// assert_eq!("My variable NAME".to_case(Case::UpperFlat), "MYVARIABLENAME");
185    /// ```
186    UpperFlat,
187
188    /// Pascal case strings are lowercase, but for every word the
189    /// first letter is capitalized.
190    /// * Boundaries: [LowerUpper](Boundary::LowerUpper), [DigitUpper](Boundary::DigitUpper),
191    ///   [UpperDigit](Boundary::UpperDigit), [DigitLower](Boundary::DigitLower),
192    ///   [LowerDigit](Boundary::LowerDigit), [Acronym](Boundary::Acronym)
193    /// * Pattern: [Capital](`Pattern::Capital`)
194    /// * Delimiter: Empty string `""`
195    ///
196    /// ```
197    /// use convert_case::ccase;
198    /// assert_eq!(ccase!(pascal, "My variable NAME"), "MyVariableName");
199    ///
200    /// use convert_case::{Case, Casing};
201    /// assert_eq!("My variable NAME".to_case(Case::Pascal), "MyVariableName");
202    /// ```
203    Pascal,
204
205    /// Upper camel case is an alternative name for [Pascal case](Case::Pascal).
206    UpperCamel,
207
208    /// Camel case strings are lowercase, but for every word _except the first_ the
209    /// first letter is capitalized.
210    /// * Boundaries: [LowerUpper](Boundary::LowerUpper), [DigitUpper](Boundary::DigitUpper),
211    ///   [UpperDigit](Boundary::UpperDigit), [DigitLower](Boundary::DigitLower),
212    ///   [LowerDigit](Boundary::LowerDigit), [Acronym](Boundary::Acronym)
213    /// * Pattern: [Camel](`Pattern::Camel`)
214    /// * Delimiter: Empty string `""`
215    ///
216    /// ```
217    /// use convert_case::ccase;
218    /// assert_eq!(ccase!(camel, "My variable NAME"), "myVariableName");
219    ///
220    /// use convert_case::{Case, Casing};
221    /// assert_eq!("My variable NAME".to_case(Case::Camel), "myVariableName");
222    /// ```
223    Camel,
224
225    /// Lowercase strings are delimited by spaces and all characters are lowercase.
226    /// * Boundaries: [Space](`Boundary::Space`)
227    /// * Pattern: [Lowercase](`Pattern::Lowercase`)
228    /// * Delimiter: Space `" "`
229    ///
230    /// ```
231    /// use convert_case::ccase;
232    /// assert_eq!(ccase!(lower, "My variable NAME"), "my variable name");
233    ///
234    /// use convert_case::{Case, Casing};
235    /// assert_eq!("My variable NAME".to_case(Case::Lower), "my variable name");
236    /// ```
237    Lower,
238
239    /// Uppercase strings are delimited by spaces and all characters are uppercase.
240    /// * Boundaries: [Space](`Boundary::Space`)
241    /// * Pattern: [Uppercase](`Pattern::Uppercase`)
242    /// * Delimiter: Space `" "`
243    ///
244    /// ```
245    /// use convert_case::ccase;
246    /// assert_eq!(ccase!(upper, "My variable NAME"), "MY VARIABLE NAME");
247    ///
248    /// use convert_case::{Case, Casing};
249    /// assert_eq!("My variable NAME".to_case(Case::Upper), "MY VARIABLE NAME");
250    /// ```
251    Upper,
252
253    /// Title case strings are delimited by spaces. Only the leading character of
254    /// each word is uppercase.  No inferences are made about language, so words
255    /// like "as", "to", and "for" will still be capitalized.
256    /// * Boundaries: [Space](`Boundary::Space`)
257    /// * Pattern: [Capital](`Pattern::Capital`)
258    /// * Delimiter: Space `" "`
259    ///
260    /// ```
261    /// use convert_case::ccase;
262    /// assert_eq!(ccase!(title, "My variable NAME"), "My Variable Name");
263    ///
264    /// use convert_case::{Case, Casing};
265    /// assert_eq!("My variable NAME".to_case(Case::Title), "My Variable Name");
266    /// ```
267    Title,
268
269    /// Sentence case strings are delimited by spaces. Only the leading character of
270    /// the first word is uppercase.
271    /// * Boundaries: [Space](`Boundary::Space`)
272    /// * Pattern: [Sentence](`Pattern::Sentence`)
273    /// * Delimiter: Space `" "`
274    ///
275    /// ```
276    /// use convert_case::ccase;
277    /// assert_eq!(ccase!(sentence, "My variable NAME"), "My variable name");
278    ///
279    /// use convert_case::{Case, Casing};
280    /// assert_eq!("My variable NAME".to_case(Case::Sentence), "My variable name");
281    /// ```
282    Sentence,
283}
284
285impl Case<'_> {
286    /// Returns the boundaries used in the corresponding case.  That is, where can word boundaries
287    /// be distinguished in a string of the given case.  The table outlines which cases use which
288    /// set of boundaries.
289    ///
290    /// | Cases | Boundaries |
291    /// | --- | --- |
292    /// | Snake, Constant, UpperSnake, Ada | [Underscore](Boundary::Underscore)  |
293    /// | Kebab, Cobol, UpperKebab, Train | [Hyphen](Boundary::Hyphen) |
294    /// | Lower, Upper, Title | [Space](Boundary::Space) |
295    /// | Pascal, UpperCamel, Camel | [LowerUpper](Boundary::LowerUpper), [LowerDigit](Boundary::LowerDigit), [UpperDigit](Boundary::UpperDigit), [DigitLower](Boundary::DigitLower), [DigitUpper](Boundary::DigitUpper), [Acronym](Boundary::Acronym) |
296    /// | Flat, UpperFlat | No boundaries |
297    pub fn boundaries(&self) -> &[Boundary] {
298        use Case::*;
299        match self {
300            Snake | Constant | UpperSnake | Ada => &[Boundary::Underscore],
301            Kebab | Cobol | UpperKebab | Train => &[Boundary::Hyphen],
302            Upper | Lower | Title | Sentence => &[Boundary::Space],
303            Camel | UpperCamel | Pascal => &[
304                Boundary::LowerUpper,
305                Boundary::Acronym,
306                Boundary::LowerDigit,
307                Boundary::UpperDigit,
308                Boundary::DigitLower,
309                Boundary::DigitUpper,
310            ],
311            UpperFlat | Flat => &[],
312            Custom { boundaries, .. } => boundaries,
313        }
314    }
315
316    /// Returns the delimiter used in the corresponding case.  The following
317    /// table outlines which cases use which delimiter.
318    ///
319    /// | Cases | Delimiter |
320    /// | --- | --- |
321    /// | Snake, Constant, UpperSnake, Ada | Underscore `"_"` |
322    /// | Kebab, Cobol, UpperKebab, Train | Hyphen `"-"` |
323    /// | Upper, Lower, Title, Sentence | Space `" "` |
324    /// | Flat, UpperFlat, Pascal, UpperCamel, Camel | Empty string `""` |
325    pub const fn delimiter(&self) -> &'static str {
326        use Case::*;
327        match self {
328            Snake | Constant | UpperSnake | Ada => "_",
329            Kebab | Cobol | UpperKebab | Train => "-",
330            Upper | Lower | Title | Sentence => " ",
331            Flat | UpperFlat | Pascal | UpperCamel | Camel => "",
332            Custom {
333                delimiter: delim, ..
334            } => delim,
335        }
336    }
337
338    /// Returns the pattern used in the corresponding case.  The following
339    /// table outlines which cases use which pattern.
340    ///
341    /// | Cases | Pattern |
342    /// | --- | --- |
343    /// | Constant, UpperSnake, Cobol, UpperKebab, UpperFlat, Upper | [Uppercase](Pattern::Uppercase) |
344    /// | Snake, Kebab, Flat, Lower | [Lowercase](Pattern::Lowercase) |
345    /// | Ada, Train, Pascal, UpperCamel, Title | [Capital](Pattern::Capital) |
346    /// | Camel | [Camel](Pattern::Camel) |
347    pub const fn pattern(&self) -> Pattern {
348        use Case::*;
349        match self {
350            Constant | UpperSnake | Cobol | UpperKebab | UpperFlat | Upper => Pattern::Uppercase,
351            Snake | Kebab | Flat | Lower => Pattern::Lowercase,
352            Ada | Train | Pascal | UpperCamel | Title => Pattern::Capital,
353            Camel => Pattern::Camel,
354            Sentence => Pattern::Sentence,
355            Custom { pattern, .. } => *pattern,
356        }
357    }
358
359    /// Split an identifier into words based on the boundaries of this case.
360    /// ```
361    /// use convert_case::Case;
362    /// assert_eq!(
363    ///     Case::Pascal.split(&"getTotalLength"),
364    ///     vec!["get", "Total", "Length"],
365    /// );
366    /// ```
367    pub fn split<T>(self, s: &T) -> Vec<&str>
368    where
369        T: AsRef<str>,
370    {
371        boundary::split(s, self.boundaries())
372    }
373
374    /// Mutate a list of words based on the pattern of this case.
375    /// ```
376    /// use convert_case::Case;
377    /// assert_eq!(
378    ///     Case::Snake.mutate(&["get", "Total", "Length"]),
379    ///     vec!["get", "total", "length"],
380    /// );
381    /// ```
382    pub fn mutate(self, words: &[&str]) -> Vec<String> {
383        self.pattern().mutate(words)
384    }
385
386    /// Join a list of words into a single identifier using the delimiter of this case.
387    /// ```
388    /// use convert_case::Case;
389    /// assert_eq!(
390    ///     Case::Snake.join(&[
391    ///         String::from("get"),
392    ///         String::from("total"),
393    ///         String::from("length")
394    ///     ]),
395    ///     String::from("get_total_length"),
396    /// );
397    /// ```
398    pub fn join(self, words: &[String]) -> String {
399        words.join(self.delimiter())
400    }
401
402    /// Array of all non-custom case enum variants.  Does not include aliases.
403    pub fn all_cases() -> &'static [Case<'static>] {
404        use Case::*;
405        &[
406            Snake, Constant, Ada, Kebab, Cobol, Train, Flat, UpperFlat, Pascal, Camel, Upper,
407            Lower, Title, Sentence,
408        ]
409    }
410}