1 use crate::{capitalize, lowercase, transform};
2 
3 /// This trait defines a mixed case conversion.
4 ///
5 /// In mixedCase, word boundaries are indicated by capital letters, excepting
6 /// the first word.
7 ///
8 /// ## Example:
9 ///
10 /// ```rust
11 /// use heck::MixedCase;
12 ///
13 /// let sentence = "It is we who built these palaces and cities.";
14 /// assert_eq!(sentence.to_mixed_case(), "itIsWeWhoBuiltThesePalacesAndCities");
15 /// ```
16 pub trait MixedCase: ToOwned {
17     /// Convert this type to mixed case.
to_mixed_case(&self) -> Self::Owned18     fn to_mixed_case(&self) -> Self::Owned;
19 }
20 
21 impl MixedCase for str {
to_mixed_case(&self) -> String22     fn to_mixed_case(&self) -> String {
23         transform(self, |s, out| {
24             if out.is_empty() { lowercase(s, out); }
25             else { capitalize(s, out) }
26         }, |_| {})
27     }
28 }
29 
30 #[cfg(test)]
31 mod tests {
32     use super::MixedCase;
33 
34     macro_rules! t {
35         ($t:ident : $s1:expr => $s2:expr) => {
36             #[test]
37             fn $t() {
38                 assert_eq!($s1.to_mixed_case(), $s2)
39             }
40         }
41     }
42 
43     t!(test1: "CamelCase" => "camelCase");
44     t!(test2: "This is Human case." => "thisIsHumanCase");
45     t!(test3: "MixedUP CamelCase, with some Spaces" => "mixedUpCamelCaseWithSomeSpaces");
46     t!(test4: "mixed_up_ snake_case, with some _spaces" => "mixedUpSnakeCaseWithSomeSpaces");
47     t!(test5: "kebab-case" => "kebabCase");
48     t!(test6: "SHOUTY_SNAKE_CASE" => "shoutySnakeCase");
49     t!(test7: "snake_case" => "snakeCase");
50     t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "thisContainsAllKindsOfWordBoundaries");
51     t!(test9: "XΣXΣ baffle" => "xσxςBaffle");
52     t!(test10: "XMLHttpRequest" => "xmlHttpRequest");
53     // TODO unicode tests
54 }
55