use crate::parser::object_literal::parse_object_literal;
pub(super) fn rewrite_object_literal_args(sql: &str) -> Option<String> {
let mut result = String::with_capacity(sql.len());
let chars: Vec<char> = sql.chars().collect();
let mut i = 0;
let mut found = false;
let mut paren_depth: i32 = 0;
while i < chars.len() {
match chars[i] {
'(' => {
paren_depth += 1;
result.push('(');
i += 1;
}
')' => {
paren_depth = paren_depth.saturating_sub(1);
result.push(')');
i += 1;
}
'\'' => {
result.push('\'');
i += 1;
while i < chars.len() {
result.push(chars[i]);
if chars[i] == '\'' {
if i + 1 < chars.len() && chars[i + 1] == '\'' {
i += 1;
result.push(chars[i]);
} else {
break;
}
}
i += 1;
}
i += 1;
}
'{' if paren_depth > 0 => {
let remaining: String = chars[i..].iter().collect();
if let Some(Ok(fields)) = parse_object_literal(&remaining)
&& let Some(end) = find_matching_brace(&chars, i)
{
let json = value_map_to_json(&fields);
result.push('\'');
result.push_str(&json);
result.push('\'');
i = end + 1;
found = true;
continue;
}
result.push('{');
i += 1;
}
_ => {
result.push(chars[i]);
i += 1;
}
}
}
if found { Some(result) } else { None }
}
fn value_map_to_json(fields: &std::collections::HashMap<String, nodedb_types::Value>) -> String {
let mut parts = Vec::with_capacity(fields.len());
let mut entries: Vec<_> = fields.iter().collect();
entries.sort_by_key(|(k, _)| k.as_str());
for (key, val) in entries {
parts.push(format!("\"{}\":{}", key, value_to_json(val)));
}
format!("{{{}}}", parts.join(","))
}
fn value_to_json(value: &nodedb_types::Value) -> String {
match value {
nodedb_types::Value::String(s) => {
format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\""))
}
nodedb_types::Value::Integer(n) => n.to_string(),
nodedb_types::Value::Float(f) => {
if f.is_finite() {
format!("{f}")
} else {
"null".to_string()
}
}
nodedb_types::Value::Bool(b) => if *b { "true" } else { "false" }.to_string(),
nodedb_types::Value::Null => "null".to_string(),
nodedb_types::Value::Array(items) => {
let inner: Vec<String> = items.iter().map(value_to_json).collect();
format!("[{}]", inner.join(","))
}
nodedb_types::Value::Object(map) => value_map_to_json(map),
_ => format!("\"{}\"", format!("{value:?}").replace('"', "\\\"")),
}
}
fn find_matching_brace(chars: &[char], start: usize) -> Option<usize> {
let mut depth = 0;
let mut in_string = false;
let mut i = start;
while i < chars.len() {
match chars[i] {
'\'' if !in_string => in_string = true,
'\'' if in_string => {
if i + 1 < chars.len() && chars[i + 1] == '\'' {
i += 2;
continue;
}
in_string = false;
}
'{' if !in_string => depth += 1,
'}' if !in_string => {
depth -= 1;
if depth == 0 {
return Some(i);
}
}
_ => {}
}
i += 1;
}
None
}