pub fn to_snake_case(s: &str) -> String {
let mut out = String::new();
transform(s, lowercase, push_underscore, &mut out);
return out;
}
pub fn to_upper_camel_case(s: &str) -> String {
let mut out = String::new();
transform(s, capitalize, no_boundary, &mut out);
return out;
}
pub fn to_screaming_snake_case(s: &str) -> String {
return to_snake_case(s).to_uppercase();
}
#[derive(Clone, Copy, PartialEq)]
enum WordMode {
Boundary,
Lowercase,
Uppercase,
}
fn transform(s: &str, with_word: fn(&str, &mut String), boundary: fn(&mut String), out: &mut String) {
let mut first_word = true;
let mut emit = |slice: &str, out: &mut String| {
if !first_word {
boundary(out);
}
first_word = false;
with_word(slice, out);
};
for word in s.split(|c: char| {
return !c.is_alphanumeric();
}) {
let mut char_indices = word.char_indices().peekable();
let mut init = 0;
let mut mode = WordMode::Boundary;
while let Some((i, c)) = char_indices.next() {
let Some(&(next_i, next)) = char_indices.peek() else {
emit(&word[init..], out);
break;
};
let next_mode = if c.is_lowercase() {
WordMode::Lowercase
} else if c.is_uppercase() {
WordMode::Uppercase
} else {
mode
};
if next_mode == WordMode::Lowercase && next.is_uppercase() {
emit(&word[init..next_i], out);
init = next_i;
mode = WordMode::Boundary;
} else if mode == WordMode::Uppercase && c.is_uppercase() && next.is_lowercase() {
emit(&word[init..i], out);
init = i;
mode = WordMode::Boundary;
} else {
mode = next_mode;
}
}
}
}
fn lowercase(s: &str, out: &mut String) {
let mut chars = s.chars().peekable();
while let Some(c) = chars.next() {
if c == 'Σ' && chars.peek().is_none() {
out.push('ς');
} else {
for lower in c.to_lowercase() {
out.push(lower);
}
}
}
}
fn capitalize(s: &str, out: &mut String) {
let mut char_indices = s.char_indices();
if let Some((_, c)) = char_indices.next() {
for upper in c.to_uppercase() {
out.push(upper);
}
if let Some((i, _)) = char_indices.next() {
lowercase(&s[i..], out);
}
}
}
fn push_underscore(out: &mut String) {
out.push('_');
}
fn no_boundary(_out: &mut String) {}