case/
lib.rs

1pub trait CaseExt {
2    type Owned;
3
4    /// Create a new string from a string slice with replacing the first character with its
5    /// to ASCII upper case counterpart (if one exists).
6    ///
7    /// # Examples
8    ///
9    /// ```
10    /// use case::CaseExt;
11    ///
12    /// assert_eq!(&CaseExt::to_capitalized("stringy string"), "Stringy string");
13    /// assert_eq!(&CaseExt::to_capitalized("言語"), "言語");
14    /// ```
15    fn to_capitalized(&self) -> Self::Owned;
16
17    /// Check whether a string is capitalized.
18    ///
19    /// If the the list is empty, the string is not considered capitalized.
20    ///
21    /// # Examples
22    ///
23    /// ```
24    /// use case::CaseExt;
25    ///
26    /// assert_eq!("Stringy string".is_capitalized(), true);
27    /// assert_eq!("hello world".is_capitalized(), false);
28    /// assert_eq!("".is_capitalized(), false);
29    /// ```
30    fn is_capitalized(&self) -> bool;
31
32    /// Convert a string slice from snake case to a new capitalized camel case string.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// use case::CaseExt;
38    ///
39    /// assert_eq!(&"a_string_and_a_miss".to_camel(), "AStringAndAMiss");
40    /// assert_eq!(&"fooby".to_camel(), "Fooby");
41    /// assert_eq!(&"_wild__underscore_s_".to_camel(), "WildUnderscoreS");
42    /// assert_eq!(&"言_語".to_camel(), "言語");
43    /// ```
44    fn to_camel(&self) -> Self::Owned;
45
46    /// Convert a string slice from snake case to a new uncapitalized camel case string.
47    ///
48    /// # Examples
49    ///
50    /// ```
51    /// use case::CaseExt;
52    ///
53    /// assert_eq!(&"string_henry_iii".to_camel_lowercase(), "stringHenryIii");
54    /// assert_eq!(&"fooby".to_camel_lowercase(), "fooby");
55    /// assert_eq!(&"_wild__underscore_s_".to_camel_lowercase(), "wildUnderscoreS");
56    /// assert_eq!(&"言_語".to_camel_lowercase(), "言語");
57    /// ```
58    fn to_camel_lowercase(&self) -> Self::Owned;
59
60    /// Check whether a string is in camel lowercase.
61    ///
62    /// If the the list is empty, the string is not considered camel lowercase.
63    ///
64    /// # Examples
65    ///
66    /// ```
67    /// use case::CaseExt;
68    ///
69    /// assert_eq!("Stringy string".is_camel_lowercase(), false);
70    /// assert_eq!("helloWorld".is_camel_lowercase(), true);
71    /// assert_eq!("WildUnderscoreS".is_camel_lowercase(), false);
72    /// assert_eq!("".is_capitalized(), false);
73    /// ```
74    fn is_camel_lowercase(&self) -> bool;
75
76    /// Convert a string from camel case to snake case.
77    ///
78    /// # Examples
79    ///
80    /// ```
81    /// use case::CaseExt;
82    ///
83    /// assert_eq!(&"martinLutherStringJr".to_snake(), "martin_luther_string_jr");
84    /// assert_eq!(&"fooby".to_snake(), "fooby");
85    /// assert_eq!(&"WildUnderscoreS".to_snake(), "wild_underscore_s");
86    /// assert_eq!(&"言語".to_snake(), "言語");
87    /// ```
88    fn to_snake(&self) -> Self::Owned;
89
90    /// Replaces underscores with dashes in a string.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use case::CaseExt;
96    ///
97    /// assert_eq!(&"stringing_in_the_rain".to_dashed(), "stringing-in-the-rain");
98    /// ```
99    fn to_dashed(&self) -> Self::Owned;
100}
101
102impl CaseExt for str {
103    type Owned = String;
104
105    fn to_capitalized(&self) -> Self::Owned {
106        let mut result = String::with_capacity(self.len());
107
108        for (i, c) in self.chars().enumerate() {
109            result.push(if i == 0 {
110                c.to_ascii_uppercase()
111            } else {
112                c
113            });
114        }
115
116        result
117    }
118
119    fn is_capitalized(&self) -> bool {
120        if let Some(first) = self.chars().next() {
121            first.is_uppercase()
122        } else {
123            false
124        }
125    }
126
127    fn to_camel(&self) -> String {
128        to_camel_internal(self, true)
129    }
130
131    fn is_camel_lowercase(&self) -> bool {
132        match self.chars().next() {
133            Some(first) if first.is_uppercase() => return false,
134            _ => ()
135        }
136
137        !self.contains('_')
138    }
139
140    fn to_camel_lowercase(&self) -> String {
141        to_camel_internal(self, false)
142    }
143
144    fn to_snake(&self) -> String {
145        // The first character is never prepended with an underscore, so skip it even if it is an
146        // uppercase ASCII character.
147        let underscore_count = self.chars().skip(1).filter(|&c| is_ascii_uppercase(c)).count();
148        let mut result = String::with_capacity(self.len() + underscore_count);
149
150        for (i, c) in self.chars().enumerate() {
151            if is_ascii_uppercase(c) {
152                if i != 0 {
153                    result.push('_');
154                }
155                result.push(c.to_ascii_lowercase());
156            } else {
157                result.push(c);
158            }
159        }
160
161        result
162    }
163
164    fn to_dashed(&self) -> String {
165        let mut result = String::with_capacity(self.len());
166
167        for c in self.chars() {
168            result.push(match c {
169                '_' => '-',
170                c => c
171            });
172        }
173
174        result
175    }
176}
177
178fn to_camel_internal(term: &str, uppercase_first_letter: bool) -> String {
179    let underscore_count = term.chars().filter(|c| *c == '_').count();
180    let mut result = String::with_capacity(term.len() - underscore_count);
181    let mut at_new_word = uppercase_first_letter;
182
183    for c in term.chars().skip_while(|&c| c == '_') {
184        if c == '_' {
185            at_new_word = true;
186        } else if at_new_word {
187            result.push(c.to_ascii_uppercase());
188            at_new_word = false;
189        } else {
190            result.push(c);
191        }
192    }
193
194    result
195}
196
197#[inline]
198fn is_ascii_uppercase(c: char) -> bool {
199    c >= 'A' && c <= 'Z'
200}