use crate::backends::swift::gen_bindings::dto::swift_typed_default_literal;
use crate::codegen::shared::binding_fields;
use crate::core::ir::{ApiSurface, DefaultValue, FieldDef, TypeRef};
use std::collections::HashSet;
pub(crate) fn swift_memberwise_default_literal(field: &FieldDef) -> Option<String> {
let collection_type = match &field.ty {
TypeRef::Optional(inner) => matches!(inner.as_ref(), TypeRef::Vec(_) | TypeRef::Map(_, _)),
TypeRef::Vec(_) | TypeRef::Map(_, _) => true,
_ => false,
};
if collection_type && (field.optional || matches!(field.ty, TypeRef::Optional(_))) {
return None;
}
field
.typed_default
.as_ref()
.and_then(swift_typed_default_literal)
.or_else(|| {
let uses_type_zero = matches!(field.typed_default, Some(DefaultValue::Empty))
|| (field.typed_default.is_none() && field.has_bare_serde_default());
if !uses_type_zero {
return None;
}
match &field.ty {
TypeRef::Vec(_) => Some("[]".to_string()),
TypeRef::Map(_, _) => Some("[:]".to_string()),
_ => None,
}
})
}
pub(crate) fn compute_zero_arg_constructible_names(
api: &ApiSurface,
known_dto_names: &HashSet<String>,
) -> HashSet<String> {
api.types
.iter()
.filter(|t| !t.is_trait && !t.is_opaque && t.has_serde && !t.fields.is_empty())
.filter(|t| known_dto_names.contains(&t.name))
.filter(|t| {
binding_fields(&t.fields).all(|field| {
let already_optional = matches!(&field.ty, TypeRef::Optional(_));
field.optional || already_optional || swift_memberwise_default_literal(field).is_some()
})
})
.map(|t| t.name.clone())
.collect()
}
pub(crate) fn zero_arg_named_default(
typed_default: &Option<DefaultValue>,
ty: &TypeRef,
swift_ty: &str,
zero_arg_constructible_names: &HashSet<String>,
) -> Option<String> {
if !matches!(typed_default, Some(DefaultValue::Empty)) {
return None;
}
let TypeRef::Named(name) = ty else {
return None;
};
zero_arg_constructible_names
.contains(name)
.then(|| format!("{swift_ty}()"))
}