use std::collections::BTreeMap;
use cratestack_core::route_naming::to_snake_case;
use cratestack_core::{Field, Schema, is_computed_field};
use crate::diagnostics::{SchemaError, span_error};
fn resolver_method_name(owner_name: &str, field_name: &str) -> String {
format!(
"resolve_{}_{}",
to_snake_case(owner_name),
to_snake_case(field_name)
)
}
pub(super) fn validate_computed_resolver_name_collisions(
schema: &Schema,
) -> Result<(), SchemaError> {
let owners = schema
.models
.iter()
.map(|model| ("model", model.name.as_str(), &model.fields))
.chain(
schema
.types
.iter()
.map(|ty| ("type", ty.name.as_str(), &ty.fields)),
);
let mut seen: BTreeMap<String, (&str, &str, &Field)> = BTreeMap::new();
for (owner_kind, owner_name, fields) in owners {
for field in fields.iter().filter(|field| is_computed_field(field)) {
let resolver_name = resolver_method_name(owner_name, &field.name);
if let Some((existing_kind, existing_owner, existing_field)) = seen.get(&resolver_name)
{
return Err(span_error(
format!(
"field `{}` on {} `{}` and field `{}` on {} `{}` both generate the \
resolver method `{}` after snake_case flattening (see \
`cratestack_core::route_naming::to_snake_case`) — rename one of the \
owners or one of the fields",
existing_field.name,
existing_kind,
existing_owner,
field.name,
owner_kind,
owner_name,
resolver_name,
),
field.span,
));
}
seen.insert(resolver_name, (owner_kind, owner_name, field));
}
}
Ok(())
}