pub trait StringCase {
#[must_use]
fn capitalize(&self) -> String;
#[must_use]
fn uncapitalize(&self) -> String;
#[must_use]
fn swap_case(&self) -> String;
}
impl StringCase for str {
fn capitalize(&self) -> String {
if self.is_empty() {
return String::new();
}
let mut chars = self.chars();
let first = chars.next().unwrap();
let mut result = String::with_capacity(self.len());
for c in first.to_uppercase() {
result.push(c);
}
result.push_str(chars.as_str());
result
}
fn uncapitalize(&self) -> String {
if self.is_empty() {
return String::new();
}
let mut chars = self.chars();
let first = chars.next().unwrap();
let mut result = String::with_capacity(self.len());
for c in first.to_lowercase() {
result.push(c);
}
result.push_str(chars.as_str());
result
}
fn swap_case(&self) -> String {
let mut result = String::with_capacity(self.len());
for c in self.chars() {
if c.is_uppercase() {
for lower in c.to_lowercase() {
result.push(lower);
}
} else if c.is_lowercase() {
for upper in c.to_uppercase() {
result.push(upper);
}
} else {
result.push(c);
}
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
mod capitalize {
use super::*;
#[test]
fn empty_string() {
assert_eq!("".capitalize(), "");
}
#[test]
fn single_lowercase() {
assert_eq!("c".capitalize(), "C");
}
#[test]
fn single_uppercase() {
assert_eq!("C".capitalize(), "C");
}
#[test]
fn lowercase_word() {
assert_eq!("cat".capitalize(), "Cat");
}
#[test]
fn mixed_case() {
assert_eq!("cAt".capitalize(), "CAt");
}
#[test]
fn already_capitalized() {
assert_eq!("Cat".capitalize(), "Cat");
}
#[test]
fn all_uppercase() {
assert_eq!("CAT".capitalize(), "CAT");
}
#[test]
fn non_letter_first_char() {
assert_eq!("'cat'".capitalize(), "'cat'");
}
#[test]
fn digit_first_char() {
assert_eq!("123abc".capitalize(), "123abc");
}
#[test]
fn space_first_char() {
assert_eq!(" cat".capitalize(), " cat");
}
#[test]
fn unicode_lowercase() {
assert_eq!("ßeta".capitalize(), "SSeta");
}
#[test]
fn unicode_title_case() {
let result = "\u{01C9}".capitalize();
assert_eq!(result, "\u{01C7}"); }
}
mod uncapitalize {
use super::*;
#[test]
fn empty_string() {
assert_eq!("".uncapitalize(), "");
}
#[test]
fn single_uppercase() {
assert_eq!("C".uncapitalize(), "c");
}
#[test]
fn single_lowercase() {
assert_eq!("c".uncapitalize(), "c");
}
#[test]
fn capitalized_word() {
assert_eq!("Cat".uncapitalize(), "cat");
}
#[test]
fn all_uppercase() {
assert_eq!("CAT".uncapitalize(), "cAT");
}
#[test]
fn already_lowercase() {
assert_eq!("cat".uncapitalize(), "cat");
}
#[test]
fn non_letter_first_char() {
assert_eq!("'Cat'".uncapitalize(), "'Cat'");
}
#[test]
fn digit_first_char() {
assert_eq!("123Abc".uncapitalize(), "123Abc");
}
#[test]
fn space_first_char() {
assert_eq!(" Cat".uncapitalize(), " Cat");
}
#[test]
fn unicode_uppercase() {
assert_eq!("İstanbul".uncapitalize(), "i\u{0307}stanbul");
}
}
mod swap_case {
use super::*;
#[test]
fn empty_string() {
assert_eq!("".swap_case(), "");
}
#[test]
fn all_lowercase() {
assert_eq!("abc".swap_case(), "ABC");
}
#[test]
fn all_uppercase() {
assert_eq!("ABC".swap_case(), "abc");
}
#[test]
fn mixed_case() {
assert_eq!("aBc".swap_case(), "AbC");
}
#[test]
fn sentence() {
assert_eq!("The dog has a BONE".swap_case(), "tHE DOG HAS A bone");
}
#[test]
fn with_digits() {
assert_eq!("a1B2c3".swap_case(), "A1b2C3");
}
#[test]
fn with_special_chars() {
assert_eq!("a-B_c".swap_case(), "A-b_C");
}
#[test]
fn unicode_title_case_unchanged() {
let result = "\u{01C8}".swap_case();
assert_eq!(result, "\u{01C8}"); }
#[test]
fn german_sharp_s() {
assert_eq!("ßeta".swap_case(), "SSETA");
}
#[test]
fn spaces_unchanged() {
assert_eq!("a b c".swap_case(), "A B C");
}
#[test]
fn single_lowercase() {
assert_eq!("a".swap_case(), "A");
}
#[test]
fn single_uppercase() {
assert_eq!("A".swap_case(), "a");
}
}
mod string_types {
use super::*;
#[test]
fn string_type() {
assert_eq!(String::from("hello").capitalize(), "Hello");
assert_eq!(String::from("Hello").uncapitalize(), "hello");
assert_eq!(String::from("HeLLo").swap_case(), "hEllO");
}
#[test]
fn string_ref() {
let s = String::from("hello");
assert_eq!(s.capitalize(), "Hello");
}
#[test]
fn boxed_str() {
let s: Box<str> = "hello".into();
assert_eq!(s.capitalize(), "Hello");
}
}
}