use crate::e2e::escape::go_string_literal;
pub(super) fn convert_json_for_go(value: serde_json::Value) -> serde_json::Value {
match value {
serde_json::Value::Object(map) => {
let new_map: serde_json::Map<String, serde_json::Value> = map
.into_iter()
.map(|(k, v)| (camel_to_snake_case(&k), convert_json_for_go(v)))
.collect();
serde_json::Value::Object(new_map)
}
serde_json::Value::Array(arr) => {
if is_byte_array(&arr) {
let bytes: Vec<u8> = arr
.iter()
.filter_map(|v| v.as_u64().and_then(|n| if n <= 255 { Some(n as u8) } else { None }))
.collect();
let encoded = base64_encode(&bytes);
serde_json::Value::String(encoded)
} else {
serde_json::Value::Array(arr.into_iter().map(convert_json_for_go).collect())
}
}
serde_json::Value::String(s) => {
serde_json::Value::String(pascal_to_snake_case(&s))
}
other => other,
}
}
fn is_byte_array(arr: &[serde_json::Value]) -> bool {
if arr.is_empty() {
return false;
}
arr.iter().all(|v| {
if let serde_json::Value::Number(n) = v {
n.is_u64() && n.as_u64().is_some_and(|u| u <= 255)
} else {
false
}
})
}
fn base64_encode(bytes: &[u8]) -> String {
const TABLE: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut result = String::new();
let mut i = 0;
while i + 2 < bytes.len() {
let b1 = bytes[i];
let b2 = bytes[i + 1];
let b3 = bytes[i + 2];
result.push(TABLE[(b1 >> 2) as usize] as char);
result.push(TABLE[(((b1 & 0x03) << 4) | (b2 >> 4)) as usize] as char);
result.push(TABLE[(((b2 & 0x0f) << 2) | (b3 >> 6)) as usize] as char);
result.push(TABLE[(b3 & 0x3f) as usize] as char);
i += 3;
}
if i < bytes.len() {
let b1 = bytes[i];
result.push(TABLE[(b1 >> 2) as usize] as char);
if i + 1 < bytes.len() {
let b2 = bytes[i + 1];
result.push(TABLE[(((b1 & 0x03) << 4) | (b2 >> 4)) as usize] as char);
result.push(TABLE[((b2 & 0x0f) << 2) as usize] as char);
result.push('=');
} else {
result.push(TABLE[((b1 & 0x03) << 4) as usize] as char);
result.push_str("==");
}
}
result
}
fn camel_to_snake_case(s: &str) -> String {
let mut result = String::new();
let mut prev_upper = false;
for (i, c) in s.char_indices() {
if c.is_uppercase() {
if i > 0 && !prev_upper {
result.push('_');
}
result.push(c.to_lowercase().next().unwrap_or(c));
prev_upper = true;
} else {
if prev_upper && i > 1 {
}
result.push(c);
prev_upper = false;
}
}
result
}
fn pascal_to_snake_case(s: &str) -> String {
let first_char = s.chars().next();
if first_char.is_none() || !first_char.unwrap().is_uppercase() || s.contains('_') || s.contains(' ') {
return s.to_string();
}
camel_to_snake_case(s)
}
pub(super) fn element_type_to_go_slice(element_type: Option<&str>, import_alias: &str) -> String {
let elem = element_type.unwrap_or("String").trim();
let go_elem = rust_type_to_go(elem, import_alias);
format!("[]{go_elem}")
}
fn rust_type_to_go(rust: &str, import_alias: &str) -> String {
let trimmed = rust.trim();
if let Some(inner) = trimmed.strip_prefix("Vec<").and_then(|s| s.strip_suffix('>')) {
return format!("[]{}", rust_type_to_go(inner, import_alias));
}
match trimmed {
"String" | "&str" | "str" => "string".to_string(),
"bool" => "bool".to_string(),
"f32" => "float32".to_string(),
"f64" => "float64".to_string(),
"i8" => "int8".to_string(),
"i16" => "int16".to_string(),
"i32" => "int32".to_string(),
"i64" | "isize" => "int64".to_string(),
"u8" => "uint8".to_string(),
"u16" => "uint16".to_string(),
"u32" => "uint32".to_string(),
"u64" | "usize" => "uint64".to_string(),
_ => format!("{import_alias}.{trimmed}"),
}
}
pub(super) fn json_to_go(value: &serde_json::Value) -> String {
match value {
serde_json::Value::String(s) => go_string_literal(s),
serde_json::Value::Bool(b) => b.to_string(),
serde_json::Value::Number(n) => n.to_string(),
serde_json::Value::Null => "nil".to_string(),
other => go_string_literal(&other.to_string()),
}
}