Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+209
View File
@@ -0,0 +1,209 @@
use crate::case::*;
/// Converts a `&str` to camelCase `String`
///
/// ```
/// use cruet::case::to_camel_case;
///
/// assert_eq!(to_camel_case("fooBar"), "fooBar");
/// assert_eq!(to_camel_case("FOO_BAR"), "fooBar");
/// assert_eq!(to_camel_case("Foo Bar"), "fooBar");
/// assert_eq!(to_camel_case("foo_bar"), "fooBar");
/// assert_eq!(to_camel_case("Foo bar"), "fooBar");
/// assert_eq!(to_camel_case("foo-bar"), "fooBar");
/// assert_eq!(to_camel_case("FooBar"), "fooBar");
/// assert_eq!(to_camel_case("FooBar3"), "fooBar3");
/// assert_eq!(to_camel_case("Foo-Bar"), "fooBar");
/// ```
pub fn to_camel_case(non_camelized_string: &str) -> String {
let options = CamelOptions {
new_word: false,
last_char: ' ',
first_word: false,
injectable_char: ' ',
has_separator: false,
inverted: false,
concat_num: true,
};
to_case_camel_like(non_camelized_string, options)
}
/// Determines if a `&str` is camelCase bool``
///
/// ```
/// use cruet::case::is_camel_case;
///
/// assert!(is_camel_case("foo"));
/// assert!(is_camel_case("fooBarIsAReallyReally3LongString"));
/// assert!(is_camel_case("fooBarIsAReallyReallyLongString"));
///
/// assert!(!is_camel_case("Foo"));
/// assert!(!is_camel_case("foo-bar-string-that-is-really-really-long"));
/// assert!(!is_camel_case("FooBarIsAReallyReallyLongString"));
/// assert!(!is_camel_case("FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG"));
/// assert!(!is_camel_case("foo_bar_string_that_is_really_really_long"));
/// assert!(!is_camel_case("Foo bar string that is really really long"));
/// assert!(!is_camel_case("Foo Bar Is A Really Really Long String"));
/// ```
pub fn is_camel_case(test_string: &str) -> bool {
to_camel_case(test_string) == test_string
}
#[cfg(test)]
mod tests {
use super::{is_camel_case, to_camel_case};
#[test]
fn from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
let expected: String = "fooBar".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
let expected: String = "fooBar".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
let expected: String = "fooBar".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
let expected: String = "fooBar".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
let expected: String = "fooBar".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
let expected: String = "fooBar".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
let expected: String = "fooBar".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
let expected: String = "fooBar".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn from_case_with_loads_of_space() {
let convertible_string: String = "foo bar".to_owned();
let expected: String = "fooBar".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn a_name_with_a_dot() {
let convertible_string: String = "Robert C. Martin".to_owned();
let expected: String = "robertCMartin".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn random_text_with_bad_chars() {
let convertible_string: String = "Random text with *(bad) chars".to_owned();
let expected: String = "randomTextWithBadChars".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn trailing_bad_chars() {
let convertible_string: String = "trailing bad_chars*(()())".to_owned();
let expected: String = "trailingBadChars".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn leading_bad_chars() {
let convertible_string: String = "-!#$%leading bad chars".to_owned();
let expected: String = "leadingBadChars".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn wrapped_in_bad_chars() {
let convertible_string: String =
"-!#$%wrapped in bad chars&*^*&(&*^&(<><?>><?><>))".to_owned();
let expected: String = "wrappedInBadChars".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn has_a_sign() {
let convertible_string: String = "has a + sign".to_owned();
let expected: String = "hasASign".to_owned();
assert_eq!(to_camel_case(&convertible_string), expected)
}
#[test]
fn is_correct_from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
assert_eq!(is_camel_case(&convertible_string), true)
}
#[test]
fn is_correct_from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
assert_eq!(is_camel_case(&convertible_string), false)
}
#[test]
fn is_correct_from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
assert_eq!(is_camel_case(&convertible_string), false)
}
#[test]
fn is_correct_from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
assert_eq!(is_camel_case(&convertible_string), false)
}
#[test]
fn is_correct_from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
assert_eq!(is_camel_case(&convertible_string), false)
}
#[test]
fn is_correct_from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
assert_eq!(is_camel_case(&convertible_string), false)
}
#[test]
fn is_correct_from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
assert_eq!(is_camel_case(&convertible_string), false)
}
#[test]
fn is_correct_from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
assert_eq!(is_camel_case(&convertible_string), false)
}
}
+226
View File
@@ -0,0 +1,226 @@
use crate::case::*;
use crate::string::singularize::to_singular;
/// Converts a `&str` to `ClassCase` `String`
///
/// ```
/// use cruet::case::to_class_case;
///
/// assert_eq!(to_class_case("FooBar"), "FooBar");
/// assert_eq!(to_class_case("FooBars"), "FooBar");
/// assert_eq!(to_class_case("Foo Bar"), "FooBar");
/// assert_eq!(to_class_case("foo-bar"), "FooBar");
/// assert_eq!(to_class_case("fooBar"), "FooBar");
/// assert_eq!(to_class_case("FOO_BAR"), "FooBar");
/// assert_eq!(to_class_case("foo_bars"), "FooBar");
/// assert_eq!(to_class_case("Foo bar"), "FooBar");
/// ```
pub fn to_class_case(non_class_case_string: &str) -> String {
let options = CamelOptions {
new_word: true,
last_char: ' ',
first_word: false,
injectable_char: ' ',
has_separator: false,
inverted: false,
concat_num: true,
};
let class_plural = to_case_camel_like(non_class_case_string, options);
let split: (&str, &str) =
class_plural.split_at(class_plural.rfind(char::is_uppercase).unwrap_or(0));
format!("{}{}", split.0, to_singular(split.1))
}
/// Determines if a `&str` is `ClassCase` `bool`
///
/// ```
/// use cruet::case::is_class_case;
///
/// assert!(is_class_case("Foo"));
/// assert!(is_class_case("FooBarIsAReallyReallyLongString"));
///
/// assert!(!is_class_case("foo"));
/// assert!(!is_class_case("FooBarIsAReallyReallyLongStrings"));
/// assert!(!is_class_case("foo-bar-string-that-is-really-really-long"));
/// assert!(!is_class_case("foo_bar_is_a_really_really_long_strings"));
/// assert!(!is_class_case("fooBarIsAReallyReallyLongString"));
/// assert!(!is_class_case("FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG"));
/// assert!(!is_class_case("foo_bar_string_that_is_really_really_long"));
/// assert!(!is_class_case("Foo bar string that is really really long"));
/// assert!(!is_class_case("Foo Bar Is A Really Really Long String"));
/// ```
pub fn is_class_case(test_string: &str) -> bool {
to_class_case(test_string) == test_string
}
#[cfg(test)]
mod tests {
use super::{is_class_case, to_class_case};
#[test]
fn from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn from_screaming_class_case() {
let convertible_string: String = "FOO_BAR".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn from_table_case() {
let convertible_string: String = "foo_bars".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn from_case_with_loads_of_space() {
let convertible_string: String = "foo bar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn a_name_with_a_dot() {
let convertible_string: String = "Robert C. Martin".to_owned();
let expected: String = "RobertCMartin".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn random_text_with_bad_chars() {
let convertible_string: String = "Random text with *(bad) chars".to_owned();
let expected: String = "RandomTextWithBadChar".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn trailing_bad_chars() {
let convertible_string: String = "trailing bad_chars*(()())".to_owned();
let expected: String = "TrailingBadChar".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn leading_bad_chars() {
let convertible_string: String = "-!#$%leading bad chars".to_owned();
let expected: String = "LeadingBadChar".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn wrapped_in_bad_chars() {
let convertible_string: String =
"-!#$%wrapped in bad chars&*^*&(&*^&(<><?>><?><>))".to_owned();
let expected: String = "WrappedInBadChar".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn has_a_sign() {
let convertible_string: String = "has a + sign".to_owned();
let expected: String = "HasASign".to_owned();
assert_eq!(to_class_case(&convertible_string), expected)
}
#[test]
fn is_correct_from_class_case() {
let convertible_string: String = "fooBar".to_owned();
assert_eq!(is_class_case(&convertible_string), false)
}
#[test]
fn is_correct_from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
assert_eq!(is_class_case(&convertible_string), true)
}
#[test]
fn is_correct_from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
assert_eq!(is_class_case(&convertible_string), false)
}
#[test]
fn is_correct_from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
assert_eq!(is_class_case(&convertible_string), false)
}
#[test]
fn is_correct_from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
assert_eq!(is_class_case(&convertible_string), false)
}
#[test]
fn is_correct_from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
assert_eq!(is_class_case(&convertible_string), false)
}
#[test]
fn is_correct_from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
assert_eq!(is_class_case(&convertible_string), false)
}
#[test]
fn is_correct_from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
assert_eq!(is_class_case(&convertible_string), false)
}
#[test]
fn is_correct_from_table_case() {
let convertible_string: String = "FooBar".to_owned();
assert_eq!(is_class_case(&convertible_string), true)
}
}
+143
View File
@@ -0,0 +1,143 @@
use crate::case::*;
/// Determines if a `&str` is `kebab-case`
///
/// ```
/// use cruet::case::is_kebab_case;
///
/// assert!(is_kebab_case("foo-bar-string-that-is-really-really-long"));
/// assert!(!is_kebab_case("FooBarIsAReallyReallyLongString"));
/// assert!(!is_kebab_case("fooBarIsAReallyReallyLongString"));
/// assert!(!is_kebab_case("FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG"));
/// assert!(!is_kebab_case("foo_bar_string_that_is_really_really_long"));
/// assert!(!is_kebab_case("Foo bar string that is really really long"));
/// assert!(!is_kebab_case("Foo Bar Is A Really Really Long String"));
/// ```
pub fn is_kebab_case(test_string: &str) -> bool {
test_string == to_kebab_case(test_string)
}
/// Converts a `&str` to `kebab-case` `String`
///
/// ```
/// use cruet::case::to_kebab_case;
///
/// assert_eq!(to_kebab_case("foo-bar"), "foo-bar");
/// assert_eq!(to_kebab_case("FOO_BAR"), "foo-bar");
/// assert_eq!(to_kebab_case("foo_bar"), "foo-bar");
/// assert_eq!(to_kebab_case("Foo Bar"), "foo-bar");
/// assert_eq!(to_kebab_case("Foo bar"), "foo-bar");
/// assert_eq!(to_kebab_case("FooBar"), "foo-bar");
/// assert_eq!(to_kebab_case("fooBar"), "foo-bar");
/// ```
pub fn to_kebab_case(non_kebab_case_string: &str) -> String {
to_case_snake_like(non_kebab_case_string, "-", "lower")
}
#[cfg(test)]
mod tests {
use super::{is_kebab_case, to_kebab_case};
#[test]
fn from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
let expected: String = "foo-bar".to_owned();
assert_eq!(to_kebab_case(&convertible_string), expected)
}
#[test]
fn from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
let expected: String = "foo-bar".to_owned();
assert_eq!(to_kebab_case(&convertible_string), expected)
}
#[test]
fn from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
let expected: String = "foo-bar".to_owned();
assert_eq!(to_kebab_case(&convertible_string), expected)
}
#[test]
fn from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
let expected: String = "foo-bar".to_owned();
assert_eq!(to_kebab_case(&convertible_string), expected)
}
#[test]
fn from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
let expected: String = "foo-bar".to_owned();
assert_eq!(to_kebab_case(&convertible_string), expected)
}
#[test]
fn from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
let expected: String = "foo-bar".to_owned();
assert_eq!(to_kebab_case(&convertible_string), expected)
}
#[test]
fn from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
let expected: String = "foo-bar".to_owned();
assert_eq!(to_kebab_case(&convertible_string), expected)
}
#[test]
fn from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
let expected: String = "foo-bar".to_owned();
assert_eq!(to_kebab_case(&convertible_string), expected)
}
#[test]
fn is_correct_from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
assert_eq!(is_kebab_case(&convertible_string), false)
}
#[test]
fn is_correct_from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
assert_eq!(is_kebab_case(&convertible_string), false)
}
#[test]
fn is_correct_from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
assert_eq!(is_kebab_case(&convertible_string), true)
}
#[test]
fn is_correct_from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
assert_eq!(is_kebab_case(&convertible_string), false)
}
#[test]
fn is_correct_from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
assert_eq!(is_kebab_case(&convertible_string), false)
}
#[test]
fn is_correct_from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
assert_eq!(is_kebab_case(&convertible_string), false)
}
#[test]
fn is_correct_from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
assert_eq!(is_kebab_case(&convertible_string), false)
}
#[test]
fn is_correct_from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
assert_eq!(is_kebab_case(&convertible_string), false)
}
}
+419
View File
@@ -0,0 +1,419 @@
/// Provides conversion to and detection of class case strings.
///
/// This version singularizes strings.
///
/// Example string `ClassCase`
pub mod class;
pub use class::{is_class_case, to_class_case};
/// Provides conversion to and detection of camel case strings.
///
/// Example string `camelCase`
pub mod camel;
pub use camel::{is_camel_case, to_camel_case};
/// Provides conversion to and detection of snake case strings.
///
/// Example string `snake_case`
pub mod snake;
pub use snake::{is_snake_case, to_snake_case};
/// Provides conversion to and detection of screaming snake case strings.
///
/// Example string `SCREAMING_SNAKE_CASE`
pub mod screaming_snake;
pub use screaming_snake::{is_screaming_snake_case, to_screaming_snake_case};
/// Provides conversion to and detection of kebab case strings.
///
/// Example string `kebab-case`
pub mod kebab;
pub use kebab::{is_kebab_case, to_kebab_case};
/// Provides conversion to and detection of train case strings.
///
/// Example string `Train-Case`
pub mod train;
pub use train::{is_train_case, to_train_case};
/// Provides conversion to and detection of sentence case strings.
///
/// Example string `Sentence case`
pub mod sentence;
pub use sentence::{is_sentence_case, to_sentence_case};
/// Provides conversion to and detection of title case strings.
///
/// Example string `Title Case`
pub mod title;
pub use title::{is_title_case, to_title_case};
/// Provides conversion to and detection of table case strings.
///
/// Example string `table_cases`
pub mod table;
pub use table::{is_table_case, to_table_case};
/// Provides conversion to pascal case strings.
///
/// Example string `PascalCase`
pub mod pascal;
pub use pascal::{is_pascal_case, to_pascal_case};
#[doc(hidden)]
pub struct CamelOptions {
pub new_word: bool,
pub last_char: char,
pub first_word: bool,
pub injectable_char: char,
pub has_separator: bool,
pub inverted: bool,
pub concat_num: bool,
}
#[doc(hidden)]
pub fn to_case_snake_like(convertible_string: &str, replace_with: &str, case: &str) -> String {
let mut first_character: bool = true;
let mut result: String = String::with_capacity(convertible_string.len() * 2);
let chars: Vec<char> = trim_right(convertible_string).chars().collect();
for (index, &current_char) in chars.iter().enumerate() {
if char_is_separator(&current_char) {
if !first_character {
first_character = true;
result.push(replace_with.chars().next().unwrap_or('_'));
}
} else if requires_separator(current_char, index, first_character, &chars) {
first_character = false;
result = snake_like_with_separator(result, replace_with, &current_char, case)
} else {
first_character = false;
result = snake_like_no_separator(result, &current_char, case)
}
}
result
}
#[doc(hidden)]
pub fn to_case_camel_like(convertible_string: &str, camel_options: CamelOptions) -> String {
let mut new_word: bool = camel_options.new_word;
let mut first_word: bool = camel_options.first_word;
let mut last_char: char = camel_options.last_char;
let mut found_real_char: bool = false;
let mut result: String = String::with_capacity(convertible_string.len() * 2);
for character in trim_right(convertible_string).chars() {
if char_is_separator(&character) && found_real_char {
new_word = true;
} else if !found_real_char && is_not_alphanumeric(character) {
continue;
} else if character.is_numeric() && camel_options.concat_num {
found_real_char = true;
new_word = true;
result.push(character);
} else if last_char_lower_current_is_upper_or_new_word(new_word, last_char, character) {
found_real_char = true;
new_word = false;
result = append_on_new_word(result, first_word, character, &camel_options);
first_word = false;
} else {
found_real_char = true;
last_char = character;
result.push(character.to_ascii_lowercase());
}
}
result
}
#[inline]
fn append_on_new_word(
mut result: String,
first_word: bool,
character: char,
camel_options: &CamelOptions,
) -> String {
if not_first_word_and_has_separator(first_word, camel_options.has_separator) {
result.push(camel_options.injectable_char);
}
if first_word_or_not_inverted(first_word, camel_options.inverted) {
result.push(character.to_ascii_uppercase());
} else {
result.push(character.to_ascii_lowercase());
}
result
}
fn not_first_word_and_has_separator(first_word: bool, has_separator: bool) -> bool {
has_separator && !first_word
}
fn first_word_or_not_inverted(first_word: bool, inverted: bool) -> bool {
!inverted || first_word
}
fn last_char_lower_current_is_upper_or_new_word(
new_word: bool,
last_char: char,
character: char,
) -> bool {
new_word || ((last_char.is_lowercase() && character.is_uppercase()) && (last_char != ' '))
}
fn char_is_separator(character: &char) -> bool {
is_not_alphanumeric(*character)
}
fn trim_right(convertible_string: &str) -> &str {
convertible_string.trim_end_matches(is_not_alphanumeric)
}
fn is_not_alphanumeric(character: char) -> bool {
!character.is_alphanumeric()
}
#[inline]
fn requires_separator(
current_char: char,
index: usize,
first_character: bool,
chars: &[char],
) -> bool {
!first_character
&& char_is_uppercase(current_char)
&& next_or_previous_char_is_lowercase(chars, index)
}
#[inline]
fn snake_like_no_separator(mut accumulator: String, current_char: &char, case: &str) -> String {
if case == "lower" {
accumulator.push(current_char.to_ascii_lowercase());
accumulator
} else {
accumulator.push(current_char.to_ascii_uppercase());
accumulator
}
}
#[inline]
fn snake_like_with_separator(
mut accumulator: String,
replace_with: &str,
current_char: &char,
case: &str,
) -> String {
if case == "lower" {
accumulator.push(replace_with.chars().next().unwrap_or('_'));
accumulator.push(current_char.to_ascii_lowercase());
accumulator
} else {
accumulator.push(replace_with.chars().next().unwrap_or('_'));
accumulator.push(current_char.to_ascii_uppercase());
accumulator
}
}
fn next_or_previous_char_is_lowercase(chars: &[char], index: usize) -> bool {
chars.get(index + 1).copied().unwrap_or('A').is_lowercase()
|| index
.checked_sub(1)
.and_then(|i| chars.get(i))
.copied()
.unwrap_or('A')
.is_lowercase()
}
fn char_is_uppercase(test_char: char) -> bool {
test_char == test_char.to_ascii_uppercase()
}
#[test]
fn test_trim_bad_chars() {
assert_eq!("abc", trim_right("abc----^"))
}
#[test]
fn test_trim_bad_chars_when_none_are_bad() {
assert_eq!("abc", trim_right("abc"))
}
#[test]
fn test_is_not_alphanumeric_on_is_alphanumeric() {
assert!(!is_not_alphanumeric('a'))
}
#[test]
fn test_is_not_alphanumeric_on_is_not_alphanumeric() {
assert!(is_not_alphanumeric('_'))
}
#[test]
fn test_char_is_uppercase_when_it_is() {
assert_eq!(char_is_uppercase('A'), true)
}
#[test]
fn test_char_is_uppercase_when_it_is_not() {
assert_eq!(char_is_uppercase('a'), false)
}
#[test]
fn test_next_or_previous_char_is_lowercase_true() {
let chars: Vec<char> = "TestWWW".chars().collect();
assert_eq!(next_or_previous_char_is_lowercase(&chars, 3), true)
}
#[test]
fn test_next_or_previous_char_is_lowercase_false() {
let chars: Vec<char> = "TestWWW".chars().collect();
assert_eq!(next_or_previous_char_is_lowercase(&chars, 5), false)
}
#[test]
fn snake_like_with_separator_lowers() {
assert_eq!(
snake_like_with_separator("".to_owned(), "^", &'c', "lower"),
"^c".to_string()
)
}
#[test]
fn snake_like_with_separator_upper() {
assert_eq!(
snake_like_with_separator("".to_owned(), "^", &'c', "upper"),
"^C".to_string()
)
}
#[test]
fn snake_like_no_separator_lower() {
assert_eq!(
snake_like_no_separator("".to_owned(), &'C', "lower"),
"c".to_string()
)
}
#[test]
fn snake_like_no_separator_upper() {
assert_eq!(
snake_like_no_separator("".to_owned(), &'c', "upper"),
"C".to_string()
)
}
#[test]
fn requires_separator_upper_not_first_wrap_is_safe_current_upper() {
let chars: Vec<char> = "test".chars().collect();
assert_eq!(requires_separator('C', 2, false, &chars), true)
}
#[test]
fn requires_separator_upper_not_first_wrap_is_safe_current_lower() {
let chars: Vec<char> = "test".chars().collect();
assert_eq!(requires_separator('c', 2, false, &chars), false)
}
#[test]
fn requires_separator_upper_first_wrap_is_safe_current_upper() {
let chars: Vec<char> = "Test".chars().collect();
assert_eq!(requires_separator('T', 0, true, &chars), false)
}
#[test]
fn requires_separator_upper_first_wrap_is_safe_current_lower() {
let chars: Vec<char> = "Test".chars().collect();
assert_eq!(requires_separator('t', 0, true, &chars), false)
}
#[test]
fn requires_separator_upper_first_wrap_is_safe_current_lower_next_is_too() {
let chars: Vec<char> = "test".chars().collect();
assert_eq!(requires_separator('t', 0, true, &chars), false)
}
#[test]
fn test_char_is_separator_dash() {
assert_eq!(char_is_separator(&'-'), true)
}
#[test]
fn test_char_is_separator_underscore() {
assert_eq!(char_is_separator(&'_'), true)
}
#[test]
fn test_char_is_separator_space() {
assert_eq!(char_is_separator(&' '), true)
}
#[test]
fn test_char_is_separator_when_not() {
assert_eq!(char_is_separator(&'A'), false)
}
#[test]
fn test_last_char_lower_current_is_upper_or_new_word_with_new_word() {
assert_eq!(
last_char_lower_current_is_upper_or_new_word(true, ' ', '-'),
true
)
}
#[test]
fn test_last_char_lower_current_is_upper_or_new_word_last_char_space() {
assert_eq!(
last_char_lower_current_is_upper_or_new_word(false, ' ', '-'),
false
)
}
#[test]
fn test_last_char_lower_current_is_upper_or_new_word_last_char_lower_current_upper() {
assert_eq!(
last_char_lower_current_is_upper_or_new_word(false, 'a', 'A'),
true
)
}
#[test]
fn test_last_char_lower_current_is_upper_or_new_word_last_char_upper_current_upper() {
assert_eq!(
last_char_lower_current_is_upper_or_new_word(false, 'A', 'A'),
false
)
}
#[test]
fn test_last_char_lower_current_is_upper_or_new_word_last_char_upper_current_lower() {
assert_eq!(
last_char_lower_current_is_upper_or_new_word(false, 'A', 'a'),
false
)
}
#[test]
fn test_first_word_or_not_inverted_with_first_word() {
assert_eq!(first_word_or_not_inverted(true, false), true)
}
#[test]
fn test_first_word_or_not_inverted_not_first_word_not_inverted() {
assert_eq!(first_word_or_not_inverted(false, false), true)
}
#[test]
fn test_first_word_or_not_inverted_not_first_word_is_inverted() {
assert_eq!(first_word_or_not_inverted(false, true), false)
}
#[test]
fn test_not_first_word_and_has_separator_is_first_and_not_separator() {
assert_eq!(not_first_word_and_has_separator(true, false), false)
}
#[test]
fn test_not_first_word_and_has_separator_not_first_and_not_separator() {
assert_eq!(not_first_word_and_has_separator(false, false), false)
}
#[test]
fn test_not_first_word_and_has_separator_not_first_and_has_separator() {
assert_eq!(not_first_word_and_has_separator(false, true), true)
}
+207
View File
@@ -0,0 +1,207 @@
use crate::case::*;
/// Converts a `&str` to pascalCase `String`
///
/// ```
/// use cruet::case::to_pascal_case;
///
/// assert_eq!(to_pascal_case("fooBar"), "FooBar");
/// assert_eq!(to_pascal_case("FOO_BAR"), "FooBar");
/// assert_eq!(to_pascal_case("Foo Bar"), "FooBar");
/// assert_eq!(to_pascal_case("foo_bar"), "FooBar");
/// assert_eq!(to_pascal_case("Foo bar"), "FooBar");
/// assert_eq!(to_pascal_case("foo-bar"), "FooBar");
/// assert_eq!(to_pascal_case("FooBar"), "FooBar");
/// assert_eq!(to_pascal_case("FooBar3"), "FooBar3");
/// ```
pub fn to_pascal_case(non_pascalized_string: &str) -> String {
let options = CamelOptions {
new_word: true,
last_char: ' ',
first_word: false,
injectable_char: ' ',
has_separator: false,
inverted: false,
concat_num: true,
};
to_case_camel_like(non_pascalized_string, options)
}
/// Determines if a `&str` is pascalCase bool``
///
/// ```
/// use cruet::case::is_pascal_case;
///
/// assert!(is_pascal_case("Foo"));
/// assert!(is_pascal_case("FooBarIsAReallyReallyLongString"));
/// assert!(is_pascal_case("FooBarIsAReallyReally3LongString"));
/// assert!(is_pascal_case("FooBarIsAReallyReallyLongString"));
///
/// assert!(!is_pascal_case("foo"));
/// assert!(!is_pascal_case("foo-bar-string-that-is-really-really-long"));
/// assert!(!is_pascal_case("FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG"));
/// assert!(!is_pascal_case("foo_bar_string_that_is_really_really_long"));
/// assert!(!is_pascal_case("Foo bar string that is really really long"));
/// assert!(!is_pascal_case("Foo Bar Is A Really Really Long String"));
/// ```
pub fn is_pascal_case(test_string: &str) -> bool {
to_pascal_case(test_string) == test_string
}
#[cfg(test)]
mod tests {
use super::{is_pascal_case, to_pascal_case};
#[test]
fn from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn from_case_with_loads_of_space() {
let convertible_string: String = "foo bar".to_owned();
let expected: String = "FooBar".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn a_name_with_a_dot() {
let convertible_string: String = "Robert C. Martin".to_owned();
let expected: String = "RobertCMartin".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn random_text_with_bad_chars() {
let convertible_string: String = "Random text with *(bad) chars".to_owned();
let expected: String = "RandomTextWithBadChars".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn trailing_bad_chars() {
let convertible_string: String = "trailing bad_chars*(()())".to_owned();
let expected: String = "TrailingBadChars".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn leading_bad_chars() {
let convertible_string: String = "-!#$%leading bad chars".to_owned();
let expected: String = "LeadingBadChars".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn wrapped_in_bad_chars() {
let convertible_string: String =
"-!#$%wrapped in bad chars&*^*&(&*^&(<><?>><?><>))".to_owned();
let expected: String = "WrappedInBadChars".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn has_a_sign() {
let convertible_string: String = "has a + sign".to_owned();
let expected: String = "HasASign".to_owned();
assert_eq!(to_pascal_case(&convertible_string), expected)
}
#[test]
fn is_correct_from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
assert_eq!(is_pascal_case(&convertible_string), false)
}
#[test]
fn is_correct_from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
assert_eq!(is_pascal_case(&convertible_string), true)
}
#[test]
fn is_correct_from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
assert_eq!(is_pascal_case(&convertible_string), false)
}
#[test]
fn is_correct_from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
assert_eq!(is_pascal_case(&convertible_string), false)
}
#[test]
fn is_correct_from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
assert_eq!(is_pascal_case(&convertible_string), false)
}
#[test]
fn is_correct_from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
assert_eq!(is_pascal_case(&convertible_string), false)
}
#[test]
fn is_correct_from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
assert_eq!(is_pascal_case(&convertible_string), false)
}
#[test]
fn is_correct_from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
assert_eq!(is_pascal_case(&convertible_string), false)
}
}
+157
View File
@@ -0,0 +1,157 @@
use crate::case::*;
/// Converts a `&str` to `SCREAMING_SNAKE_CASE` `String`
///
/// ```
/// use cruet::case::to_screaming_snake_case;
///
/// assert_eq!(to_screaming_snake_case("foo_bar"), "FOO_BAR");
/// assert_eq!(to_screaming_snake_case("HTTP Foo bar"), "HTTP_FOO_BAR");
/// assert_eq!(to_screaming_snake_case("Foo bar"), "FOO_BAR");
/// assert_eq!(to_screaming_snake_case("Foo Bar"), "FOO_BAR");
/// assert_eq!(to_screaming_snake_case("FooBar"), "FOO_BAR");
/// assert_eq!(to_screaming_snake_case("fooBar"), "FOO_BAR");
/// assert_eq!(to_screaming_snake_case("fooBar3"), "FOO_BAR_3");
/// ```
pub fn to_screaming_snake_case(non_snake_case_string: &str) -> String {
to_case_snake_like(non_snake_case_string, "_", "upper")
}
/// Determines of a `&str` is `SCREAMING_SNAKE_CASE`
///
/// ```
/// use cruet::case::is_screaming_snake_case;
///
/// assert!(is_screaming_snake_case(
/// "FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG"
/// ));
/// assert!(is_screaming_snake_case(
/// "FOO_BAR1_STRING_THAT_IS_REALLY_REALLY_LONG"
/// ));
/// assert!(is_screaming_snake_case(
/// "FOO_BAR_1_STRING_THAT_IS_REALLY_REALLY_LONG"
/// ));
///
/// assert!(!is_screaming_snake_case(
/// "Foo bar string that is really really long"
/// ));
/// assert!(!is_screaming_snake_case(
/// "foo-bar-string-that-is-really-really-long"
/// ));
/// assert!(!is_screaming_snake_case("FooBarIsAReallyReallyLongString"));
/// assert!(!is_screaming_snake_case(
/// "Foo Bar Is A Really Really Long String"
/// ));
/// assert!(!is_screaming_snake_case("fooBarIsAReallyReallyLongString"));
/// ```
pub fn is_screaming_snake_case(test_string: &str) -> bool {
test_string == to_screaming_snake_case(test_string)
}
#[cfg(test)]
mod tests {
use super::{is_screaming_snake_case, to_screaming_snake_case};
#[test]
fn from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
let expected: String = "FOO_BAR".to_owned();
assert_eq!(to_screaming_snake_case(&convertible_string), expected)
}
#[test]
fn from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
let expected: String = "FOO_BAR".to_owned();
assert_eq!(to_screaming_snake_case(&convertible_string), expected)
}
#[test]
fn from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
let expected: String = "FOO_BAR".to_owned();
assert_eq!(to_screaming_snake_case(&convertible_string), expected)
}
#[test]
fn from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
let expected: String = "FOO_BAR".to_owned();
assert_eq!(to_screaming_snake_case(&convertible_string), expected)
}
#[test]
fn from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
let expected: String = "FOO_BAR".to_owned();
assert_eq!(to_screaming_snake_case(&convertible_string), expected)
}
#[test]
fn from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
let expected: String = "FOO_BAR".to_owned();
assert_eq!(to_screaming_snake_case(&convertible_string), expected)
}
#[test]
fn from_screaming_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
let expected: String = "FOO_BAR".to_owned();
assert_eq!(to_screaming_snake_case(&convertible_string), expected)
}
#[test]
fn from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
let expected: String = "FOO_BAR".to_owned();
assert_eq!(to_screaming_snake_case(&convertible_string), expected)
}
#[test]
fn is_correct_from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
assert_eq!(is_screaming_snake_case(&convertible_string), false)
}
#[test]
fn is_correct_from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
assert_eq!(is_screaming_snake_case(&convertible_string), false)
}
#[test]
fn is_correct_from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
assert_eq!(is_screaming_snake_case(&convertible_string), false)
}
#[test]
fn is_correct_from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
assert_eq!(is_screaming_snake_case(&convertible_string), false)
}
#[test]
fn is_correct_from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
assert_eq!(is_screaming_snake_case(&convertible_string), false)
}
#[test]
fn is_correct_from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
assert_eq!(is_screaming_snake_case(&convertible_string), false)
}
#[test]
fn is_correct_from_screaming_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
assert_eq!(is_screaming_snake_case(&convertible_string), true)
}
#[test]
fn is_correct_from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
assert_eq!(is_screaming_snake_case(&convertible_string), false)
}
}
+211
View File
@@ -0,0 +1,211 @@
use crate::case::*;
/// Converts a `&str` to `Sentence case` `String`
///
/// ```
/// use cruet::case::to_sentence_case;
///
/// assert_eq!(to_sentence_case("Foo bar"), "Foo bar");
/// assert_eq!(to_sentence_case("FooBar"), "Foo bar");
/// assert_eq!(to_sentence_case("fooBar"), "Foo bar");
/// assert_eq!(to_sentence_case("FOO_BAR"), "Foo bar");
/// assert_eq!(to_sentence_case("foo_bar"), "Foo bar");
/// assert_eq!(to_sentence_case("foo-bar"), "Foo bar");
/// ```
pub fn to_sentence_case(non_sentence_case_string: &str) -> String {
let options = CamelOptions {
new_word: true,
last_char: ' ',
first_word: true,
injectable_char: ' ',
has_separator: true,
inverted: true,
concat_num: false,
};
to_case_camel_like(non_sentence_case_string, options)
}
/// Determines of a `&str` is `Sentence case`
///
/// ```
/// use cruet::case::is_sentence_case;
///
/// assert!(is_sentence_case("Foo"));
/// assert!(is_sentence_case(
/// "Foo bar string that is really really long"
/// ));
///
/// assert!(!is_sentence_case(
/// "foo-bar-string-that-is-really-really-long"
/// ));
/// assert!(!is_sentence_case("FooBarIsAReallyReallyLongString"));
/// assert!(!is_sentence_case("fooBarIsAReallyReallyLongString"));
/// assert!(!is_sentence_case("Foo Bar Is A Really Really Long String"));
/// assert!(!is_sentence_case(
/// "FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG"
/// ));
/// assert!(!is_sentence_case(
/// "foo_bar_string_that_is_really_really_long"
/// ));
/// assert!(!is_sentence_case("foo"));
/// ```
pub fn is_sentence_case(test_string: &str) -> bool {
test_string == to_sentence_case(test_string)
}
#[cfg(test)]
mod tests {
use super::{is_sentence_case, to_sentence_case};
#[test]
fn from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
let expected: String = "Foo bar".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
let expected: String = "Foo bar".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
let expected: String = "Foo bar".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
let expected: String = "Foo bar".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
let expected: String = "Foo bar".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
let expected: String = "Foo bar".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
let expected: String = "Foo bar".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
let expected: String = "Foo bar".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn from_case_with_loads_of_space() {
let convertible_string: String = "foo bar".to_owned();
let expected: String = "Foo bar".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn a_name_with_a_dot() {
let convertible_string: String = "Robert C. Martin".to_owned();
let expected: String = "Robert c martin".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn random_text_with_bad_chars() {
let convertible_string: String = "Random text with *(bad) chars".to_owned();
let expected: String = "Random text with bad chars".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn trailing_bad_chars() {
let convertible_string: String = "trailing bad_chars*(()())".to_owned();
let expected: String = "Trailing bad chars".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn leading_bad_chars() {
let convertible_string: String = "-!#$%leading bad chars".to_owned();
let expected: String = "Leading bad chars".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn wrapped_in_bad_chars() {
let convertible_string: String =
"-!#$%wrapped in bad chars&*^*&(&*^&(<><?>><?><>))".to_owned();
let expected: String = "Wrapped in bad chars".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn has_a_sign() {
let convertible_string: String = "has a + sign".to_owned();
let expected: String = "Has a sign".to_owned();
assert_eq!(to_sentence_case(&convertible_string), expected)
}
#[test]
fn is_correct_from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
assert_eq!(is_sentence_case(&convertible_string), false)
}
#[test]
fn is_correct_from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
assert_eq!(is_sentence_case(&convertible_string), false)
}
#[test]
fn is_correct_from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
assert_eq!(is_sentence_case(&convertible_string), false)
}
#[test]
fn is_correct_from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
assert_eq!(is_sentence_case(&convertible_string), true)
}
#[test]
fn is_correct_from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
assert_eq!(is_sentence_case(&convertible_string), false)
}
#[test]
fn is_correct_from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
assert_eq!(is_sentence_case(&convertible_string), false)
}
#[test]
fn is_correct_from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
assert_eq!(is_sentence_case(&convertible_string), false)
}
#[test]
fn is_correct_from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
assert_eq!(is_sentence_case(&convertible_string), false)
}
}
+198
View File
@@ -0,0 +1,198 @@
use crate::case::*;
/// Converts a `&str` to `snake_case` `String`
///
/// ```
/// use cruet::case::to_snake_case;
///
/// assert_eq!(to_snake_case("foo_bar"), "foo_bar");
/// assert_eq!(to_snake_case("HTTP Foo bar"), "http_foo_bar");
/// assert_eq!(to_snake_case("HTTPFooBar"), "http_foo_bar");
/// assert_eq!(to_snake_case("Foo bar"), "foo_bar");
/// assert_eq!(to_snake_case("Foo Bar"), "foo_bar");
/// assert_eq!(to_snake_case("FooBar"), "foo_bar");
/// assert_eq!(to_snake_case("FOO_BAR"), "foo_bar");
/// assert_eq!(to_snake_case("fooBar"), "foo_bar");
/// assert_eq!(to_snake_case("fooBar3"), "foo_bar_3");
/// ```
pub fn to_snake_case(non_snake_case_string: &str) -> String {
to_case_snake_like(non_snake_case_string, "_", "lower")
}
/// Determines of a `&str` is `snake_case`
///
/// ```
/// use cruet::case::is_snake_case;
///
/// assert!(is_snake_case("foo_bar_string_that_is_really_really_long"));
/// assert!(is_snake_case("foo_bar_1_string_that_is_really_really_long"));
///
/// assert!(!is_snake_case("foo_bar1_string_that_is_really_really_long"));
/// assert!(!is_snake_case("Foo bar string that is really really long"));
/// assert!(!is_snake_case("foo-bar-string-that-is-really-really-long"));
/// assert!(!is_snake_case("FooBarIsAReallyReallyLongString"));
/// assert!(!is_snake_case("Foo Bar Is A Really Really Long String"));
/// assert!(!is_snake_case("FOO_BAR_IS_A_REALLY_REALLY_LONG_STRING"));
/// assert!(!is_snake_case("fooBarIsAReallyReallyLongString"));
/// ```
pub fn is_snake_case(test_string: &str) -> bool {
test_string == to_snake_case(test_string)
}
#[cfg(test)]
mod tests {
use super::{is_snake_case, to_snake_case};
#[test]
fn from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
let expected: String = "foo_bar".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
let expected: String = "foo_bar".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
let expected: String = "foo_bar".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
let expected: String = "foo_bar".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
let expected: String = "foo_bar".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
let expected: String = "foo_bar".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
let expected: String = "foo_bar".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
let expected: String = "foo_bar".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn from_case_with_loads_of_space() {
let convertible_string: String = "foo bar".to_owned();
let expected: String = "foo_bar".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn a_name_with_a_dot() {
let convertible_string: String = "Robert C. Martin".to_owned();
let expected: String = "robert_c_martin".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn random_text_with_bad_chars() {
let convertible_string: String = "Random text with *(bad) chars".to_owned();
let expected: String = "random_text_with_bad_chars".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn trailing_bad_chars() {
let convertible_string: String = "trailing bad_chars*(()())".to_owned();
let expected: String = "trailing_bad_chars".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn leading_bad_chars() {
let convertible_string: String = "-!#$%leading bad chars".to_owned();
let expected: String = "leading_bad_chars".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn wrapped_in_bad_chars() {
let convertible_string: String =
"-!#$%wrapped in bad chars&*^*&(&*^&(<><?>><?><>))".to_owned();
let expected: String = "wrapped_in_bad_chars".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn has_a_sign() {
let convertible_string: String = "has a + sign".to_owned();
let expected: String = "has_a_sign".to_owned();
assert_eq!(to_snake_case(&convertible_string), expected)
}
#[test]
fn is_correct_from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
assert_eq!(is_snake_case(&convertible_string), false)
}
#[test]
fn is_correct_from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
assert_eq!(is_snake_case(&convertible_string), false)
}
#[test]
fn is_correct_from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
assert_eq!(is_snake_case(&convertible_string), false)
}
#[test]
fn is_correct_from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
assert_eq!(is_snake_case(&convertible_string), false)
}
#[test]
fn is_correct_from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
assert_eq!(is_snake_case(&convertible_string), false)
}
#[test]
fn is_correct_from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
assert_eq!(is_snake_case(&convertible_string), false)
}
#[test]
fn is_correct_from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
assert_eq!(is_snake_case(&convertible_string), false)
}
#[test]
fn is_correct_from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
assert_eq!(is_snake_case(&convertible_string), true)
}
}
+160
View File
@@ -0,0 +1,160 @@
use crate::case::*;
use crate::string::pluralize::to_plural;
/// Converts a `&str` to `table-case` `String`
///
/// ```
/// use cruet::case::table::to_table_case;
///
/// assert_eq!(to_table_case("foo-bar"), "foo_bars");
/// assert_eq!(to_table_case("FOO_BAR"), "foo_bars");
/// assert_eq!(to_table_case("foo_bar"), "foo_bars");
/// assert_eq!(to_table_case("Foo Bar"), "foo_bars");
/// assert_eq!(to_table_case("Foo bar"), "foo_bars");
/// assert_eq!(to_table_case("FooBar"), "foo_bars");
/// assert_eq!(to_table_case("fooBar"), "foo_bars");
/// ```
pub fn to_table_case(non_table_case_string: &str) -> String {
let snaked: String = to_case_snake_like(non_table_case_string, "_", "lower");
let split: (&str, &str) = snaked.split_at(snaked.rfind('_').unwrap_or(0));
format!("{}{}", split.0, to_plural(split.1))
}
/// Determines if a `&str` is `table-case`
///
/// ```
/// use cruet::case::table::is_table_case;
/// assert!(is_table_case("foo_bar_strings"));
/// assert!(!is_table_case("foo-bar-string-that-is-really-really-long"));
/// assert!(!is_table_case("FooBarIsAReallyReallyLongString"));
/// assert!(!is_table_case("fooBarIsAReallyReallyLongString"));
/// assert!(!is_table_case("FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG"));
/// assert!(!is_table_case("foo_bar_string_that_is_really_really_long"));
/// assert!(!is_table_case("Foo bar string that is really really long"));
/// assert!(!is_table_case("Foo Bar Is A Really Really Long String"));
/// ```
pub fn is_table_case(test_string: &str) -> bool {
to_table_case(test_string) == test_string
}
#[cfg(test)]
mod tests {
use super::{is_table_case, to_table_case};
#[test]
fn from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
let expected: String = "foo_bars".to_owned();
assert_eq!(to_table_case(&convertible_string), expected)
}
#[test]
fn from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
let expected: String = "foo_bars".to_owned();
assert_eq!(to_table_case(&convertible_string), expected)
}
#[test]
fn from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
let expected: String = "foo_bars".to_owned();
assert_eq!(to_table_case(&convertible_string), expected)
}
#[test]
fn from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
let expected: String = "foo_bars".to_owned();
assert_eq!(to_table_case(&convertible_string), expected)
}
#[test]
fn from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
let expected: String = "foo_bars".to_owned();
assert_eq!(to_table_case(&convertible_string), expected)
}
#[test]
fn from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
let expected: String = "foo_bars".to_owned();
assert_eq!(to_table_case(&convertible_string), expected)
}
#[test]
fn from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
let expected: String = "foo_bars".to_owned();
assert_eq!(to_table_case(&convertible_string), expected)
}
#[test]
fn from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
let expected: String = "foo_bars".to_owned();
assert_eq!(to_table_case(&convertible_string), expected)
}
#[test]
fn from_table_case() {
let convertible_string: String = "foo_bars".to_owned();
let expected: String = "foo_bars".to_owned();
assert_eq!(to_table_case(&convertible_string), expected)
}
#[test]
fn is_correct_from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
assert_eq!(is_table_case(&convertible_string), false)
}
#[test]
fn is_correct_from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
assert_eq!(is_table_case(&convertible_string), false)
}
#[test]
fn is_correct_from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
assert_eq!(is_table_case(&convertible_string), false)
}
#[test]
fn is_correct_from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
assert_eq!(is_table_case(&convertible_string), false)
}
#[test]
fn is_correct_from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
assert_eq!(is_table_case(&convertible_string), false)
}
#[test]
fn is_correct_from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
assert_eq!(is_table_case(&convertible_string), false)
}
#[test]
fn is_correct_from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
assert_eq!(is_table_case(&convertible_string), false)
}
#[test]
fn is_correct_from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
assert_eq!(is_table_case(&convertible_string), false)
}
#[test]
fn is_correct_from_table_case() {
let convertible_string: String = "foo_bars".to_owned();
assert_eq!(is_table_case(&convertible_string), true)
}
}
+202
View File
@@ -0,0 +1,202 @@
use crate::case::*;
/// Converts a `&str` to `Title Case` `String`
///
/// ```
/// use cruet::case::title::to_title_case;
///
/// assert!(to_title_case("Foo bar") == "Foo Bar");
/// assert!(to_title_case("FooBar") == "Foo Bar");
/// assert!(to_title_case("fooBar") == "Foo Bar");
/// assert!(to_title_case("FOO_BAR") == "Foo Bar");
/// assert!(to_title_case("foo_bar") == "Foo Bar");
/// assert!(to_title_case("foo-bar") == "Foo Bar");
/// ```
pub fn to_title_case(non_title_case_string: &str) -> String {
let options = CamelOptions {
new_word: true,
last_char: ' ',
first_word: true,
injectable_char: ' ',
has_separator: true,
inverted: false,
concat_num: false,
};
to_case_camel_like(non_title_case_string, options)
}
/// Determines if a `&str` is `Title Case`
///
/// ```
/// use cruet::case::title::is_title_case;
///
/// assert!(is_title_case("Foo Bar String That Is Really Really Long"));
/// assert!(!is_title_case("foo-bar-string-that-is-really-really-long"));
/// assert!(!is_title_case("FooBarIsAReallyReallyLongString"));
/// assert!(!is_title_case("fooBarIsAReallyReallyLongString"));
/// assert!(!is_title_case("FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG"));
/// assert!(!is_title_case("foo_bar_string_that_is_really_really_long"));
/// assert!(!is_title_case("Foo bar string that is really really long"));
/// assert!(!is_title_case("foo"));
/// ```
pub fn is_title_case(test_string: &str) -> bool {
test_string == to_title_case(test_string)
}
#[cfg(test)]
mod tests {
use super::{is_title_case, to_title_case};
#[test]
fn from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
let expected: String = "Foo Bar".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
let expected: String = "Foo Bar".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
let expected: String = "Foo Bar".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
let expected: String = "Foo Bar".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
let expected: String = "Foo Bar".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
let expected: String = "Foo Bar".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
let expected: String = "Foo Bar".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
let expected: String = "Foo Bar".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn from_case_with_loads_of_space() {
let convertible_string: String = "foo bar".to_owned();
let expected: String = "Foo Bar".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn a_name_with_a_dot() {
let convertible_string: String = "Robert C. Martin".to_owned();
let expected: String = "Robert C Martin".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn random_text_with_bad_chars() {
let convertible_string: String = "Random text with *(bad) chars".to_owned();
let expected: String = "Random Text With Bad Chars".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn trailing_bad_chars() {
let convertible_string: String = "trailing bad_chars*(()())".to_owned();
let expected: String = "Trailing Bad Chars".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn leading_bad_chars() {
let convertible_string: String = "-!#$%leading bad chars".to_owned();
let expected: String = "Leading Bad Chars".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn wrapped_in_bad_chars() {
let convertible_string: String =
"-!#$%wrapped in bad chars&*^*&(&*^&(<><?>><?><>))".to_owned();
let expected: String = "Wrapped In Bad Chars".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn has_a_sign() {
let convertible_string: String = "has a + sign".to_owned();
let expected: String = "Has A Sign".to_owned();
assert_eq!(to_title_case(&convertible_string), expected)
}
#[test]
fn is_correct_from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
assert_eq!(is_title_case(&convertible_string), false)
}
#[test]
fn is_correct_from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
assert_eq!(is_title_case(&convertible_string), false)
}
#[test]
fn is_correct_from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
assert_eq!(is_title_case(&convertible_string), false)
}
#[test]
fn is_correct_from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
assert_eq!(is_title_case(&convertible_string), false)
}
#[test]
fn is_correct_from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
assert_eq!(is_title_case(&convertible_string), true)
}
#[test]
fn is_correct_from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
assert_eq!(is_title_case(&convertible_string), false)
}
#[test]
fn is_correct_from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
assert_eq!(is_title_case(&convertible_string), false)
}
#[test]
fn is_correct_from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
assert_eq!(is_title_case(&convertible_string), false)
}
}
+202
View File
@@ -0,0 +1,202 @@
use crate::case::*;
/// Determines if a `&str` is `Train-Case`
///
/// ```
/// use cruet::case::train::is_train_case;
///
/// assert!(is_train_case("Foo-Bar-String-That-Is-Really-Really-Long"));
/// assert!(!is_train_case("foo-bar-string-that-is-really-really-long"));
/// assert!(!is_train_case("FooBarIsAReallyReallyLongString"));
/// assert!(!is_train_case("fooBarIsAReallyReallyLongString"));
/// assert!(!is_train_case("foo_bar_string_that_is_really_really_long"));
/// assert!(!is_train_case("Foo bar string that is really really long"));
/// assert!(!is_train_case("Foo Bar Is A Really Really Long String"));
/// ```
pub fn is_train_case(test_string: &str) -> bool {
test_string == to_train_case(test_string)
}
/// Converts a `&str` to `Train-Case` `String`
///
/// ```
/// use cruet::case::train::to_train_case;
///
/// assert!(to_train_case("foo-bar") == "Foo-Bar");
/// assert!(to_train_case("FOO_BAR") == "Foo-Bar");
/// assert!(to_train_case("foo_bar") == "Foo-Bar");
/// assert!(to_train_case("Foo Bar") == "Foo-Bar");
/// assert!(to_train_case("Foo-Bar") == "Foo-Bar");
/// assert!(to_train_case("FooBar") == "Foo-Bar");
/// assert!(to_train_case("fooBar") == "Foo-Bar");
/// ```
pub fn to_train_case(non_train_case_string: &str) -> String {
let options = CamelOptions {
new_word: true,
last_char: ' ',
first_word: true,
injectable_char: '-',
has_separator: true,
inverted: false,
concat_num: true,
};
to_case_camel_like(non_train_case_string, options)
}
#[cfg(test)]
mod tests {
use super::{is_train_case, to_train_case};
#[test]
fn from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
let expected: String = "Foo-Bar".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
let expected: String = "Foo-Bar".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
let expected: String = "Foo-Bar".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
let expected: String = "Foo-Bar".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
let expected: String = "Foo-Bar".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
let expected: String = "Foo-Bar".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
let expected: String = "Foo-Bar".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
let expected: String = "Foo-Bar".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn from_case_with_loads_of_space() {
let convertible_string: String = "foo bar".to_owned();
let expected: String = "Foo-Bar".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn a_name_with_a_dot() {
let convertible_string: String = "Robert C. Martin".to_owned();
let expected: String = "Robert-C-Martin".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn random_text_with_bad_chars() {
let convertible_string: String = "Random text with *(bad) chars".to_owned();
let expected: String = "Random-Text-With-Bad-Chars".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn trailing_bad_chars() {
let convertible_string: String = "trailing bad_chars*(()())".to_owned();
let expected: String = "Trailing-Bad-Chars".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn leading_bad_chars() {
let convertible_string: String = "-!#$%leading bad chars".to_owned();
let expected: String = "Leading-Bad-Chars".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn wrapped_in_bad_chars() {
let convertible_string: String =
"-!#$%wrapped in bad chars&*^*&(&*^&(<><?>><?><>))".to_owned();
let expected: String = "Wrapped-In-Bad-Chars".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn has_a_sign() {
let convertible_string: String = "has a + sign".to_owned();
let expected: String = "Has-A-Sign".to_owned();
assert_eq!(to_train_case(&convertible_string), expected)
}
#[test]
fn is_correct_from_camel_case() {
let convertible_string: String = "fooBar".to_owned();
assert_eq!(is_train_case(&convertible_string), false)
}
#[test]
fn is_correct_from_pascal_case() {
let convertible_string: String = "FooBar".to_owned();
assert_eq!(is_train_case(&convertible_string), false)
}
#[test]
fn is_correct_from_kebab_case() {
let convertible_string: String = "foo-bar".to_owned();
assert_eq!(is_train_case(&convertible_string), false)
}
#[test]
fn is_correct_from_sentence_case() {
let convertible_string: String = "Foo bar".to_owned();
assert_eq!(is_train_case(&convertible_string), false)
}
#[test]
fn is_correct_from_title_case() {
let convertible_string: String = "Foo Bar".to_owned();
assert_eq!(is_train_case(&convertible_string), false)
}
#[test]
fn is_correct_from_train_case() {
let convertible_string: String = "Foo-Bar".to_owned();
assert_eq!(is_train_case(&convertible_string), true)
}
#[test]
fn is_correct_from_screaming_snake_case() {
let convertible_string: String = "FOO_BAR".to_owned();
assert_eq!(is_train_case(&convertible_string), false)
}
#[test]
fn is_correct_from_snake_case() {
let convertible_string: String = "foo_bar".to_owned();
assert_eq!(is_train_case(&convertible_string), false)
}
}
+206
View File
@@ -0,0 +1,206 @@
#![deny(unused_variables, missing_docs, unsafe_code, unused_extern_crates)]
//! Adds String based inflections for Rust. Snake, kebab, train, camel,
//! sentence, class, and title cases as well as ordinalize,
//! deordinalize, demodulize, deconstantize, and foreign key are supported as
//! both traits and pure functions acting on String types.
//! ```rust
//! use cruet::Inflector;
//! let camel_case_string: String = "some_string".to_camel_case();
//! let is_camel_cased: bool = camel_case_string.is_camel_case();
//! assert!(is_camel_cased == true);
//! ```
/// Provides case inflections
/// - Camel case
/// - Class case
/// - Kebab case
/// - Train case
/// - Screaming snake case
/// - Table case
/// - Sentence case
/// - Snake case
/// - Pascal case
pub mod case;
/// Provides number inflections
/// - Ordinalize
/// - Deordinalize
pub mod number;
/// Provides string inflections
/// - Deconstantize
/// - Demodulize
/// - Pluralize
/// - Singularize
pub mod string;
/// Provides suffix inflections
/// - Foreign key
pub mod suffix;
pub use case::camel::{is_camel_case, to_camel_case};
pub use case::class::{is_class_case, to_class_case};
pub use case::kebab::{is_kebab_case, to_kebab_case};
pub use case::pascal::{is_pascal_case, to_pascal_case};
pub use case::screaming_snake::{is_screaming_snake_case, to_screaming_snake_case};
pub use case::sentence::{is_sentence_case, to_sentence_case};
pub use case::snake::{is_snake_case, to_snake_case};
pub use case::table::{is_table_case, to_table_case};
pub use case::title::{is_title_case, to_title_case};
pub use case::train::{is_train_case, to_train_case};
pub use number::deordinalize::deordinalize;
pub use number::ordinalize::ordinalize;
pub use string::deconstantize::deconstantize;
pub use string::demodulize::demodulize;
pub use string::pluralize::to_plural;
pub use string::singularize::to_singular;
pub use suffix::foreign_key::{is_foreign_key, to_foreign_key};
#[allow(missing_docs)]
pub trait Inflector {
fn to_camel_case(&self) -> String;
fn is_camel_case(&self) -> bool;
fn to_pascal_case(&self) -> String;
fn is_pascal_case(&self) -> bool;
fn to_snake_case(&self) -> String;
fn is_snake_case(&self) -> bool;
fn to_screaming_snake_case(&self) -> String;
fn is_screaming_snake_case(&self) -> bool;
fn to_kebab_case(&self) -> String;
fn is_kebab_case(&self) -> bool;
fn to_train_case(&self) -> String;
fn is_train_case(&self) -> bool;
fn to_sentence_case(&self) -> String;
fn is_sentence_case(&self) -> bool;
fn to_title_case(&self) -> String;
fn is_title_case(&self) -> bool;
fn ordinalize(&self) -> String;
fn deordinalize(&self) -> String;
fn to_foreign_key(&self) -> String;
fn is_foreign_key(&self) -> bool;
fn demodulize(&self) -> String;
fn deconstantize(&self) -> String;
fn to_class_case(&self) -> String;
fn is_class_case(&self) -> bool;
fn to_table_case(&self) -> String;
fn is_table_case(&self) -> bool;
fn to_plural(&self) -> String;
fn to_singular(&self) -> String;
}
#[allow(missing_docs)]
pub trait InflectorNumbers {
fn ordinalize(&self) -> String;
}
macro_rules! define_implementations {
( $slf:ident; $($imp_trait:ident => $typ:ident), *) => {
$(
#[inline]
fn $imp_trait(&$slf) -> $typ {
$imp_trait($slf)
}
)*
}
}
macro_rules! define_number_implementations {
( $slf:ident; $($imp_trait:ident => $typ:ident), *) => {
$(
#[inline]
fn $imp_trait(&$slf) -> $typ {
$imp_trait(&$slf.to_string())
}
)*
}
}
macro_rules! define_gated_implementations {
( $slf:ident; $($imp_trait:ident => $typ:ident), *) => {
$(
#[inline]
fn $imp_trait(&$slf) -> $typ {
$imp_trait($slf)
}
)*
}
}
macro_rules! implement_string_for {
( $trt:ident; $($typ:ident), *) => {
$(
impl $trt for $typ {
define_implementations![self;
to_camel_case => String,
is_camel_case => bool,
to_pascal_case => String,
is_pascal_case => bool,
to_screaming_snake_case => String,
is_screaming_snake_case => bool,
to_snake_case => String,
is_snake_case => bool,
to_kebab_case => String,
is_kebab_case => bool,
to_train_case => String,
is_train_case => bool,
to_sentence_case => String,
is_sentence_case => bool,
to_title_case => String,
is_title_case => bool,
to_foreign_key => String,
is_foreign_key => bool,
ordinalize => String,
deordinalize => String
];
define_gated_implementations![self;
to_class_case => String,
is_class_case => bool,
to_table_case => String,
is_table_case => bool,
to_plural => String,
to_singular => String,
demodulize => String,
deconstantize => String
];
}
)*
}
}
macro_rules! implement_number_for {
( $trt:ident; $($typ:ident), *) => {
$(
impl $trt for $typ {
define_number_implementations![self;
ordinalize => String
];
}
)*
}
}
implement_string_for![
Inflector;
String, str
];
implement_number_for![
InflectorNumbers;
i8, i16, i32, i64, u8, u16, u32, u64, isize, usize, f32, f64
];
+34
View File
@@ -0,0 +1,34 @@
/// Deordinalizes a `&str`
///
/// ```
/// use cruet::number::deordinalize::deordinalize;
///
/// assert!(deordinalize("0.1") == "0.1");
/// assert!(deordinalize("-1st") == "-1");
/// assert!(deordinalize("0th") == "0");
/// assert!(deordinalize("1st") == "1");
/// assert!(deordinalize("2nd") == "2");
/// assert!(deordinalize("3rd") == "3");
/// assert!(deordinalize("9th") == "9");
/// assert!(deordinalize("12th") == "12");
/// assert!(deordinalize("12000th") == "12000");
/// assert!(deordinalize("12001th") == "12001");
/// assert!(deordinalize("12002nd") == "12002");
/// assert!(deordinalize("12003rd") == "12003");
/// assert!(deordinalize("12004th") == "12004");
/// assert!(deordinalize("3rd") == "3");
/// assert!(deordinalize("3rd") == "3");
/// assert!(deordinalize("") == "");
/// ```
pub fn deordinalize(non_ordinalized_string: &str) -> String {
if non_ordinalized_string.contains('.') {
non_ordinalized_string.to_owned()
} else {
non_ordinalized_string
.trim_end_matches("st")
.trim_end_matches("nd")
.trim_end_matches("rd")
.trim_end_matches("th")
.to_owned()
}
}
+11
View File
@@ -0,0 +1,11 @@
/// Provides deordinalization of a string.
///
/// Example string "1st" becomes "1"
pub mod deordinalize;
pub use deordinalize::deordinalize;
/// Provides ordinalization of a string.
///
/// Example string "1" becomes "1st"
pub mod ordinalize;
pub use ordinalize::ordinalize;
+143
View File
@@ -0,0 +1,143 @@
/// Ordinalizes a `&str`
///
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "";
/// let expected_string: String = "".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "a";
/// let expected_string: String = "a".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "0.1";
/// let expected_string: String = "0.1".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "-1";
/// let expected_string: String = "-1st".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "0";
/// let expected_string: String = "0th".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "1";
/// let expected_string: String = "1st".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "2";
/// let expected_string: String = "2nd".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "3";
/// let expected_string: String = "3rd".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "9";
/// let expected_string: String = "9th".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "12";
/// let expected_string: String = "12th".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "12000";
/// let expected_string: String = "12000th".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "12001";
/// let expected_string: String = "12001st".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "12002";
/// let expected_string: String = "12002nd".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "12003";
/// let expected_string: String = "12003rd".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::number::ordinalize::ordinalize;
/// let mock_string: &str = "12004";
/// let expected_string: String = "12004th".to_owned();
/// let asserted_string: String = ordinalize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
pub fn ordinalize(non_ordinalized_string: &str) -> String {
if non_ordinalized_string.is_empty() {
return String::new();
}
let chars: Vec<char> = non_ordinalized_string.chars().collect();
let last_number: char = chars[chars.len() - 1];
if is_ordinalizable(last_number) {
return non_ordinalized_string.to_owned();
}
if chars.len() > 1 {
if second_last_number_is_one(&chars) {
return format!("{}{}", non_ordinalized_string, "th");
} else if string_contains_decimal(non_ordinalized_string) {
return non_ordinalized_string.to_owned();
}
}
match last_number {
'1' => format!("{}{}", non_ordinalized_string, "st"),
'2' => format!("{}{}", non_ordinalized_string, "nd"),
'3' => format!("{}{}", non_ordinalized_string, "rd"),
_ => format!("{}{}", non_ordinalized_string, "th"),
}
}
fn is_ordinalizable(last_number: char) -> bool {
!last_number.is_numeric()
}
fn second_last_number_is_one(chars: &[char]) -> bool {
let second_last_number: char = chars[chars.len() - 2];
second_last_number == '1'
}
fn string_contains_decimal(non_ordinalized_string: &str) -> bool {
non_ordinalized_string.contains('.')
}
+204
View File
@@ -0,0 +1,204 @@
pub const UNCOUNTABLE_WORDS: [&str; 202] = [
"accommodation",
"adulthood",
"advertising",
"advice",
"aggression",
"aid",
"air",
"aircraft",
"alcohol",
"anger",
"applause",
"arithmetic",
"assistance",
"athletics",
"bacon",
"baggage",
"beef",
"biology",
"blood",
"botany",
"bread",
"butter",
"carbon",
"cardboard",
"cash",
"chalk",
"chaos",
"chess",
"crossroads",
"countryside",
"dancing",
"deer",
"dignity",
"dirt",
"dust",
"economics",
"education",
"electricity",
"engineering",
"enjoyment",
"envy",
"equipment",
"ethics",
"evidence",
"evolution",
"fame",
"fiction",
"flour",
"flu",
"food",
"fuel",
"fun",
"furniture",
"gallows",
"garbage",
"garlic",
"genetics",
"gold",
"golf",
"gossip",
"grammar",
"gratitude",
"grief",
"guilt",
"gymnastics",
"happiness",
"hardware",
"harm",
"hate",
"hatred",
"health",
"heat",
"help",
"homework",
"honesty",
"honey",
"hospitality",
"housework",
"humour",
"hunger",
"hydrogen",
"ice",
"importance",
"inflation",
"information",
"innocence",
"iron",
"irony",
"jam",
"jewelry",
"judo",
"karate",
"knowledge",
"lack",
"laughter",
"lava",
"leather",
"leisure",
"lightning",
"linguine",
"linguini",
"linguistics",
"literature",
"litter",
"livestock",
"logic",
"loneliness",
"luck",
"luggage",
"macaroni",
"machinery",
"magic",
"management",
"mankind",
"marble",
"mathematics",
"mayonnaise",
"measles",
"methane",
"milk",
"money",
"mud",
"music",
"mumps",
"nature",
"news",
"nitrogen",
"nonsense",
"nurture",
"nutrition",
"obedience",
"obesity",
"oxygen",
"pasta",
"patience",
"physics",
"poetry",
"pollution",
"poverty",
"pride",
"psychology",
"publicity",
"punctuation",
"quartz",
"racism",
"relaxation",
"reliability",
"research",
"respect",
"revenge",
"rice",
"rubbish",
"rum",
"safety",
"scenery",
"seafood",
"seaside",
"series",
"shame",
"sheep",
"shopping",
"sleep",
"smoke",
"smoking",
"snow",
"soap",
"software",
"soil",
"spaghetti",
"species",
"steam",
"stuff",
"stupidity",
"sunshine",
"symmetry",
"tennis",
"thirst",
"thunder",
"timber",
"traffic",
"transportation",
"trust",
"underwear",
"unemployment",
"unity",
"validity",
"veal",
"vegetation",
"vegetarianism",
"vengeance",
"violence",
"vitality",
"warmth",
"wealth",
"weather",
"welfare",
"wheat",
"wildlife",
"wisdom",
"yoga",
"zinc",
"zoology",
];
+47
View File
@@ -0,0 +1,47 @@
use crate::case::class::to_class_case;
/// Deconstantizes a `&str`
///
/// ```
/// use cruet::string::deconstantize::deconstantize;
/// let mock_string: &str = "Bar";
/// let expected_string: String = "".to_owned();
/// let asserted_string: String = deconstantize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::string::deconstantize::deconstantize;
/// let mock_string: &str = "::Bar";
/// let expected_string: String = "".to_owned();
/// let asserted_string: String = deconstantize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::string::deconstantize::deconstantize;
/// let mock_string: &str = "Foo::Bar";
/// let expected_string: String = "Foo".to_owned();
/// let asserted_string: String = deconstantize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::string::deconstantize::deconstantize;
/// let mock_string: &str = "Test::Foo::Bar";
/// let expected_string: String = "Foo".to_owned();
/// let asserted_string: String = deconstantize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
pub fn deconstantize(non_deconstantized_string: &str) -> String {
match non_deconstantized_string.rsplit_once("::") {
Some((prefix, _)) => {
if prefix.is_empty() {
"".to_owned()
} else {
match prefix.rsplit_once("::") {
Some((_, second_last)) => to_class_case(second_last),
None => to_class_case(prefix),
}
}
}
None => "".to_owned(),
}
}
+38
View File
@@ -0,0 +1,38 @@
use crate::case::class::to_class_case;
/// Demodulize a `&str`
///
/// ```
/// use cruet::string::demodulize::demodulize;
/// let mock_string: &str = "Bar";
/// let expected_string: String = "Bar".to_owned();
/// let asserted_string: String = demodulize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::string::demodulize::demodulize;
/// let mock_string: &str = "::Bar";
/// let expected_string: String = "Bar".to_owned();
/// let asserted_string: String = demodulize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::string::demodulize::demodulize;
/// let mock_string: &str = "Foo::Bar";
/// let expected_string: String = "Bar".to_owned();
/// let asserted_string: String = demodulize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::string::demodulize::demodulize;
/// let mock_string: &str = "Test::Foo::Bar";
/// let expected_string: String = "Bar".to_owned();
/// let asserted_string: String = demodulize(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
pub fn demodulize(non_demodulize_string: &str) -> String {
match non_demodulize_string.rsplit_once("::") {
Some((_, last)) => to_class_case(last),
None => non_demodulize_string.to_owned(),
}
}
+38
View File
@@ -0,0 +1,38 @@
macro_rules! special_cases {
($s:ident, $($singular:expr => $plural:expr),*) => {
match &$s[..] {
$(
$singular => {
return $plural.to_owned();
},
)*
_ => ()
}
}
}
/// Provides deconstantize a string.
///
/// Example string `Foo::Bar` becomes `Foo`
pub mod deconstantize;
pub use deconstantize::deconstantize;
/// Provides demodulize a string.
///
/// Example string `Foo::Bar` becomes `Bar`
pub mod demodulize;
pub use demodulize::demodulize;
/// Provides conversion to plural strings.
///
/// Example string `FooBar` -> `FooBars`
pub mod pluralize;
pub use pluralize::to_plural;
/// Provides conversion to singular strings.
///
/// Example string `FooBars` -> `FooBar`
pub mod singularize;
pub use singularize::to_singular;
mod constants;
+182
View File
@@ -0,0 +1,182 @@
use std::sync::LazyLock;
use regex::Regex;
use crate::string::constants::UNCOUNTABLE_WORDS;
static RULES: LazyLock<Vec<(Regex, &'static str)>> = LazyLock::new(|| {
vec![(r"(\w*)s$", "s"),
(r"(\w*([^aeiou]ese))$", ""),
(r"(\w*(ax|test))is$", "es"),
(r"(\w*(alias|[^aou]us|tlas|gas|ris))$", "es"),
(r"(\w*(e[mn]u))s?$", "s"),
(r"(\w*([^l]ias|[aeiou]las|[emjzr]as|[iu]am))$", ""),
(r"(\w*(alumn|syllab|octop|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat))(?:us|i)$", "i"),
(r"(\w*(alumn|alg|vertebr))(?:a|ae)$", "ae"),
(r"(\w*(seraph|cherub))(?:im)?$", "im"),
(r"(\w*(her|at|gr))o$", "oes"),
(r"(\w*(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|automat|quor))(?:a|um)$", "a"),
(r"(\w*(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat))(?:a|on)$", "a"),
(r"(\w*)sis$", "ses"),
(r"(\w*(kni|wi|li))fe$", "ves"),
(r"(\w*(ar|l|ea|eo|oa|hoo))f$", "ves"),
(r"(\w*([^aeiouy]|qu))y$", "ies"),
(r"(\w*([^ch][ieo][ln]))ey$", "ies"),
(r"(\w*(x|ch|ss|sh|zz)es)$", ""),
(r"(\w*(x|ch|ss|sh|zz))$", "es"),
(r"(\w*(matr|cod|mur|sil|vert|ind|append))(?:ix|ex)$", "ices"),
(r"(\w*(m|l))(?:ice|ouse)$", "ice"),
(r"(\w*(pe))(?:rson|ople)$", "ople"),
(r"(\w*(child))(?:ren)?$", "ren"),
(r"(\w*eaux)$", "")].into_iter().map(|(rule, replace)| {(Regex::new(rule).unwrap(), replace)}).collect()
});
/// Converts a `&str` to pluralized `String`
///
/// ```
/// use cruet::string::pluralize::to_plural;
/// let mock_string: &str = "foo_bar";
/// let expected_string: String = "foo_bars".to_owned();
/// let asserted_string: String = to_plural(mock_string);
/// assert_eq!(asserted_string, expected_string);
/// ```
/// ```
/// use cruet::string::pluralize::to_plural;
/// let mock_string: &str = "ox";
/// let expected_string: String = "oxen".to_owned();
/// let asserted_string: String = to_plural(mock_string);
/// assert_eq!(asserted_string, expected_string);
/// ```
/// ```
/// use cruet::string::pluralize::to_plural;
/// let mock_string: &str = "crate";
/// let expected_string: String = "crates".to_owned();
/// let asserted_string: String = to_plural(mock_string);
/// assert_eq!(asserted_string, expected_string);
/// ```
/// ```
/// use cruet::string::pluralize::to_plural;
/// let mock_string: &str = "boxes";
/// let expected_string: String = "boxes".to_owned();
/// let asserted_string: String = to_plural(mock_string);
/// assert_eq!(asserted_string, expected_string);
/// ```
/// ```
/// use cruet::string::pluralize::to_plural;
/// let mock_string: &str = "vengeance";
/// let expected_string: String = "vengeance".to_owned();
/// let asserted_string: String = to_plural(mock_string);
/// assert_eq!(asserted_string, expected_string);
/// ```
/// ```
/// use cruet::string::pluralize::to_plural;
/// let mock_string: &str = "yoga";
/// let expected_string: String = "yoga".to_owned();
/// let asserted_string: String = to_plural(mock_string);
/// assert_eq!(asserted_string, expected_string);
/// ```
/// ```
/// use cruet::string::pluralize::to_plural;
/// let mock_string: &str = "geometry";
/// let expected_string: String = "geometries".to_owned();
/// let asserted_string: String = to_plural(mock_string);
/// assert_eq!(asserted_string, expected_string);
/// ```
pub fn to_plural(non_plural_string: &str) -> String {
// Find the last separator (hyphen or underscore) to preserve prefixes
if let Some(pos) = non_plural_string.rfind(|c| c == '-' || c == '_') {
let prefix = &non_plural_string[..=pos]; // includes the separator
let last_word = &non_plural_string[pos + 1..];
if last_word.is_empty() {
return non_plural_string.to_owned();
}
return format!("{}{}", prefix, to_plural(last_word));
}
if UNCOUNTABLE_WORDS.contains(&non_plural_string) {
non_plural_string.to_owned()
} else {
special_cases![non_plural_string,
"ox" => "oxen",
"man" => "men",
"woman" => "women",
"die" => "dice",
"yes" => "yeses",
"foot" => "feet",
"eave" => "eaves",
"goose" => "geese",
"tooth" => "teeth",
"quiz" => "quizzes"
];
for &(ref rule, replace) in RULES.iter().rev() {
if let Some(c) = rule.captures(non_plural_string)
&& let Some(c) = c.get(1)
{
return format!("{}{}", c.as_str(), replace);
}
}
format!("{}s", non_plural_string)
}
}
#[cfg(test)]
mod tests {
macro_rules! as_item {
($i:item) => {
$i
};
}
macro_rules! make_tests{
($($singular:ident => $plural:ident); *) =>{
$(
as_item! {
#[test]
fn $singular(){
assert_eq!(
stringify!($plural),
super::to_plural(stringify!($singular))
);
}
}
)*
}
}
#[test]
fn boxes() {
assert_eq!("boxes", super::to_plural("box"));
}
make_tests! {
geometry => geometries;
ox => oxen;
woman => women;
test => tests;
axis => axes;
knife => knives;
agendum => agenda;
elf => elves;
zoology => zoology;
mice => mice;
people => people
}
#[test]
fn pluralize_kebab_case() {
assert_eq!(
"section-difficulties",
super::to_plural("section-difficulty")
);
}
#[test]
fn pluralize_snake_case_compound() {
assert_eq!(
"section_difficulties",
super::to_plural("section_difficulty")
);
}
}
+208
View File
@@ -0,0 +1,208 @@
use std::sync::LazyLock;
use regex::Regex;
use crate::string::constants::UNCOUNTABLE_WORDS;
/// Converts a `&str` to singularized `String`
///
/// ```
/// use cruet::string::singularize::to_singular;
/// let mock_string: &str = "foo_bars";
/// let expected_string: String = "foo_bar".to_owned();
/// let asserted_string: String = to_singular(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::string::singularize::to_singular;
/// let mock_string: &str = "oxen";
/// let expected_string: String = "ox".to_owned();
/// let asserted_string: String = to_singular(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::string::singularize::to_singular;
/// let mock_string: &str = "crates";
/// let expected_string: String = "crate".to_owned();
/// let asserted_string: String = to_singular(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::string::singularize::to_singular;
/// let mock_string: &str = "oxen";
/// let expected_string: String = "ox".to_owned();
/// let asserted_string: String = to_singular(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::string::singularize::to_singular;
/// let mock_string: &str = "boxes";
/// let expected_string: String = "box".to_owned();
/// let asserted_string: String = to_singular(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::string::singularize::to_singular;
/// let mock_string: &str = "vengeance";
/// let expected_string: String = "vengeance".to_owned();
/// let asserted_string: String = to_singular(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
/// ```
/// use cruet::string::singularize::to_singular;
/// let mock_string: &str = "yoga";
/// let expected_string: String = "yoga".to_owned();
/// let asserted_string: String = to_singular(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
pub fn to_singular(non_singular_string: &str) -> String {
// Find the last separator (hyphen or underscore) to preserve prefixes
if let Some(pos) = non_singular_string.rfind(|c| c == '-' || c == '_') {
let prefix = &non_singular_string[..=pos]; // includes the separator
let last_word = &non_singular_string[pos + 1..];
if last_word.is_empty() {
return non_singular_string.to_owned();
}
return format!("{}{}", prefix, to_singular(last_word));
}
if UNCOUNTABLE_WORDS.contains(&non_singular_string) {
non_singular_string.to_owned()
} else {
special_cases![non_singular_string,
"oxen" => "ox",
"boxes" => "box",
"men" => "man",
"women" => "woman",
"dice" => "die",
"yeses" => "yes",
"feet" => "foot",
"eaves" => "eave",
"geese" => "goose",
"teeth" => "tooth",
"quizzes" => "quiz"
];
for &(ref rule, replace) in RULES.iter().rev() {
if let Some(captures) = rule.captures(non_singular_string)
&& let Some(c) = captures.get(1)
{
return format!("{}{}", c.as_str(), replace);
}
}
non_singular_string.to_owned()
}
}
static RULES: LazyLock<Vec<(Regex, &'static str)>> = LazyLock::new(|| {
vec![
(r"(\w*)s$", ""),
(r"(\w*(ss))$", ""),
(r"(\w*(n))ews$", "ews"),
(r"(\w*(o))es$", ""),
(r"(\w*([ti]))a$", "um"),
(
r"(\w*((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he))(sis|ses)$",
"sis",
),
(r"(^analy)(sis|ses)$", "sis"),
(r"(\w*([^f]))ves$", "fe"),
(r"(\w*(hive))s$", ""),
(r"(\w*(tive))s$", ""),
(r"(\w*([lr]))ves$", "f"),
(r"(\w*([^aeiouy]|qu))ies$", "y"),
(r"(\w*(s))eries$", "eries"),
(r"(\w*(m))ovies$", "ovie"),
(r"(\w*(x|ch|ss|sh))es$", ""),
(r"(\w*(m|l))ice$", "ouse"),
(r"(\w*(bus))(es)?$", ""),
(r"(\w*(shoe))s$", ""),
(r"(\w*(cris|test))(is|es)$", "is"),
(r"^(a)x[ie]s$", "xis"),
(r"(\w*(octop|vir))(us|i)$", "us"),
(r"(\w*(alias|status))(es)?$", ""),
(r"^(ox)en", ""),
(r"(\w*(vert|ind))ices$", "ex"),
(r"(\w*(matr))ices$", "ix"),
(r"(\w*(quiz))zes$", ""),
(r"(\w*(database))s$", ""),
]
.into_iter()
.map(|(rule, replace)| (Regex::new(rule).unwrap(), replace))
.collect()
});
#[test]
fn singularize_ies_suffix() {
assert_eq!("reply", to_singular("replies"));
assert_eq!("lady", to_singular("ladies"));
assert_eq!("soliloquy", to_singular("soliloquies"));
}
#[test]
fn singularize_ss_suffix() {
assert_eq!("glass", to_singular("glass"));
assert_eq!("access", to_singular("access"));
assert_eq!("glass", to_singular("glasses"));
assert_eq!("witch", to_singular("witches"));
assert_eq!("dish", to_singular("dishes"));
}
#[test]
fn singularize_string_if_a_regex_will_match() {
assert_eq!("news", to_singular("news"));
assert_eq!("goodnews", to_singular("goodnews"));
assert_eq!("potato", to_singular("potatoes"));
assert_eq!("datum", to_singular("data"));
assert_eq!("analysis", to_singular("analyses"));
assert_eq!("codebasis", to_singular("codebases"));
assert_eq!("diagnosis", to_singular("diagnoses"));
assert_eq!("parenthesis", to_singular("parentheses"));
assert_eq!("prognosis", to_singular("prognoses"));
assert_eq!("synopsis", to_singular("synopses"));
assert_eq!("thesis", to_singular("theses"));
assert_eq!("knife", to_singular("knives"));
assert_eq!("archive", to_singular("archives"));
assert_eq!("motive", to_singular("motives"));
assert_eq!("half", to_singular("halves"));
assert_eq!("wolf", to_singular("wolves"));
assert_eq!("calf", to_singular("calves"));
assert_eq!("shelf", to_singular("shelves"));
assert_eq!("series", to_singular("series"));
assert_eq!("movie", to_singular("movies"));
assert_eq!("bus", to_singular("buses"));
assert_eq!("wish", to_singular("wishes"));
assert_eq!("pitch", to_singular("pitches"));
assert_eq!("box", to_singular("boxes"));
assert_eq!("mouse", to_singular("mice"));
assert_eq!("minibus", to_singular("minibuses"));
assert_eq!("snowshoe", to_singular("snowshoes"));
assert_eq!("crisis", to_singular("crises"));
assert_eq!("ovotestis", to_singular("ovotestes"));
assert_eq!("axis", to_singular("axes"));
assert_eq!("octopus", to_singular("octopi"));
assert_eq!("alias", to_singular("aliases"));
assert_eq!("ox", to_singular("oxen"));
assert_eq!("index", to_singular("indices"));
assert_eq!("matrix", to_singular("matrices"));
assert_eq!("quiz", to_singular("quizzes"));
assert_eq!("database", to_singular("databases"));
}
#[test]
fn singularize_string_returns_none_option_if_no_match() {
let expected_string: String = "bacon".to_owned();
let asserted_string: String = to_singular("bacon");
assert!(expected_string == asserted_string);
}
#[test]
fn singularize_kebab_case() {
assert_eq!("section-difficulty", to_singular("section-difficulties"));
}
#[test]
fn singularize_snake_case_compound() {
assert_eq!("section_difficulty", to_singular("section_difficulties"));
}
+51
View File
@@ -0,0 +1,51 @@
use crate::case::snake::to_snake_case;
/// Converts a `&str` to a `foreign_key`
///
/// ```
/// use cruet::suffix::foreign_key::to_foreign_key;
///
/// assert!(to_foreign_key("foo_bar") == "foo_bar_id");
/// assert!(to_foreign_key("Foo bar") == "foo_bar_id");
/// assert!(to_foreign_key("Foo Bar") == "foo_bar_id");
/// assert!(to_foreign_key("Foo::Bar") == "bar_id");
/// assert!(to_foreign_key("Test::Foo::Bar") == "bar_id");
/// assert!(to_foreign_key("FooBar") == "foo_bar_id");
/// assert!(to_foreign_key("fooBar") == "foo_bar_id");
/// assert!(to_foreign_key("fooBar3") == "foo_bar_3_id");
/// ```
pub fn to_foreign_key(non_foreign_key_string: &str) -> String {
if non_foreign_key_string.contains("::") {
let split_string: Vec<&str> = non_foreign_key_string.split("::").collect();
safe_convert(split_string[split_string.len() - 1])
} else {
safe_convert(non_foreign_key_string)
}
}
fn safe_convert(safe_string: &str) -> String {
let snake_cased: String = to_snake_case(safe_string);
if snake_cased.ends_with("_id") {
snake_cased
} else {
format!("{}{}", snake_cased, "_id")
}
}
/// Determines if a `&str` is a `foreign_key`
///
/// ```
/// use cruet::suffix::foreign_key::is_foreign_key;
///
/// assert!(!is_foreign_key("Foo bar string that is really really long"));
/// assert!(!is_foreign_key("foo-bar-string-that-is-really-really-long"));
/// assert!(!is_foreign_key("FooBarIsAReallyReallyLongString"));
/// assert!(!is_foreign_key("Foo Bar Is A Really Really Long String"));
/// assert!(!is_foreign_key("fooBarIsAReallyReallyLongString"));
/// assert!(!is_foreign_key("foo_bar_string_that_is_really_really_long"));
/// assert!(is_foreign_key(
/// "foo_bar_string_that_is_really_really_long_id"
/// ));
/// ```
pub fn is_foreign_key(test_string: &str) -> bool {
to_foreign_key(test_string) == test_string
}
+5
View File
@@ -0,0 +1,5 @@
/// Provides foreign key conversion for String.
///
/// Example string `foo` becomes `foo_id`
pub mod foreign_key;
pub use foreign_key::{is_foreign_key, to_foreign_key};