//! Utilities to change the case of a string to another case. Supports “lower //! case,” “UPPER CASE,” “sentence case,” “Title Case,” “camelCase,” //! “PascalCase,” “kebab-case,” “Train-Case,” “snake_case,” and //! “CONSTANT_CASE.” //! //! For more information [Wikipedia][1] has an interesting article on these //! special case styles. //! //! [1]: https://en.wikipedia.org/wiki/Letter_case#Special_case_styles //! //! # Example //! ```rust //! use inflections::case::to_camel_case; //! //! assert_eq!(to_camel_case("Hello World"), "helloWorld".to_owned()); //! ``` use std::char::ToUppercase; use std::iter::Peekable; /// Converts any case into lower case ignoring separators. /// /// # Example /// ```rust /// # use inflections::case::to_lower_case; /// let lower = "hello world".to_owned(); /// assert_eq!(to_lower_case("hello world"), lower); /// assert_eq!(to_lower_case("HELLO WORLD"), lower); /// assert_eq!(to_lower_case("Hello World"), lower); /// assert_eq!(to_lower_case("helloWorld"), "helloworld".to_owned()); /// assert_eq!(to_lower_case("HelloWorld"), "helloworld".to_owned()); /// assert_eq!(to_lower_case("hello-world"), "hello-world".to_owned()); /// assert_eq!(to_lower_case("Hello-World"), "hello-world".to_owned()); /// assert_eq!(to_lower_case("hello_world"), "hello_world".to_owned()); /// assert_eq!(to_lower_case("HELLO_WORLD"), "hello_world".to_owned()); /// ``` pub fn to_lower_case(string: &str) -> String { string .chars() .flat_map(char::to_lowercase) .collect() } /// Check to see if a string is completely lower case. /// /// # Example /// ```rust /// # use inflections::case::is_lower_case; /// assert_eq!(is_lower_case("hello world"), true); /// assert_eq!(is_lower_case("HELLO WORLD"), false); /// assert_eq!(is_lower_case("Hello World"), false); /// assert_eq!(is_lower_case("helloWorld"), false); /// assert_eq!(is_lower_case("HelloWorld"), false); /// assert_eq!(is_lower_case("hello-world"), true); /// assert_eq!(is_lower_case("Hello-World"), false); /// assert_eq!(is_lower_case("hello_world"), true); /// assert_eq!(is_lower_case("HELLO_WORLD"), false); pub fn is_lower_case(string: &str) -> bool { string == to_lower_case(string) } /// Converts any case into UPPER CASE ignoring separators. /// /// # Example /// ```rust /// # use inflections::case::to_upper_case; /// let upper = "HELLO WORLD".to_owned(); /// assert_eq!(to_upper_case("hello world"), upper); /// assert_eq!(to_upper_case("HELLO WORLD"), upper); /// assert_eq!(to_upper_case("Hello World"), upper); /// assert_eq!(to_upper_case("helloWorld"), "HELLOWORLD".to_owned()); /// assert_eq!(to_upper_case("HelloWorld"), "HELLOWORLD".to_owned()); /// assert_eq!(to_upper_case("hello-world"), "HELLO-WORLD".to_owned()); /// assert_eq!(to_upper_case("Hello-World"), "HELLO-WORLD".to_owned()); /// assert_eq!(to_upper_case("hello_world"), "HELLO_WORLD".to_owned()); /// assert_eq!(to_upper_case("HELLO_WORLD"), "HELLO_WORLD".to_owned()); /// ``` pub fn to_upper_case(string: &str) -> String { string .chars() .flat_map(char::to_uppercase) .collect() } /// Check to see if a string is completely UPPER CASE. /// /// # Example /// ```rust /// # use inflections::case::is_upper_case; /// assert_eq!(is_upper_case("hello world"), false); /// assert_eq!(is_upper_case("HELLO WORLD"), true); /// assert_eq!(is_upper_case("Hello World"), false); /// assert_eq!(is_upper_case("helloWorld"), false); /// assert_eq!(is_upper_case("HelloWorld"), false); /// assert_eq!(is_upper_case("hello-world"), false); /// assert_eq!(is_upper_case("Hello-World"), false); /// assert_eq!(is_upper_case("hello_world"), false); /// assert_eq!(is_upper_case("HELLO_WORLD"), true); pub fn is_upper_case(string: &str) -> bool { string == to_upper_case(string) } /// Converts any case into traditional sentence case without capitalizing the /// first letter. /// /// # Example /// ```rust /// # use inflections::case::to_sentence_case; /// let sentence = "hello world".to_owned(); /// assert_eq!(to_sentence_case("hello world"), sentence); /// assert_eq!(to_sentence_case("HELLO WORLD"), sentence); /// assert_eq!(to_sentence_case("Hello World"), sentence); /// assert_eq!(to_sentence_case("helloWorld"), sentence); /// assert_eq!(to_sentence_case("HelloWorld"), sentence); /// assert_eq!(to_sentence_case("hello-world"), sentence); /// assert_eq!(to_sentence_case("Hello-World"), sentence); /// assert_eq!(to_sentence_case("hello_world"), sentence); /// assert_eq!(to_sentence_case("HELLO_WORLD"), sentence); /// ``` pub fn to_sentence_case(string: &str) -> String { string .chars() .map(|c| swap_separator(c, ' ')) .break_camel(' ') .flat_map(char::to_lowercase) .collect() } /// Check to see if a string is sentence case. /// /// # Example /// ```rust /// # use inflections::case::is_sentence_case; /// assert_eq!(is_sentence_case("hello world"), true); /// assert_eq!(is_sentence_case("HELLO WORLD"), false); /// assert_eq!(is_sentence_case("Hello World"), false); /// assert_eq!(is_sentence_case("helloWorld"), false); /// assert_eq!(is_sentence_case("HelloWorld"), false); /// assert_eq!(is_sentence_case("hello-world"), false); /// assert_eq!(is_sentence_case("Hello-World"), false); /// assert_eq!(is_sentence_case("hello_world"), false); /// assert_eq!(is_sentence_case("HELLO_WORLD"), false); pub fn is_sentence_case(string: &str) -> bool { string == to_sentence_case(string) } /// Converts any case into title case where *every* word is capitalized. /// /// # Example /// ```rust /// # use inflections::case::to_title_case; /// let title = "Hello World".to_owned(); /// assert_eq!(to_title_case("hello world"), title); /// assert_eq!(to_title_case("HELLO WORLD"), title); /// assert_eq!(to_title_case("Hello World"), title); /// assert_eq!(to_title_case("helloWorld"), title); /// assert_eq!(to_title_case("HelloWorld"), title); /// assert_eq!(to_title_case("hello-world"), title); /// assert_eq!(to_title_case("Hello-World"), title); /// assert_eq!(to_title_case("hello_world"), title); /// assert_eq!(to_title_case("HELLO_WORLD"), title); /// ``` pub fn to_title_case(string: &str) -> String { string .chars() .map(|c| swap_separator(c, ' ')) .break_camel(' ') .flat_map(char::to_lowercase) .capitalize_words() .collect() } /// Check to see if a string is Title Case. /// /// # Example /// ```rust /// # use inflections::case::is_title_case; /// assert_eq!(is_title_case("Hello World"), true); /// assert_eq!(is_title_case("hello world"), false); /// assert_eq!(is_title_case("HELLO WORLD"), false); /// assert_eq!(is_title_case("helloWorld"), false); /// assert_eq!(is_title_case("HelloWorld"), false); /// assert_eq!(is_title_case("hello-world"), false); /// assert_eq!(is_title_case("Hello-World"), false); /// assert_eq!(is_title_case("hello_world"), false); /// assert_eq!(is_title_case("HELLO_WORLD"), false); pub fn is_title_case(string: &str) -> bool { string == to_title_case(string) } /// Converts any case into camelCase. /// /// # Example /// ```rust /// # use inflections::case::to_camel_case; /// let camel = "helloWorld".to_owned(); /// assert_eq!(to_camel_case("hello world"), camel); /// assert_eq!(to_camel_case("HELLO WORLD"), camel); /// assert_eq!(to_camel_case("Hello World"), camel); /// assert_eq!(to_camel_case("helloWorld"), camel); /// assert_eq!(to_camel_case("HelloWorld"), camel); /// assert_eq!(to_camel_case("hello-world"), camel); /// assert_eq!(to_camel_case("Hello-World"), camel); /// assert_eq!(to_camel_case("hello_world"), camel); /// assert_eq!(to_camel_case("HELLO_WORLD"), camel); /// ``` pub fn to_camel_case(string: &str) -> String { string .chars() .scan((false, None), scan_to_camel) .collect() } /// Check to see if a string is camelCase. /// /// # Example /// ```rust /// # use inflections::case::is_camel_case; /// assert_eq!(is_camel_case("helloWorld"), true); /// assert_eq!(is_camel_case("hello world"), false); /// assert_eq!(is_camel_case("HELLO WORLD"), false); /// assert_eq!(is_camel_case("Hello World"), false); /// assert_eq!(is_camel_case("HelloWorld"), false); /// assert_eq!(is_camel_case("hello-world"), false); /// assert_eq!(is_camel_case("Hello-World"), false); /// assert_eq!(is_camel_case("hello_world"), false); /// assert_eq!(is_camel_case("HELLO_WORLD"), false); pub fn is_camel_case(string: &str) -> bool { string == to_camel_case(string) } /// Converts any case into PascalCase. /// /// # Example /// ```rust /// # use inflections::case::to_pascal_case; /// let pascal = "HelloWorld".to_owned(); /// assert_eq!(to_pascal_case("hello world"), pascal); /// assert_eq!(to_pascal_case("HELLO WORLD"), pascal); /// assert_eq!(to_pascal_case("Hello World"), pascal); /// assert_eq!(to_pascal_case("helloWorld"), pascal); /// assert_eq!(to_pascal_case("HelloWorld"), pascal); /// assert_eq!(to_pascal_case("hello-world"), pascal); /// assert_eq!(to_pascal_case("Hello-World"), pascal); /// assert_eq!(to_pascal_case("hello_world"), pascal); /// assert_eq!(to_pascal_case("HELLO_WORLD"), pascal); /// ``` pub fn to_pascal_case(string: &str) -> String { string .chars() .scan((true, None), scan_to_camel) .collect() } /// Check to see if a string is PascalCase. /// /// # Example /// ```rust /// # use inflections::case::is_pascal_case; /// assert_eq!(is_pascal_case("HelloWorld"), true); /// assert_eq!(is_pascal_case("hello world"), false); /// assert_eq!(is_pascal_case("HELLO WORLD"), false); /// assert_eq!(is_pascal_case("Hello World"), false); /// assert_eq!(is_pascal_case("helloWorld"), false); /// assert_eq!(is_pascal_case("hello-world"), false); /// assert_eq!(is_pascal_case("Hello-World"), false); /// assert_eq!(is_pascal_case("hello_world"), false); /// assert_eq!(is_pascal_case("HELLO_WORLD"), false); pub fn is_pascal_case(string: &str) -> bool { string == to_pascal_case(string) } /// Converts any case into kebab-case. /// /// # Example /// ```rust /// # use inflections::case::to_kebab_case; /// let kebab = "hello-world".to_owned(); /// assert_eq!(to_kebab_case("hello world"), kebab); /// assert_eq!(to_kebab_case("HELLO WORLD"), kebab); /// assert_eq!(to_kebab_case("Hello World"), kebab); /// assert_eq!(to_kebab_case("helloWorld"), kebab); /// assert_eq!(to_kebab_case("HelloWorld"), kebab); /// assert_eq!(to_kebab_case("hello-world"), kebab); /// assert_eq!(to_kebab_case("Hello-World"), kebab); /// assert_eq!(to_kebab_case("hello_world"), kebab); /// assert_eq!(to_kebab_case("HELLO_WORLD"), kebab); /// ``` pub fn to_kebab_case(string: &str) -> String { string .chars() .map(|c| swap_separator(c, '-')) .break_camel('-') .flat_map(char::to_lowercase) .collect() } /// Check to see if a string is kebab-case. /// /// # Example /// ```rust /// # use inflections::case::is_kebab_case; /// assert_eq!(is_kebab_case("hello-world"), true); /// assert_eq!(is_kebab_case("hello world"), false); /// assert_eq!(is_kebab_case("HELLO WORLD"), false); /// assert_eq!(is_kebab_case("Hello World"), false); /// assert_eq!(is_kebab_case("helloWorld"), false); /// assert_eq!(is_kebab_case("HelloWorld"), false); /// assert_eq!(is_kebab_case("Hello-World"), false); /// assert_eq!(is_kebab_case("hello_world"), false); /// assert_eq!(is_kebab_case("HELLO_WORLD"), false); pub fn is_kebab_case(string: &str) -> bool { string == to_kebab_case(string) } /// Converts any case into Train-Case. /// /// # Example /// ```rust /// # use inflections::case::to_train_case; /// let train = "Hello-World".to_owned(); /// assert_eq!(to_train_case("hello world"), train); /// assert_eq!(to_train_case("HELLO WORLD"), train); /// assert_eq!(to_train_case("Hello World"), train); /// assert_eq!(to_train_case("helloWorld"), train); /// assert_eq!(to_train_case("HelloWorld"), train); /// assert_eq!(to_train_case("hello-world"), train); /// assert_eq!(to_train_case("Hello-World"), train); /// assert_eq!(to_train_case("hello_world"), train); /// assert_eq!(to_train_case("HELLO_WORLD"), train); /// ``` pub fn to_train_case(string: &str) -> String { string .chars() .map(|c| swap_separator(c, '-')) .break_camel('-') .flat_map(char::to_lowercase) .capitalize_words() .collect() } /// Check to see if a string is Train-Case. /// /// # Example /// ```rust /// # use inflections::case::is_train_case; /// assert_eq!(is_train_case("Hello-World"), true); /// assert_eq!(is_train_case("hello world"), false); /// assert_eq!(is_train_case("HELLO WORLD"), false); /// assert_eq!(is_train_case("Hello World"), false); /// assert_eq!(is_train_case("helloWorld"), false); /// assert_eq!(is_train_case("HelloWorld"), false); /// assert_eq!(is_train_case("hello-world"), false); /// assert_eq!(is_train_case("hello_world"), false); /// assert_eq!(is_train_case("HELLO_WORLD"), false); pub fn is_train_case(string: &str) -> bool { string == to_train_case(string) } /// Converts any case into snake_case. /// /// # Example /// ```rust /// # use inflections::case::to_snake_case; /// let snake = "hello_world".to_owned(); /// assert_eq!(to_snake_case("hello world"), snake); /// assert_eq!(to_snake_case("HELLO WORLD"), snake); /// assert_eq!(to_snake_case("Hello World"), snake); /// assert_eq!(to_snake_case("helloWorld"), snake); /// assert_eq!(to_snake_case("HelloWorld"), snake); /// assert_eq!(to_snake_case("hello-world"), snake); /// assert_eq!(to_snake_case("Hello-World"), snake); /// assert_eq!(to_snake_case("hello_world"), snake); /// assert_eq!(to_snake_case("HELLO_WORLD"), snake); /// ``` pub fn to_snake_case(string: &str) -> String { string .chars() .map(|c| swap_separator(c, '_')) .break_camel('_') .flat_map(char::to_lowercase) .collect() } /// Check to see if a string is snake_case. /// /// # Example /// ```rust /// # use inflections::case::is_snake_case; /// assert_eq!(is_snake_case("hello_world"), true); /// assert_eq!(is_snake_case("hello world"), false); /// assert_eq!(is_snake_case("HELLO WORLD"), false); /// assert_eq!(is_snake_case("Hello World"), false); /// assert_eq!(is_snake_case("helloWorld"), false); /// assert_eq!(is_snake_case("HelloWorld"), false); /// assert_eq!(is_snake_case("hello-world"), false); /// assert_eq!(is_snake_case("Hello-World"), false); /// assert_eq!(is_snake_case("HELLO_WORLD"), false); pub fn is_snake_case(string: &str) -> bool { string == to_snake_case(string) } /// Converts any case into CONSTANT_CASE. /// /// # Example /// ```rust /// # use inflections::case::to_constant_case; /// let constant = "HELLO_WORLD".to_owned(); /// assert_eq!(to_constant_case("hello world"), constant); /// assert_eq!(to_constant_case("HELLO WORLD"), constant); /// assert_eq!(to_constant_case("Hello World"), constant); /// assert_eq!(to_constant_case("helloWorld"), constant); /// assert_eq!(to_constant_case("HelloWorld"), constant); /// assert_eq!(to_constant_case("hello-world"), constant); /// assert_eq!(to_constant_case("Hello-World"), constant); /// assert_eq!(to_constant_case("hello_world"), constant); /// assert_eq!(to_constant_case("HELLO_WORLD"), constant); /// ``` pub fn to_constant_case(string: &str) -> String { string .chars() .map(|c| swap_separator(c, '_')) .break_camel('_') .flat_map(char::to_uppercase) .collect() } /// Check to see if a string is CONSTANT_CASE. /// /// # Example /// ```rust /// # use inflections::case::is_constant_case; /// assert_eq!(is_constant_case("HELLO_WORLD"), true); /// assert_eq!(is_constant_case("hello world"), false); /// assert_eq!(is_constant_case("HELLO WORLD"), false); /// assert_eq!(is_constant_case("Hello World"), false); /// assert_eq!(is_constant_case("helloWorld"), false); /// assert_eq!(is_constant_case("HelloWorld"), false); /// assert_eq!(is_constant_case("hello-world"), false); /// assert_eq!(is_constant_case("Hello-World"), false); /// assert_eq!(is_constant_case("hello_world"), false); pub fn is_constant_case(string: &str) -> bool { string == to_constant_case(string) } /// Checks if a character is a separator. #[inline] fn is_separator(c: char) -> bool { c == ' ' || c == '-' || c == '_' } /// Swaps the current character (which may be a separator), with the separator /// of choice. Currently ' ', '-', and '_' are considered separators which will /// be swapped. #[inline] fn swap_separator(c: char, sep: char) -> char { if is_separator(c) { sep } else { c } } /// The function to be used with the iterator `scan` method which converts a /// char iterator into a string iterator which has /// removed/uppercased/lowercased the bits which need for the conversion to be /// successful. This would work best with a `flat_scan`. #[inline] fn scan_to_camel(state: &mut (bool, Option), curr: char) -> Option { // Store the last character in the scope and update the state to use the // current character. let last = state.1; state.1 = Some(curr); if state.0 { // If the state has signaled the next character must be capitalized, // capitalize it and mark the state as finished. state.0 = false; Some(curr.to_uppercase().collect()) } else if is_separator(curr) { // If the current character is a separator, mark the state to capitalize // the next character and remove the separator. state.0 = true; Some("".to_owned()) } else if !last.map_or(false, char::is_lowercase) { // If the last character was not lowercase, this character should be // lower cased. This magic preserves camelCase strings while lowercasing // cases like CONSTANT_CASE. Some(curr.to_lowercase().collect()) } else { // Otherwise, just return the character. let mut string = String::with_capacity(1); string.push(curr); Some(string) } } /// Trait with some extra methods for the iterators we use. trait Extras: Iterator { /// Uses the `BreakCamel` type to break apart camel case strings, i.e. /// strings like `helloWorld` to `hello world` using the `sep` argument to /// seperate the new words. #[inline] fn break_camel(self, sep: char) -> BreakCamel where Self: Sized { BreakCamel { iter: self.peekable(), sep: sep, br: false } } /// Uses the `CapitalizeWords` type to capitilize individual words seperated /// by a separator (as defined by `is_separator`). #[inline] fn capitalize_words(self) -> CapitalizeWords where Self: Sized { let mut iter = self.peekable(); // If the first character is not a separator, we want to capitilize it. let cap = !iter.peek().cloned().map_or(false, is_separator); CapitalizeWords { iter: iter, cap: cap, upper: None } } } /// Add the extra methods to the iterators. impl Extras for I where I: Iterator {} /// Utility for breaking apart camelCased strings. struct BreakCamel where I: Iterator { /// The source iterator. iter: Peekable, /// Character to use when breaking apart camelCase strings. sep: char, /// Iterator state representing whether the iterator should insert a break. br: bool } impl Iterator for BreakCamel where I: Iterator { type Item = char; #[inline] fn next(&mut self) -> Option { // If we have been signaled to break, the next item is a separator and we // should disable break mode. if self.br { self.br = false; return Some(self.sep); } match (self.iter.next(), self.iter.peek()) { // If we have a current character that is lowercase and we have a next // character that is uppercase, we need to break the string apart next // time `next` is called. (Some(curr), Some(next)) if curr.is_lowercase() && next.is_uppercase() => { self.br = true; Some(curr) }, // Otherwise behave as normal. (Some(curr), _) => Some(curr), (None, _) => None } } } /// Utility for capitalizing words. struct CapitalizeWords where I: Iterator { /// The source iterator. iter: Peekable, /// Iteration state indicating whether or not the next letter should be /// capitalized. cap: bool, /// An iterator for the letter being upper cased. upper: Option } impl Iterator for CapitalizeWords where I: Iterator { type Item = char; #[inline] fn next(&mut self) -> Option { // Note: only one control path should allow this loop to continue. loop { // If there is an upper casing still in progress… if let Some(ref mut upper) = self.upper { if let Some(next) = upper.next() { // If there was a character in the uppercase iterator, return it. return Some(next); } } // If we are here this means we have exhausted the upper case iterator, // therefore we reset the upper iterator state. if self.upper.is_some() { self.upper = None; } if let Some(c) = self.iter.next() { // If there is a character… if self.cap { // If we have been signaled to capitalize the next character, disable // the signal, and enable the capitalization iterator. self.cap = false; self.upper = Some(c.to_uppercase()); // We want it to loop back here… } else { // Otherwise return the character, but if it is a separator, and the // next character is not a separator—signal the next character should // be capitalized. if is_separator(c) && !self.iter.peek().cloned().map_or(false, is_separator) { self.cap = true; } return Some(c); } } else { // End the iterator. return None; } } } }