use std::collections::BTreeSet;
use cratestack_core::{Query, Schema, TypeArity};
use crate::diagnostics::{SchemaError, span_error};
use crate::validate::reserved_idents::validate_reserved_identifier;
const BINDABLE_ARG_TYPES: &[&str] = &[
"String", "Cuid", "Int", "Float", "Boolean", "DateTime", "Uuid", "Bytes",
];
pub(super) fn validate_query_args(query: &Query) -> Result<(), SchemaError> {
let mut seen = BTreeSet::new();
for arg in &query.args {
validate_reserved_identifier(
&arg.name,
arg.name_span,
&format!("query parameter `{}` on query `{}`", arg.name, query.name),
)?;
if !seen.insert(arg.name.as_str()) {
return Err(span_error(
format!(
"query `{}` declares parameter `{}` more than once",
query.name, arg.name
),
arg.span,
));
}
if arg.ty.arity != TypeArity::Required {
return Err(span_error(
format!(
"query `{}` parameter `{}` must be a required scalar — optional (`T?`) and \
list (`T[]`) parameters are not supported in v1",
query.name, arg.name
),
arg.span,
));
}
if !BINDABLE_ARG_TYPES.contains(&arg.ty.name.as_str()) {
return Err(span_error(
format!(
"query `{}` parameter `{}` has type `{}`, which cannot be bound as a SQL \
parameter; supported parameter types are: {}",
query.name,
arg.name,
arg.ty.name,
BINDABLE_ARG_TYPES.join(", ")
),
arg.span,
));
}
}
Ok(())
}
pub(super) fn validate_query_result_type(
query: &Query,
schema: &Schema,
type_names: &BTreeSet<String>,
) -> Result<(), SchemaError> {
let name = query.result_type.name.as_str();
if query.result_type.arity == TypeArity::Optional {
return Err(span_error(
format!(
"query `{}` declares an optional result type `{name}?`; use `{name}` for exactly \
one row or `{name}[]` for zero or more",
query.name
),
query.span,
));
}
if schema.types.iter().any(|ty| ty.name == name) {
return Ok(());
}
let hint = if type_names.contains(name) {
format!(
"`{name}` is not a `type` declaration; a query's result must be a `type` block, \
because a query's raw SQL gets none of the soft-delete or row-policy filtering a \
model read does"
)
} else {
format!("no `type {name}` is declared in this schema")
};
Err(span_error(
format!("query `{}` has an unknown result type: {hint}", query.name),
query.span,
))
}