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}