use std::collections::BTreeSet;
use cratestack_core::{Field, Schema};
use crate::diagnostics::{SchemaError, span_error};
use crate::validate::type_names::BUILTIN_TYPES;
pub(super) struct ComputedParamsNameSets<'a> {
type_decl_names: BTreeSet<&'a str>,
model_names: &'a BTreeSet<&'a str>,
enum_names: BTreeSet<&'a str>,
mixin_names: BTreeSet<&'a str>,
}
impl<'a> ComputedParamsNameSets<'a> {
pub(super) fn collect(schema: &'a Schema, model_names: &'a BTreeSet<&'a str>) -> Self {
ComputedParamsNameSets {
type_decl_names: schema.types.iter().map(|ty| ty.name.as_str()).collect(),
model_names,
enum_names: schema
.enums
.iter()
.map(|enum_decl| enum_decl.name.as_str())
.collect(),
mixin_names: schema
.mixins
.iter()
.map(|mixin| mixin.name.as_str())
.collect(),
}
}
}
pub(super) fn validate_computed_params_type(
owner_kind: &str,
owner_name: &str,
field: &Field,
params_type: &str,
name_sets: &ComputedParamsNameSets<'_>,
bearing: &BTreeSet<String>,
) -> Result<(), SchemaError> {
if name_sets.type_decl_names.contains(params_type) {
if bearing.contains(params_type) {
return Err(span_error(
format!(
"field `{}` on {} `{}` uses `@computed(params: {}?)`, but `{}` itself \
contains `@computed` fields — computed params are decoded from the \
request, so a computed field inside the params type could never be \
resolved",
field.name, owner_kind, owner_name, params_type, params_type,
),
field.span,
));
}
return Ok(());
}
let reason = if name_sets.model_names.contains(params_type) {
format!("`{params_type}` is a model, not a declared `type` block")
} else if name_sets.enum_names.contains(params_type) {
format!("`{params_type}` is an enum, not a declared `type` block")
} else if name_sets.mixin_names.contains(params_type) {
format!("`{params_type}` is a mixin, not a declared `type` block")
} else if BUILTIN_TYPES.contains(¶ms_type) {
format!("`{params_type}` is a builtin scalar, not a declared `type` block")
} else {
format!("`{params_type}` is not declared anywhere in this schema")
};
Err(span_error(
format!(
"field `{}` on {} `{}` uses `@computed(params: {}?)`, but {} — computed params \
must reference a declared `type` block (it has fields to decode a JSON payload \
into; models, builtin scalars, enums, and mixins don't)",
field.name, owner_kind, owner_name, params_type, reason,
),
field.span,
))
}