use crate::core::ir::TypeRef;
use crate::e2e::escape::escape_php;
use heck::ToLowerCamelCase;
pub(super) fn emit_php_object_array(arr: &serde_json::Value, elem_type: &str) -> String {
if let Some(items) = arr.as_array() {
let item_strs: Vec<String> = items
.iter()
.filter_map(|item| {
if let Some(obj) = item.as_object() {
let json_str = serde_json::to_string(&serde_json::Value::Object(obj.clone()))
.unwrap_or_else(|_| "{}".to_string());
let php_literal = json_str.replace('\\', "\\\\").replace('\'', "\\'");
Some(format!("{}::from_json('{}')", elem_type, php_literal))
} else {
None
}
})
.collect();
format!("[{}]", item_strs.join(", "))
} else {
"[]".to_string()
}
}
pub(super) fn filter_empty_enum_strings(value: &serde_json::Value) -> serde_json::Value {
match value {
serde_json::Value::Object(map) => {
let filtered: serde_json::Map<String, serde_json::Value> = map
.iter()
.filter_map(|(k, v)| {
if let serde_json::Value::String(s) = v {
if s.is_empty() {
return None;
}
}
Some((k.clone(), filter_empty_enum_strings(v)))
})
.collect();
serde_json::Value::Object(filtered)
}
serde_json::Value::Array(arr) => {
let filtered: Vec<serde_json::Value> = arr.iter().map(filter_empty_enum_strings).collect();
serde_json::Value::Array(filtered)
}
other => other.clone(),
}
}
pub(super) fn json_to_php(value: &serde_json::Value) -> String {
match value {
serde_json::Value::String(s) => format!("\"{}\"", escape_php(s)),
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(json_to_php).collect();
format!("[{}]", items.join(", "))
}
serde_json::Value::Object(map) => {
let items: Vec<String> = map
.iter()
.map(|(k, v)| format!("\"{}\" => {}", escape_php(k), json_to_php(v)))
.collect();
format!("[{}]", items.join(", "))
}
}
}
pub(super) fn get_field_type_name(
struct_name: &str,
field_name: &str,
type_defs: &[crate::core::ir::TypeDef],
) -> Option<String> {
type_defs
.iter()
.find(|td| td.name == struct_name)
.and_then(|td| td.fields.iter().find(|f| f.name == field_name))
.and_then(|field| match &field.ty {
TypeRef::Named(name) => Some(name.clone()),
TypeRef::Optional(inner) => match &**inner {
TypeRef::Named(name) => Some(name.clone()),
_ => None,
},
_ => None,
})
}
pub(super) fn json_to_php_camel_keys_with_types(
value: &serde_json::Value,
current_type_name: Option<&str>,
serde_rename_all: Option<&str>,
type_defs: &[crate::core::ir::TypeDef],
) -> String {
match value {
serde_json::Value::Object(map) => {
let items: Vec<String> = map
.iter()
.map(|(k, v)| {
let final_key = if serde_rename_all == Some("camelCase") {
k.to_lower_camel_case()
} else {
k.to_string()
};
let nested_type_name = current_type_name.and_then(|tn| get_field_type_name(tn, k, type_defs));
format!(
"\"{}\" => {}",
escape_php(&final_key),
json_to_php_camel_keys_with_types(v, nested_type_name.as_deref(), serde_rename_all, type_defs)
)
})
.collect();
format!("[{}]", items.join(", "))
}
serde_json::Value::Array(arr) => {
let items: Vec<String> = arr
.iter()
.map(|item| json_to_php_camel_keys_with_types(item, current_type_name, serde_rename_all, type_defs))
.collect();
format!("[{}]", items.join(", "))
}
_ => json_to_php(value),
}
}
pub(super) fn is_php_reserved_type(name: &str) -> bool {
matches!(
name.to_ascii_lowercase().as_str(),
"string"
| "int"
| "integer"
| "float"
| "double"
| "bool"
| "boolean"
| "array"
| "object"
| "null"
| "void"
| "callable"
| "iterable"
| "never"
| "self"
| "parent"
| "static"
| "true"
| "false"
| "mixed"
)
}