#[must_use]
pub fn to_snake_case(s: &str) -> String {
if !s.chars().any(char::is_uppercase) {
return s.to_string();
}
let mut result = String::with_capacity(s.len() + 5);
let mut prev_was_upper = false;
let mut prev_was_lower = false;
for (i, c) in s.chars().enumerate() {
if c.is_uppercase() {
if i > 0 {
let next_is_lower = s.chars().nth(i + 1).is_some_and(char::is_lowercase);
if prev_was_lower || (prev_was_upper && next_is_lower) {
result.push('_');
}
}
result.push(c.to_ascii_lowercase());
prev_was_upper = true;
prev_was_lower = false;
} else {
result.push(c);
prev_was_upper = false;
prev_was_lower = c.is_lowercase();
}
}
result
}
#[must_use]
pub fn to_camel_case(s: &str) -> String {
if !s.contains('_') {
return s.to_string();
}
let mut result = String::with_capacity(s.len());
let mut capitalize_next = false;
for c in s.chars() {
if c == '_' {
capitalize_next = true;
} else if capitalize_next {
result.push(c.to_ascii_uppercase());
capitalize_next = false;
} else {
result.push(c);
}
}
result
}
#[must_use]
pub fn normalize_field_path(path: &str) -> String {
if !path.contains('.') {
return to_snake_case(path);
}
path.split('.').map(to_snake_case).collect::<Vec<_>>().join(".")
}