use anyhow::bail;
use fraiseql_core::schema::{FieldType, TypeDefinition};
enum IdDefect {
WrongType(String),
Missing,
}
fn id_defect(ty: &TypeDefinition) -> Option<IdDefect> {
match ty.fields.iter().find(|f| f.name == "id") {
Some(f) if f.field_type == FieldType::Id => None,
Some(f) => Some(IdDefect::WrongType(f.field_type.to_graphql_string())),
None => Some(IdDefect::Missing),
}
}
pub(super) fn enforce_node_id_conformance<'a>(
entities: impl Iterator<Item = &'a TypeDefinition>,
lead: &str,
remedy: &str,
) -> anyhow::Result<()> {
let offenders: Vec<String> = entities
.filter_map(|ty| id_defect(ty).map(|defect| (ty.name.to_string(), defect)))
.map(|(name, defect)| match defect {
IdDefect::WrongType(actual) => format!(" - Type '{name}': `id` is {actual}, not ID"),
IdDefect::Missing => format!(" - Type '{name}': no `id` field"),
})
.collect();
if offenders.is_empty() {
return Ok(());
}
bail!(
"{lead}:\n{}\nFix: expose `id: ID!` on each type, or {remedy}.",
offenders.join("\n"),
);
}