use serde_json::{Map, Value};
pub fn camel_to_snake_case(input: &str) -> String {
let mut snake: String = String::with_capacity(input.len() * 2);
let mut chars = input.chars().peekable();
let mut prev_char: Option<char> = None;
while let Some(c) = chars.next() {
if c.is_ascii_uppercase() {
if let Some(prev) = prev_char {
let prev_is_lower_or_digit: bool =
prev.is_ascii_lowercase() || prev.is_ascii_digit();
let next_is_lower: bool = chars
.peek()
.map(|next| next.is_ascii_lowercase())
.unwrap_or(false);
if prev_is_lower_or_digit || (prev.is_ascii_uppercase() && next_is_lower) {
snake.push('_');
}
}
snake.push(c.to_ascii_lowercase());
} else {
snake.push(c);
}
prev_char = Some(c);
}
snake
}
pub fn normalize_column_name(column: &str, force: bool) -> String {
if !force || column == "*" {
return column.to_string();
}
camel_to_snake_case(column)
}
pub fn normalize_columns_csv(columns: &str, force: bool) -> String {
if !force {
return columns.to_string();
}
columns
.split(',')
.map(str::trim)
.filter(|value| !value.is_empty())
.map(|value| normalize_column_name(value, true))
.collect::<Vec<_>>()
.join(",")
}
pub fn convert_value_keys(value: &Value, force: bool) -> Value {
if !force {
return value.clone();
}
match value {
Value::Object(map) => {
let mut normalized: Map<String, Value> = Map::new();
for (key, val) in map {
normalized.insert(camel_to_snake_case(key), convert_value_keys(val, true));
}
Value::Object(normalized)
}
Value::Array(items) => Value::Array(
items
.iter()
.map(|item| convert_value_keys(item, true))
.collect(),
),
other => other.clone(),
}
}
pub fn normalize_rows(rows: &[Value], force: bool) -> Vec<Value> {
if !force {
return rows.to_vec();
}
rows.iter()
.map(|row| convert_value_keys(row, true))
.collect()
}