Convert Between camelCase and snake_case

Split identifiers on separators and case boundaries, then rebuild them as snake_case or camelCase without external crates.

RustCli Tooling

Code

snippet
fn split_words(input: &str) -> Vec<String> {
    let mut words = Vec::new();
    let mut current = String::new();
    let characters: Vec<char> = input.chars().collect();

    for (index, &character) in characters.iter().enumerate() {
        if character == '_' || character == '-' || character == ' ' {
            if !current.is_empty() {
                words.push(std::mem::take(&mut current));
            }
            continue;
        }
        let previous = if index > 0 { Some(characters[index - 1]) } else { None };
        let next = characters.get(index + 1).copied();
        let boundary = (character.is_uppercase()
            && previous.map_or(false, |value| value.is_lowercase() || value.is_ascii_digit()))
            || (character.is_uppercase()
                && previous.map_or(false, |value| value.is_uppercase())
                && next.map_or(false, |value| value.is_lowercase()));
        if boundary && !current.is_empty() {
            words.push(std::mem::take(&mut current));
        }
        current.push(character.to_ascii_lowercase());
    }
    if !current.is_empty() {
        words.push(current);
    }
    words
}

fn to_snake_case(input: &str) -> String {
    split_words(input).join("_")
}

fn to_camel_case(input: &str) -> String {
    let mut words = split_words(input);
    if words.is_empty() {
        return String::new();
    }
    let mut output = words.remove(0);
    for word in words {
        let mut characters = word.chars();
        if let Some(first) = characters.next() {
            output.push(first.to_ascii_uppercase());
            output.extend(characters);
        }
    }
    output
}

fn main() {
    for sample in ["HTTPServer", "user_profile_id", "OrderServiceConfig", "v2Beta"] {
        println!("{sample} -> {} / {}", to_snake_case(sample), to_camel_case(sample));
    }
    assert_eq!(to_snake_case("HTTPServer"), "http_server");
    assert_eq!(to_snake_case("user_profile_id"), "user_profile_id");
    assert_eq!(to_camel_case("order-service-config"), "orderServiceConfig");
    assert_eq!(to_camel_case("already_snake"), "alreadySnake");
}