use crate::parser::types::{FragmentDefinition, InlineFragment, TypeCondition, VariableDefinition}; use crate::registry::MetaTypeName; use crate::validation::visitor::{Visitor, VisitorContext}; use crate::{Name, Pos, Positioned}; #[derive(Default)] pub struct KnownTypeNames; impl<'a> Visitor<'a> for KnownTypeNames { fn enter_fragment_definition( &mut self, ctx: &mut VisitorContext<'a>, _name: &'a Name, fragment_definition: &'a Positioned, ) { let TypeCondition { on: name } = &fragment_definition.node.type_condition.node; validate_type(ctx, &name.node, fragment_definition.pos); } fn enter_variable_definition( &mut self, ctx: &mut VisitorContext<'a>, variable_definition: &'a Positioned, ) { validate_type( ctx, MetaTypeName::concrete_typename(&variable_definition.node.var_type.to_string()), variable_definition.pos, ); } fn enter_inline_fragment( &mut self, ctx: &mut VisitorContext<'a>, inline_fragment: &'a Positioned, ) { if let Some(TypeCondition { on: name }) = inline_fragment .node .type_condition .as_ref() .map(|c| &c.node) { validate_type(ctx, &name.node, inline_fragment.pos); } } } fn validate_type(ctx: &mut VisitorContext<'_>, type_name: &str, pos: Pos) { if ctx.registry.types.get(type_name).is_none() { ctx.report_error(vec![pos], format!(r#"Unknown type "{}""#, type_name)); } } #[cfg(test)] mod tests { use super::*; pub fn factory() -> KnownTypeNames { KnownTypeNames::default() } #[test] fn known_type_names_are_valid() { expect_passes_rule!( factory, r#" query Foo($var: String, $required: [String!]!) { user(id: 4) { pets { ... on Pet { name }, ...PetFields, ... { name } } } } fragment PetFields on Pet { name } "#, ); } #[test] fn unknown_type_names_are_invalid() { expect_fails_rule!( factory, r#" query Foo($var: JumbledUpLetters) { user(id: 4) { name pets { ... on Badger { name }, ...PetFields } } } fragment PetFields on Peettt { name } "#, ); } }