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}