use super::{ApiSurface, DefaultValue, FieldDef, PrimitiveType, TypeDef, TypeRef};
impl TypeDef {
#[must_use]
pub fn legacy_constructor_fields(&self) -> &[String] {
&self.version.legacy_constructor_fields
}
#[must_use]
pub fn is_constructor_extension(&self, field: &FieldDef) -> bool {
!self.legacy_constructor_fields().is_empty() && !self.legacy_constructor_fields().contains(&field.name)
}
#[must_use]
pub fn constructor_extension_default(&self, field: &FieldDef) -> Option<bool> {
if field.ty != TypeRef::Primitive(PrimitiveType::Bool) {
return None;
}
match self.constructor_extension_value(field)? {
DefaultValue::BoolLiteral(value) => Some(*value),
DefaultValue::Empty => Some(false),
_ => None,
}
}
#[must_use]
pub fn constructor_extension_value<'a>(&self, field: &'a FieldDef) -> Option<&'a DefaultValue> {
if !self.is_constructor_extension(field) {
return None;
}
field.typed_default.as_ref().filter(|value| {
!matches!(
value,
DefaultValue::Unresolved(_) | DefaultValue::FunctionCall(_) | DefaultValue::PublicFunctionCall(_)
)
})
}
}
pub(crate) fn validate_constructor_history(api: &ApiSurface) -> anyhow::Result<()> {
for typ in &api.types {
if typ.legacy_constructor_fields().is_empty() {
continue;
}
anyhow::ensure!(typ.has_default, "{} legacy constructor requires Rust Default", typ.name);
anyhow::ensure!(
!typ.is_opaque && !typ.has_private_fields,
"{} legacy constructor metadata requires a transparent DTO",
typ.name
);
anyhow::ensure!(
!typ.methods
.iter()
.any(|method| matches!(method.name.as_str(), "new" | "new_with_fields")),
"{} legacy constructor metadata conflicts with an explicit constructor",
typ.name
);
let mut seen = std::collections::HashSet::new();
for name in typ.legacy_constructor_fields() {
anyhow::ensure!(seen.insert(name), "duplicate legacy constructor field `{name}`");
anyhow::ensure!(
typ.fields
.iter()
.any(|field| field.name == *name && !field.binding_excluded),
"unknown legacy constructor field `{name}`"
);
}
anyhow::ensure!(
typ.fields
.iter()
.all(|field| field.binding_excluded || field.cfg.is_none()),
"{} legacy constructor metadata requires unconditional fields",
typ.name
);
for field in typ
.fields
.iter()
.filter(|field| !field.binding_excluded && typ.is_constructor_extension(field))
{
anyhow::ensure!(
typ.constructor_extension_value(field).is_some(),
"{} legacy constructor extension `{}` of type `{:?}` requires a resolved Rust default",
typ.name,
field.name,
field.ty
);
}
}
Ok(())
}