fn case_change(input: &str) -> usize {
if input.len() < 2 {
return input.len();
}
let mut chars = input.chars().enumerate();
let (_, initial_char) = chars.next().unwrap(); let initial_case = initial_char.is_lowercase();
for (i, c) in chars {
if c.is_lowercase() != initial_case {
return i;
}
}
input.len()
}
#[derive(Default)]
pub struct NameTransform<'a> {
pub words: Vec<&'a str>,
}
impl<'a> NameTransform<'a> {
pub fn from_camel_case(name: &str) -> NameTransform {
let mut words = Vec::new();
let mut remainder = name;
let mut starting_lowercase = name
.chars()
.next()
.map(|c| c.is_lowercase())
.unwrap_or_default();
while !remainder.is_empty() {
let mut next_boundary = case_change(remainder);
if starting_lowercase {
starting_lowercase = false; } else if next_boundary == remainder.len() {
} else if next_boundary > 1 {
next_boundary -= 1; } else if next_boundary == 1 {
next_boundary += case_change(&remainder[1..]); }
words.push(&remainder[0..next_boundary]);
remainder = &remainder[next_boundary..];
}
NameTransform { words }
}
pub fn to_camel_case(&self) -> String {
let mut snake_case = String::new();
for word in &self.words {
snake_case += &word[..1].to_uppercase();
snake_case += &word[1..];
}
snake_case
}
pub fn to_snake_case(&self) -> String {
let mut words_iter = self.words.iter();
let mut snake_case = String::new();
snake_case += &words_iter.next().unwrap_or(&"").to_lowercase();
for remaining_word in words_iter {
snake_case += &format!("_{}", remaining_word.to_lowercase());
}
snake_case
}
pub fn to_kebab_case(&self) -> String {
let mut words_iter = self.words.iter();
let mut snake_case = String::new();
snake_case += &words_iter.next().unwrap_or(&"").to_lowercase();
for remaining_word in words_iter {
snake_case += &format!("-{}", remaining_word.to_lowercase());
}
snake_case
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_camel_case_empty() {
assert_eq!(
NameTransform::from_camel_case("").words,
Vec::<String>::new()
);
}
#[test]
fn parse_camel_case_verbatim() {
assert_eq!(NameTransform::from_camel_case("Name").words, vec!["Name"]);
}
#[test]
fn parse_camel_case_simple() {
assert_eq!(
NameTransform::from_camel_case("NameTransform").words,
vec!["Name", "Transform"]
);
}
#[test]
fn parse_camel_case_acronym() {
assert_eq!(
NameTransform::from_camel_case("DNSResolver").words,
vec!["DNS", "Resolver"]
);
}
#[test]
fn parse_camel_case_acronym_at_end() {
assert_eq!(
NameTransform::from_camel_case("ResolveDNS").words,
vec!["Resolve", "DNS"]
);
}
#[test]
fn parse_camel_case_single_letter_caps() {
assert_eq!(
NameTransform::from_camel_case("RStudio").words,
vec!["R", "Studio"]
);
}
#[test]
fn parse_camel_case_starting_lower_case() {
assert_eq!(
NameTransform::from_camel_case("aRealVariable").words,
vec!["a", "Real", "Variable"]
);
}
#[test]
fn parse_camel_case_starting_lower_case_word() {
assert_eq!(
NameTransform::from_camel_case("anRc").words,
vec!["an", "Rc"]
);
}
#[test]
fn to_snake_case_empty() {
assert_eq!(NameTransform::default().to_snake_case(), "".to_owned());
}
#[test]
fn to_snake_case_simple() {
assert_eq!(
NameTransform::from_camel_case("Name").to_snake_case(),
"name".to_owned()
);
}
#[test]
fn to_snake_case_multiple_words() {
assert_eq!(
NameTransform::from_camel_case("NameTransform").to_snake_case(),
"name_transform".to_owned()
);
}
#[test]
fn to_kebab_case_empty() {
assert_eq!(NameTransform::default().to_kebab_case(), "".to_owned());
}
#[test]
fn to_kebab_case_simple() {
assert_eq!(
NameTransform::from_camel_case("Name").to_kebab_case(),
"name".to_owned()
);
}
#[test]
fn to_kebab_case_multiple_words() {
assert_eq!(
NameTransform::from_camel_case("NameTransform").to_kebab_case(),
"name-transform".to_owned()
);
}
#[test]
fn to_camel_case_empty() {
assert_eq!(NameTransform::default().to_camel_case(), "".to_owned());
}
#[test]
fn to_camel_case_simple() {
assert_eq!(
NameTransform::from_camel_case("Name").to_camel_case(),
"Name".to_owned()
);
}
#[test]
fn to_camel_case_multiple_words() {
assert_eq!(
NameTransform::from_camel_case("NameTransform").to_camel_case(),
"NameTransform".to_owned()
);
}
#[test]
fn to_camel_case_keep_acronym() {
assert_eq!(
NameTransform::from_camel_case("DNSResolver").to_camel_case(),
"DNSResolver".to_owned()
);
}
#[test]
fn to_camel_case_uppercase_first() {
assert_eq!(
NameTransform::from_camel_case("anRc").to_camel_case(),
"AnRc".to_owned()
);
}
}