use crate::e2e::escape::escape_r;
fn pascal_to_snake_case(s: &str) -> String {
let mut result = String::with_capacity(s.len() + 4);
for (i, ch) in s.chars().enumerate() {
if ch.is_uppercase() && i > 0 {
result.push('_');
}
for lc in ch.to_lowercase() {
result.push(lc);
}
}
result
}
pub(super) fn json_to_r_preserve_arrays(value: &serde_json::Value, lowercase_enum_values: bool) -> String {
match value {
serde_json::Value::Array(arr) => {
if arr.is_empty() {
"I(list())".to_string()
} else {
let items: Vec<String> = arr.iter().map(|v| json_to_r(v, lowercase_enum_values)).collect();
format!("I(c({}))", items.join(", "))
}
}
serde_json::Value::Object(map) => {
let entries: Vec<String> = map
.iter()
.map(|(k, v)| {
format!(
"\"{}\" = {}",
escape_r(k),
json_to_r_preserve_arrays(v, lowercase_enum_values)
)
})
.collect();
format!("list({})", entries.join(", "))
}
_ => json_to_r(value, lowercase_enum_values),
}
}
pub(super) fn json_to_r(value: &serde_json::Value, lowercase_enum_values: bool) -> String {
match value {
serde_json::Value::String(s) => {
let normalized = if lowercase_enum_values && s.chars().next().is_some_and(|c| c.is_uppercase()) {
pascal_to_snake_case(s)
} else {
s.clone()
};
format!("\"{}\"", escape_r(&normalized))
}
serde_json::Value::Bool(true) => "TRUE".to_string(),
serde_json::Value::Bool(false) => "FALSE".to_string(),
serde_json::Value::Number(n) => n.to_string(),
serde_json::Value::Null => "NULL".to_string(),
serde_json::Value::Array(arr) => {
let items: Vec<String> = arr.iter().map(|v| json_to_r(v, lowercase_enum_values)).collect();
format!("c({})", items.join(", "))
}
serde_json::Value::Object(map) => {
let entries: Vec<String> = map
.iter()
.map(|(k, v)| format!("\"{}\" = {}", escape_r(k), json_to_r(v, lowercase_enum_values)))
.collect();
format!("list({})", entries.join(", "))
}
}
}