From eca72cea7b5daa09dd6a5f8ac0d1fcc955c5f071 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 21 Jul 2019 15:03:55 +0200 Subject: [PATCH 1/8] Add `SchemaVisitor` trait and functions for walking a schema ast --- src/schema/mod.rs | 1 + src/schema/schema_visitor.rs | 218 +++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 src/schema/schema_visitor.rs diff --git a/src/schema/mod.rs b/src/schema/mod.rs index 49d5e9d..2f72eb8 100644 --- a/src/schema/mod.rs +++ b/src/schema/mod.rs @@ -4,6 +4,7 @@ mod ast; mod grammar; mod error; mod format; +pub mod schema_visitor; pub use self::ast::*; pub use self::error::ParseError; diff --git a/src/schema/schema_visitor.rs b/src/schema/schema_visitor.rs new file mode 100644 index 0000000..b1a6839 --- /dev/null +++ b/src/schema/schema_visitor.rs @@ -0,0 +1,218 @@ +//! Schema syntax tree traversal. +//! +//! Each method of [`SchemaVisitor`] is a hook that can be overridden to customize the behavior when +//! visiting the corresponding type of node. By default, the methods don't do anything. The actual +//! walking of the ast is done by the `walk_*` functions. So to run a visitor over the whole +//! document you should use [`walk_document`]. +//! +//! Example: +//! +//! ``` +//! use graphql_parser::schema::{ +//! ObjectType, +//! parse_schema, +//! schema_visitor::{SchemaVisitor, walk_document}, +//! }; +//! +//! struct ObjectTypesCounter { +//! count: usize, +//! } +//! +//! impl ObjectTypesCounter { +//! fn new() -> Self { +//! Self { count: 0 } +//! } +//! } +//! +//! impl<'ast> SchemaVisitor<'ast> for ObjectTypesCounter { +//! fn visit_object_type(&mut self, node: &'ast ObjectType) { +//! self.count += 1; +//! } +//! } +//! +//! fn main() { +//! let mut number_of_type = ObjectTypesCounter::new(); +//! +//! let doc = parse_schema(r#" +//! schema { +//! query: Query +//! } +//! +//! type Query { +//! users: [User!]! +//! } +//! +//! type User { +//! id: ID! +//! } +//! "#).expect("Failed to parse schema"); +//! +//! walk_document(&mut number_of_type, &doc); +//! +//! assert_eq!(number_of_type.count, 2); +//! } +//! ``` +//! +//! [`SchemaVisitor`]: /graphql_parser/schema/schema_visitor/trait.SchemaVisitor.html +//! [`walk_document`]: /graphql_parser/schema/schema_visitor/fn.walk_document.html + +#![allow(unused_variables)] + +use super::ast::*; + +/// Trait for easy schema syntax tree traversal. +/// +/// See [module docs](/graphql_parser/schema/schema_visitor/index.html) for more info. +pub trait SchemaVisitor<'ast> { + fn visit_document(&mut self, doc: &'ast Document) {} + + fn visit_schema_definition(&mut self, node: &'ast SchemaDefinition) {} + + fn visit_directive_definition(&mut self, node: &'ast DirectiveDefinition) {} + + fn visit_type_definition(&mut self, node: &'ast TypeDefinition) {} + + fn visit_scalar_type(&mut self, node: &'ast ScalarType) {} + + fn visit_object_type(&mut self, node: &'ast ObjectType) {} + + fn visit_interface_type(&mut self, node: &'ast InterfaceType) {} + + fn visit_union_type(&mut self, node: &'ast UnionType) {} + + fn visit_enum_type(&mut self, node: &'ast EnumType) {} + + fn visit_input_object_type(&mut self, node: &'ast InputObjectType) {} + + fn visit_type_extension(&mut self, node: &'ast TypeExtension) {} + + fn visit_scalar_type_extension(&mut self, node: &'ast ScalarTypeExtension) {} + + fn visit_object_type_extension(&mut self, node: &'ast ObjectTypeExtension) {} + + fn visit_interface_type_extension(&mut self, node: &'ast InterfaceTypeExtension) {} + + fn visit_union_type_extension(&mut self, node: &'ast UnionTypeExtension) {} + + fn visit_enum_type_extension(&mut self, node: &'ast EnumTypeExtension) {} + + fn visit_input_object_type_extension(&mut self, node: &'ast InputObjectTypeExtension) {} +} + +/// Walk a schema syntax tree and call the visitor methods for each type of node. +/// +/// This function is how you should initiate a visitor. +pub fn walk_document<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, doc: &'ast Document) { + use super::ast::Definition::*; + + for def in &doc.definitions { + match def { + SchemaDefinition(inner) => { + visitor.visit_schema_definition(inner); + walk_schema_definition(visitor, inner); + } + TypeDefinition(inner) => { + visitor.visit_type_definition(inner); + walk_type_definition(visitor, inner); + } + TypeExtension(inner) => { + visitor.visit_type_extension(inner); + walk_type_extension(visitor, inner); + } + DirectiveDefinition(inner) => { + visitor.visit_directive_definition(inner); + walk_directive_definition(visitor, inner); + } + } + } +} + +fn walk_schema_definition<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast SchemaDefinition) {} + +fn walk_directive_definition<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast DirectiveDefinition) {} + +fn walk_type_definition<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast TypeDefinition) { + use super::ast::TypeDefinition::*; + + match node { + Scalar(inner) => { + visitor.visit_scalar_type(inner); + walk_scalar_type(visitor, inner); + } + Object(inner) => { + visitor.visit_object_type(inner); + walk_object_type(visitor, inner); + } + Interface(inner) => { + visitor.visit_interface_type(inner); + walk_interface_type(visitor, inner); + } + Union(inner) => { + visitor.visit_union_type(inner); + walk_union_type(visitor, inner); + } + Enum(inner) => { + visitor.visit_enum_type(inner); + walk_enum_type(visitor, inner); + } + InputObject(inner) => { + visitor.visit_input_object_type(inner); + walk_input_object_type(visitor, inner); + } + } +} + +fn walk_scalar_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast ScalarType) {} + +fn walk_object_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast ObjectType) {} + +fn walk_interface_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast InterfaceType) {} + +fn walk_union_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast UnionType) {} + +fn walk_enum_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast EnumType) {} + +fn walk_input_object_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast InputObjectType) {} + +fn walk_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast TypeExtension) { + use super::ast::TypeExtension::*; + + match node { + Scalar(inner) => { + visitor.visit_scalar_type_extension(inner); + walk_scalar_type_extension(visitor, inner); + } + Object(inner) => { + visitor.visit_object_type_extension(inner); + walk_object_type_extension(visitor, inner); + } + Interface(inner) => { + visitor.visit_interface_type_extension(inner); + walk_interface_type_extension(visitor, inner); + } + Union(inner) => { + visitor.visit_union_type_extension(inner); + walk_union_type_extension(visitor, inner); + } + Enum(inner) => { + visitor.visit_enum_type_extension(inner); + walk_enum_type_extension(visitor, inner); + } + InputObject(inner) => { + visitor.visit_input_object_type_extension(inner); + walk_input_object_type_extension(visitor, inner); + } + } +} + +fn walk_scalar_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast ScalarTypeExtension) {} + +fn walk_object_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast ObjectTypeExtension) {} + +fn walk_interface_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast InterfaceTypeExtension) {} + +fn walk_union_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast UnionTypeExtension) {} + +fn walk_enum_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast EnumTypeExtension) {} + +fn walk_input_object_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast InputObjectTypeExtension) {} From 158b5e71c56283e598bdf8f5396894f04179de2e Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 21 Jul 2019 23:44:11 +0200 Subject: [PATCH 2/8] Add `QueryVisitor` trait and functions for walking a query ast --- src/query/mod.rs | 1 + src/query/query_visitor.rs | 225 +++++++++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 src/query/query_visitor.rs diff --git a/src/query/mod.rs b/src/query/mod.rs index d1cfcf0..2c60b17 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -4,6 +4,7 @@ mod ast; mod error; mod format; mod grammar; +pub mod query_visitor; pub use self::grammar::parse_query; diff --git a/src/query/query_visitor.rs b/src/query/query_visitor.rs new file mode 100644 index 0000000..34305b4 --- /dev/null +++ b/src/query/query_visitor.rs @@ -0,0 +1,225 @@ +//! Query syntax tree traversal. +//! +//! Each method of [`QueryVisitor`] is a hook that can be overridden to customize the behavior when +//! visiting the corresponding type of node. By default, the methods don't do anything. The actual +//! walking of the ast is done by the `walk_*` functions. So to run a visitor over the whole +//! document you should use [`walk_document`]. +//! +//! Example: +//! +//! ``` +//! use graphql_parser::query::{ +//! Field, +//! parse_query, +//! query_visitor::{QueryVisitor, walk_document}, +//! }; +//! +//! struct FieldsCounter { +//! count: usize, +//! } +//! +//! impl FieldsCounter { +//! fn new() -> Self { +//! Self { count: 0 } +//! } +//! } +//! +//! impl<'ast> QueryVisitor<'ast> for FieldsCounter { +//! fn visit_field(&mut self, node: &'ast Field) { +//! self.count += 1 +//! } +//! } +//! +//! fn main() { +//! let mut number_of_type = FieldsCounter::new(); +//! +//! let doc = parse_query(r#" +//! query TestQuery { +//! users { +//! id +//! country { +//! id +//! } +//! } +//! } +//! "#).expect("Failed to parse query"); +//! +//! walk_document(&mut number_of_type, &doc); +//! +//! assert_eq!(number_of_type.count, 2); +//! } +//! ``` +//! +//! [`QueryVisitor`]: /graphql_parser/query/query_visitor/trait.QueryVisitor.html +//! [`walk_document`]: /graphql_parser/query/query_visitor/fn.walk_document.html + +#![allow(unused_variables)] + +use super::ast::*; + +/// Trait for easy query syntax tree traversal. +/// +/// See [module docs](/graphql_parser/query/query_visitor/index.html) for more info. +pub trait QueryVisitor<'ast> { + fn visit_document(&mut self, node: &'ast Document) {} + + fn visit_definition(&mut self, node: &'ast Definition) {} + + fn visit_fragment_definition(&mut self, node: &'ast FragmentDefinition) {} + + fn visit_operation_definition(&mut self, node: &'ast OperationDefinition) {} + + fn visit_query(&mut self, node: &'ast Query) {} + + fn visit_mutation(&mut self, node: &'ast Mutation) {} + + fn visit_subscription(&mut self, node: &'ast Subscription) {} + + fn visit_selection_set(&mut self, node: &'ast SelectionSet) {} + + fn visit_variable_definition(&mut self, node: &'ast VariableDefinition) {} + + fn visit_selection(&mut self, node: &'ast Selection) {} + + fn visit_field(&mut self, node: &'ast Field) {} + + fn visit_fragment_spread(&mut self, node: &'ast FragmentSpread) {} + + fn visit_inline_fragment(&mut self, node: &'ast InlineFragment) {} +} + + +/// Walk a query syntax tree and call the visitor methods for each type of node. +/// +/// This function is how you should initiate a visitor. +pub fn walk_document<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Document) { + visitor.visit_document(node); + for def in &node.definitions { + walk_definition(visitor, def); + } +} + +fn walk_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Definition) { + use super::ast::Definition::*; + + visitor.visit_definition(node); + match node { + Operation(inner) => { + visitor.visit_operation_definition(inner); + walk_operation_definition(visitor, inner); + }, + Fragment(inner) => { + visitor.visit_fragment_definition(inner); + walk_fragment_definition(visitor, inner); + }, + } +} + +fn walk_fragment_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast FragmentDefinition) { + visitor.visit_fragment_definition(node); +} + +fn walk_operation_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast OperationDefinition) { + use super::ast::OperationDefinition::*; + + visitor.visit_operation_definition(node); + match node { + SelectionSet(inner) => { + visitor.visit_selection_set(inner); + walk_selection_set(visitor, inner); + } + Query(inner) => { + visitor.visit_query(inner); + walk_query(visitor, inner); + } + Mutation(inner) => { + visitor.visit_mutation(inner); + walk_mutation(visitor, inner); + } + Subscription(inner) => { + visitor.visit_subscription(inner); + walk_subscription(visitor, inner); + } + } +} + +fn walk_query<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Query) { + visitor.visit_query(node); + + for var_def in &node.variable_definitions { + visitor.visit_variable_definition(var_def); + walk_variable_definition(visitor, var_def); + } + + visitor.visit_selection_set(&node.selection_set); + walk_selection_set(visitor, &node.selection_set); +} + +fn walk_mutation<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Mutation) { + visitor.visit_mutation(node); + + for var_def in &node.variable_definitions { + visitor.visit_variable_definition(var_def); + walk_variable_definition(visitor, var_def); + } + + visitor.visit_selection_set(&node.selection_set); + walk_selection_set(visitor, &node.selection_set); +} + +fn walk_subscription<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Subscription) { + visitor.visit_subscription(node); + + for var_def in &node.variable_definitions { + visitor.visit_variable_definition(var_def); + walk_variable_definition(visitor, var_def); + } + + visitor.visit_selection_set(&node.selection_set); + walk_selection_set(visitor, &node.selection_set); +} + +fn walk_selection_set<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast SelectionSet) { + visitor.visit_selection_set(node); + + for selection in &node.items { + visitor.visit_selection(selection); + walk_selection(visitor, selection); + } +} + +fn walk_variable_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast VariableDefinition) { + visitor.visit_variable_definition(node) +} + +fn walk_selection<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Selection) { + use super::ast::Selection::*; + + visitor.visit_selection(node); + match node { + Field(inner) => { + visitor.visit_field(inner); + walk_field(visitor, inner); + } + FragmentSpread(inner) => { + visitor.visit_fragment_spread(inner); + walk_fragment_spread(visitor, inner); + } + InlineFragment(inner) => { + visitor.visit_inline_fragment(inner); + walk_inline_fragment(visitor, inner); + } + } +} + +fn walk_field<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Field) { + visitor.visit_field(node) +} + +fn walk_fragment_spread<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast FragmentSpread) { + visitor.visit_fragment_spread(node) +} + +fn walk_inline_fragment<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast InlineFragment) { + visitor.visit_inline_fragment(node); +} From db9310ea1f9ac75b7e676e3ea9ab16fdb2cefe31 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Mon, 7 Oct 2019 21:34:09 +0200 Subject: [PATCH 3/8] Walk down selection set of fragment definitions --- src/query/query_visitor.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/query/query_visitor.rs b/src/query/query_visitor.rs index 34305b4..b6cce46 100644 --- a/src/query/query_visitor.rs +++ b/src/query/query_visitor.rs @@ -117,6 +117,7 @@ fn walk_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Def fn walk_fragment_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast FragmentDefinition) { visitor.visit_fragment_definition(node); + walk_selection_set(visitor, &node.selection_set); } fn walk_operation_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast OperationDefinition) { From cb71a7e00b38e1fe349c41fe8a1982fcf869b833 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Mon, 7 Oct 2019 21:35:09 +0200 Subject: [PATCH 4/8] Walk down selection of inline fragment --- src/query/query_visitor.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/query/query_visitor.rs b/src/query/query_visitor.rs index b6cce46..9674597 100644 --- a/src/query/query_visitor.rs +++ b/src/query/query_visitor.rs @@ -223,4 +223,5 @@ fn walk_fragment_spread<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'as fn walk_inline_fragment<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast InlineFragment) { visitor.visit_inline_fragment(node); + walk_selection_set(visitor, &node.selection_set); } From e12a623d24aaa44cac75f416e8c3a28247cf10ad Mon Sep 17 00:00:00 2001 From: Vlady Veselinov Date: Fri, 2 Jul 2021 17:27:11 +0100 Subject: [PATCH 5/8] add TypeInfo struct --- src/common.rs | 18 ++++++++++++++++++ src/lib.rs | 3 +++ src/schema/ast.rs | 2 +- src/validation/mod.rs | 1 + src/validation/type_info.rs | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/validation/mod.rs create mode 100644 src/validation/type_info.rs diff --git a/src/common.rs b/src/common.rs index aae4d39..295dd70 100644 --- a/src/common.rs +++ b/src/common.rs @@ -5,6 +5,7 @@ use combine::easy::Error; use combine::error::StreamError; use combine::combinator::{many, many1, optional, position, choice}; +use crate::schema::{EnumType, InputObjectType, InterfaceType, ObjectType, ScalarType, UnionType}; use crate::tokenizer::{Kind as T, Token, TokenStream}; use crate::helpers::{punct, ident, kind, name}; use crate::position::Pos; @@ -65,6 +66,23 @@ pub enum Type<'a, T: Text<'a>> { NonNullType(Box>), } +#[derive(Debug, Clone, PartialEq)] +pub enum CompositeType<'a, T: Text<'a>> { + Object(ObjectType<'a, T>), + Interface(InterfaceType<'a, T>), + Union(UnionType<'a, T>), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum InputType<'a, T: Text<'a>> { + Scalar(ScalarType<'a, T>), + Enum(EnumType<'a, T>), + InputObject(InputObjectType<'a, T>), + List(Box>), + NonNullType(Box>), +} + + impl Number { /// Returns a number as i64 if it fits the type pub fn as_i64(&self) -> Option { diff --git a/src/lib.rs b/src/lib.rs index 3649bdf..9689e10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,6 +95,8 @@ mod common; +mod validation; + #[macro_use] mod format; mod position; @@ -102,6 +104,7 @@ mod tokenizer; mod helpers; pub mod query; pub mod schema; +pub mod validation; pub use crate::query::parse_query; pub use crate::schema::parse_schema; diff --git a/src/schema/ast.rs b/src/schema/ast.rs index 39ba829..ee028db 100644 --- a/src/schema/ast.rs +++ b/src/schema/ast.rs @@ -518,4 +518,4 @@ impl FromStr for DirectiveLocation { Ok(val) } -} +} \ No newline at end of file diff --git a/src/validation/mod.rs b/src/validation/mod.rs new file mode 100644 index 0000000..82ddb56 --- /dev/null +++ b/src/validation/mod.rs @@ -0,0 +1 @@ +mod type_info; \ No newline at end of file diff --git a/src/validation/type_info.rs b/src/validation/type_info.rs new file mode 100644 index 0000000..530b98d --- /dev/null +++ b/src/validation/type_info.rs @@ -0,0 +1,36 @@ +//! TypeInfo is a utility class which, given a GraphQL schema, can keep track +//! of the current field and type definitions at any point in a GraphQL document +//! AST during a recursive descent by calling `enter(node)` and `leave(node)`. + +use crate::{ + common::{CompositeType, InputType}, + query::Field, + schema::{Directive, EnumValue, SchemaDefinition, Text, Type, Value}, +}; +pub struct TypeInfo<'ast, T: Text<'ast>> { + schema: SchemaDefinition<'ast, T>, + type_stack: Vec>>, + parent_type_stack: Vec>>, + input_type_stack: Vec>>, + field_def_stack: Vec>>, + default_value_stack: Vec>, + directive: Option>, + argument: Option<(T::Value, Value<'ast, T>)>, + enum_value: Option>, +} + +impl<'ast, T: Text<'ast>> TypeInfo<'ast, T> { + pub fn new(schema: SchemaDefinition<'ast, T>) -> TypeInfo<'ast, T> { + TypeInfo { + schema, + type_stack: Vec::new(), + parent_type_stack: Vec::new(), + input_type_stack: Vec::new(), + field_def_stack: Vec::new(), + default_value_stack: Vec::new(), + directive: None, + argument: None, + enum_value: None, + } + } +} From 7404956a13dfde2c79fffc50f9261bed9109062c Mon Sep 17 00:00:00 2001 From: Vlady Veselinov Date: Fri, 2 Jul 2021 20:20:09 +0100 Subject: [PATCH 6/8] fix compile errors --- src/validation/type_info.rs | 46 +++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/validation/type_info.rs b/src/validation/type_info.rs index 530b98d..c089f80 100644 --- a/src/validation/type_info.rs +++ b/src/validation/type_info.rs @@ -2,11 +2,7 @@ //! of the current field and type definitions at any point in a GraphQL document //! AST during a recursive descent by calling `enter(node)` and `leave(node)`. -use crate::{ - common::{CompositeType, InputType}, - query::Field, - schema::{Directive, EnumValue, SchemaDefinition, Text, Type, Value}, -}; +use crate::{common::{CompositeType, InputType}, query::{Field, query_visitor::QueryVisitor}, schema::{Directive, EnumValue, SchemaDefinition, Text, Type, Value}}; pub struct TypeInfo<'ast, T: Text<'ast>> { schema: SchemaDefinition<'ast, T>, type_stack: Vec>>, @@ -34,3 +30,43 @@ impl<'ast, T: Text<'ast>> TypeInfo<'ast, T> { } } } + +impl<'ast> QueryVisitor<'ast> for TypeInfo<'ast, &'ast str> { + +} + + +#[cfg(test)] +mod type_info_tests { + const TEST_SCHEMA: &str = r#" + interface Pet { + name: String + } + + type Dog implements Pet { + name: String + } + + type Cat implements Pet { + name: String + } + + type Human { + name: String + pets: [Pet] + } + + type Alien { + name(surname: Boolean): String + } + + type QueryRoot { + human(id: ID): Human + alien: Alien + } + + schema { + query: QueryRoot + } + "#; +} \ No newline at end of file From e8ecfb23fcb7398f71c825af714ca811f27502dc Mon Sep 17 00:00:00 2001 From: Vlady Veselinov Date: Fri, 2 Jul 2021 20:20:34 +0100 Subject: [PATCH 7/8] fix type errors --- src/lib.rs | 1 - src/query/query_visitor.rs | 52 +++++++++++++++++++------------------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9689e10..63e321d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,7 +95,6 @@ mod common; -mod validation; #[macro_use] mod format; diff --git a/src/query/query_visitor.rs b/src/query/query_visitor.rs index 9674597..278572b 100644 --- a/src/query/query_visitor.rs +++ b/src/query/query_visitor.rs @@ -61,45 +61,45 @@ use super::ast::*; /// /// See [module docs](/graphql_parser/query/query_visitor/index.html) for more info. pub trait QueryVisitor<'ast> { - fn visit_document(&mut self, node: &'ast Document) {} + fn visit_document(&mut self, node: &'ast Document<'ast, &'ast str>) {} - fn visit_definition(&mut self, node: &'ast Definition) {} + fn visit_definition(&mut self, node: &'ast Definition<'ast, &'ast str>) {} - fn visit_fragment_definition(&mut self, node: &'ast FragmentDefinition) {} + fn visit_fragment_definition(&mut self, node: &'ast FragmentDefinition<'ast, &'ast str>) {} - fn visit_operation_definition(&mut self, node: &'ast OperationDefinition) {} + fn visit_operation_definition(&mut self, node: &'ast OperationDefinition<'ast, &'ast str>) {} - fn visit_query(&mut self, node: &'ast Query) {} + fn visit_query(&mut self, node: &'ast Query<'ast, &'ast str>) {} - fn visit_mutation(&mut self, node: &'ast Mutation) {} + fn visit_mutation(&mut self, node: &'ast Mutation<'ast, &'ast str>) {} - fn visit_subscription(&mut self, node: &'ast Subscription) {} + fn visit_subscription(&mut self, node: &'ast Subscription<'ast, &'ast str>) {} - fn visit_selection_set(&mut self, node: &'ast SelectionSet) {} + fn visit_selection_set(&mut self, node: &'ast SelectionSet<'ast, &'ast str>) {} - fn visit_variable_definition(&mut self, node: &'ast VariableDefinition) {} + fn visit_variable_definition(&mut self, node: &'ast VariableDefinition<'ast, &'ast str>) {} - fn visit_selection(&mut self, node: &'ast Selection) {} + fn visit_selection(&mut self, node: &'ast Selection<'ast, &'ast str>) {} - fn visit_field(&mut self, node: &'ast Field) {} + fn visit_field(&mut self, node: &'ast Field<'ast, &'ast str>) {} - fn visit_fragment_spread(&mut self, node: &'ast FragmentSpread) {} + fn visit_fragment_spread(&mut self, node: &'ast FragmentSpread<'ast, &'ast str>) {} - fn visit_inline_fragment(&mut self, node: &'ast InlineFragment) {} + fn visit_inline_fragment(&mut self, node: &'ast InlineFragment<'ast, &'ast str>) {} } /// Walk a query syntax tree and call the visitor methods for each type of node. /// /// This function is how you should initiate a visitor. -pub fn walk_document<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Document) { +pub fn walk_document<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Document<'ast, &'ast str>) { visitor.visit_document(node); for def in &node.definitions { walk_definition(visitor, def); } } -fn walk_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Definition) { +fn walk_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Definition<'ast, &'ast str>) { use super::ast::Definition::*; visitor.visit_definition(node); @@ -115,12 +115,12 @@ fn walk_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Def } } -fn walk_fragment_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast FragmentDefinition) { +fn walk_fragment_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast FragmentDefinition<'ast, &'ast str>) { visitor.visit_fragment_definition(node); walk_selection_set(visitor, &node.selection_set); } -fn walk_operation_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast OperationDefinition) { +fn walk_operation_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast OperationDefinition<'ast, &'ast str>) { use super::ast::OperationDefinition::*; visitor.visit_operation_definition(node); @@ -144,7 +144,7 @@ fn walk_operation_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: } } -fn walk_query<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Query) { +fn walk_query<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Query<'ast, &'ast str>) { visitor.visit_query(node); for var_def in &node.variable_definitions { @@ -156,7 +156,7 @@ fn walk_query<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Query) { walk_selection_set(visitor, &node.selection_set); } -fn walk_mutation<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Mutation) { +fn walk_mutation<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Mutation<'ast, &'ast str>) { visitor.visit_mutation(node); for var_def in &node.variable_definitions { @@ -168,7 +168,7 @@ fn walk_mutation<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Mutat walk_selection_set(visitor, &node.selection_set); } -fn walk_subscription<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Subscription) { +fn walk_subscription<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Subscription<'ast, &'ast str>) { visitor.visit_subscription(node); for var_def in &node.variable_definitions { @@ -180,7 +180,7 @@ fn walk_subscription<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast S walk_selection_set(visitor, &node.selection_set); } -fn walk_selection_set<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast SelectionSet) { +fn walk_selection_set<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast SelectionSet<'ast, &'ast str>) { visitor.visit_selection_set(node); for selection in &node.items { @@ -189,11 +189,11 @@ fn walk_selection_set<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast } } -fn walk_variable_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast VariableDefinition) { +fn walk_variable_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast VariableDefinition<'ast, &'ast str>) { visitor.visit_variable_definition(node) } -fn walk_selection<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Selection) { +fn walk_selection<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Selection<'ast, &'ast str>) { use super::ast::Selection::*; visitor.visit_selection(node); @@ -213,15 +213,15 @@ fn walk_selection<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Sele } } -fn walk_field<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Field) { +fn walk_field<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Field<'ast, &'ast str>) { visitor.visit_field(node) } -fn walk_fragment_spread<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast FragmentSpread) { +fn walk_fragment_spread<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast FragmentSpread<'ast, &'ast str>) { visitor.visit_fragment_spread(node) } -fn walk_inline_fragment<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast InlineFragment) { +fn walk_inline_fragment<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast InlineFragment<'ast, &'ast str>) { visitor.visit_inline_fragment(node); walk_selection_set(visitor, &node.selection_set); } From 1fa659c41f59a24799ae35cf02c9d02318283901 Mon Sep 17 00:00:00 2001 From: Vlady Veselinov Date: Mon, 5 Jul 2021 19:44:09 +0100 Subject: [PATCH 8/8] add TypeInfo to QueryVisitor --- Cargo.toml | 1 + src/query/query_visitor.rs | 333 ++++++++++++++++++++++++++--------- src/schema/schema_visitor.rs | 68 +++---- src/validation/mod.rs | 3 +- src/validation/type_info.rs | 128 +++++++++++++- 5 files changed, 412 insertions(+), 121 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 602e85f..246ab28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,4 @@ thiserror = "1.0.11" [dev-dependencies] pretty_assertions = "0.5.0" +k9 = "0.11.0" \ No newline at end of file diff --git a/src/query/query_visitor.rs b/src/query/query_visitor.rs index 278572b..900d421 100644 --- a/src/query/query_visitor.rs +++ b/src/query/query_visitor.rs @@ -44,7 +44,7 @@ //! } //! "#).expect("Failed to parse query"); //! -//! walk_document(&mut number_of_type, &doc); +//! walk_document(&mut number_of_type, &doc, None); //! //! assert_eq!(number_of_type.count, 2); //! } @@ -56,172 +56,347 @@ #![allow(unused_variables)] use super::ast::*; +use crate::validation::TypeInfo; /// Trait for easy query syntax tree traversal. /// /// See [module docs](/graphql_parser/query/query_visitor/index.html) for more info. -pub trait QueryVisitor<'ast> { - fn visit_document(&mut self, node: &'ast Document<'ast, &'ast str>) {} +pub trait QueryVisitor<'ast, T: Text<'ast>> { + fn visit_document( + &mut self, + node: &'ast Document<'ast, T>, + type_info: &mut Option>, + ) { + } - fn visit_definition(&mut self, node: &'ast Definition<'ast, &'ast str>) {} + fn visit_definition( + &mut self, + node: &'ast Definition<'ast, T>, + type_info: &mut Option>, + ) { + } - fn visit_fragment_definition(&mut self, node: &'ast FragmentDefinition<'ast, &'ast str>) {} + fn visit_fragment_definition( + &mut self, + node: &'ast FragmentDefinition<'ast, T>, + type_info: &mut Option>, + ) { + } - fn visit_operation_definition(&mut self, node: &'ast OperationDefinition<'ast, &'ast str>) {} + fn visit_operation_definition( + &mut self, + node: &'ast OperationDefinition<'ast, T>, + type_info: &mut Option>, + ) { + } - fn visit_query(&mut self, node: &'ast Query<'ast, &'ast str>) {} + fn visit_query( + &mut self, + node: &'ast Query<'ast, T>, + type_info: &mut Option>, + ) { + } - fn visit_mutation(&mut self, node: &'ast Mutation<'ast, &'ast str>) {} + fn visit_mutation( + &mut self, + node: &'ast Mutation<'ast, T>, + type_info: &mut Option>, + ) { + } - fn visit_subscription(&mut self, node: &'ast Subscription<'ast, &'ast str>) {} + fn visit_subscription( + &mut self, + node: &'ast Subscription<'ast, T>, + type_info: &mut Option>, + ) { + } - fn visit_selection_set(&mut self, node: &'ast SelectionSet<'ast, &'ast str>) {} + fn visit_selection_set( + &mut self, + node: &'ast SelectionSet<'ast, T>, + type_info: &mut Option>, + ) { + } - fn visit_variable_definition(&mut self, node: &'ast VariableDefinition<'ast, &'ast str>) {} + fn visit_variable_definition( + &mut self, + node: &'ast VariableDefinition<'ast, T>, + type_info: &mut Option>, + ) { + } - fn visit_selection(&mut self, node: &'ast Selection<'ast, &'ast str>) {} + fn visit_selection( + &mut self, + node: &'ast Selection<'ast, T>, + type_info: &mut Option>, + ) { + } - fn visit_field(&mut self, node: &'ast Field<'ast, &'ast str>) {} + fn visit_field( + &mut self, + node: &'ast Field<'ast, T>, + type_info: &mut Option>, + ) { + } - fn visit_fragment_spread(&mut self, node: &'ast FragmentSpread<'ast, &'ast str>) {} + fn visit_fragment_spread( + &mut self, + node: &'ast FragmentSpread<'ast, T>, + type_info: &mut Option>, + ) { + } - fn visit_inline_fragment(&mut self, node: &'ast InlineFragment<'ast, &'ast str>) {} + fn visit_inline_fragment( + &mut self, + node: &'ast InlineFragment<'ast, T>, + type_info: &mut Option>, + ) { + } } - /// Walk a query syntax tree and call the visitor methods for each type of node. /// /// This function is how you should initiate a visitor. -pub fn walk_document<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Document<'ast, &'ast str>) { - visitor.visit_document(node); +pub fn walk_document<'ast, T: Text<'ast>, V: QueryVisitor<'ast, T>>( + visitor: &mut V, + node: &'ast Document<'ast, T>, + type_info: &mut Option>, +) { + if let Some(info) = type_info { + info.visit_document(node, &mut None); + } + + visitor.visit_document(node, type_info); + for def in &node.definitions { - walk_definition(visitor, def); + walk_definition(visitor, def, type_info); } } -fn walk_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Definition<'ast, &'ast str>) { +fn walk_definition<'ast, T: Text<'ast>, V: QueryVisitor<'ast, T>>( + visitor: &mut V, + node: &'ast Definition<'ast, T>, + type_info: &mut Option>, +) { use super::ast::Definition::*; - visitor.visit_definition(node); + if let Some(info) = type_info { + info.visit_definition(node, &mut None); + } + + visitor.visit_definition(node, type_info); + match node { Operation(inner) => { - visitor.visit_operation_definition(inner); - walk_operation_definition(visitor, inner); - }, + visitor.visit_operation_definition(inner, type_info); + walk_operation_definition(visitor, inner, type_info); + } Fragment(inner) => { - visitor.visit_fragment_definition(inner); - walk_fragment_definition(visitor, inner); - }, + visitor.visit_fragment_definition(inner, type_info); + walk_fragment_definition(visitor, inner, type_info); + } } } -fn walk_fragment_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast FragmentDefinition<'ast, &'ast str>) { - visitor.visit_fragment_definition(node); - walk_selection_set(visitor, &node.selection_set); +fn walk_fragment_definition<'ast, T: Text<'ast>, V: QueryVisitor<'ast, T>>( + visitor: &mut V, + node: &'ast FragmentDefinition<'ast, T>, + type_info: &mut Option>, +) { + if let Some(info) = type_info { + info.visit_fragment_definition(node, &mut None); + } + + visitor.visit_fragment_definition(node, type_info); + + walk_selection_set(visitor, &node.selection_set, type_info); } -fn walk_operation_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast OperationDefinition<'ast, &'ast str>) { +fn walk_operation_definition<'ast, T: Text<'ast>, V: QueryVisitor<'ast, T>>( + visitor: &mut V, + node: &'ast OperationDefinition<'ast, T>, + type_info: &mut Option>, +) { use super::ast::OperationDefinition::*; - visitor.visit_operation_definition(node); + if let Some(info) = type_info { + info.visit_operation_definition(node, &mut None); + } + + visitor.visit_operation_definition(node, type_info); + match node { SelectionSet(inner) => { - visitor.visit_selection_set(inner); - walk_selection_set(visitor, inner); + visitor.visit_selection_set(inner, type_info); + walk_selection_set(visitor, inner, type_info); } Query(inner) => { - visitor.visit_query(inner); - walk_query(visitor, inner); + visitor.visit_query(inner, type_info); + walk_query(visitor, inner, type_info); } Mutation(inner) => { - visitor.visit_mutation(inner); - walk_mutation(visitor, inner); + visitor.visit_mutation(inner, type_info); + walk_mutation(visitor, inner, type_info); } Subscription(inner) => { - visitor.visit_subscription(inner); - walk_subscription(visitor, inner); + visitor.visit_subscription(inner, type_info); + walk_subscription(visitor, inner, type_info); } } } -fn walk_query<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Query<'ast, &'ast str>) { - visitor.visit_query(node); +fn walk_query<'ast, T: Text<'ast>, V: QueryVisitor<'ast, T>>( + visitor: &mut V, + node: &'ast Query<'ast, T>, + type_info: &mut Option>, +) { + if let Some(info) = type_info { + info.visit_query(node, &mut None); + } + + visitor.visit_query(node, type_info); for var_def in &node.variable_definitions { - visitor.visit_variable_definition(var_def); - walk_variable_definition(visitor, var_def); + visitor.visit_variable_definition(var_def, type_info); + walk_variable_definition(visitor, var_def, type_info); } - visitor.visit_selection_set(&node.selection_set); - walk_selection_set(visitor, &node.selection_set); + visitor.visit_selection_set(&node.selection_set, type_info); + walk_selection_set(visitor, &node.selection_set, type_info); } -fn walk_mutation<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Mutation<'ast, &'ast str>) { - visitor.visit_mutation(node); +fn walk_mutation<'ast, T: Text<'ast>, V: QueryVisitor<'ast, T>>( + visitor: &mut V, + node: &'ast Mutation<'ast, T>, + type_info: &mut Option>, +) { + if let Some(info) = type_info { + info.visit_mutation(node, &mut None); + } + + visitor.visit_mutation(node, type_info); for var_def in &node.variable_definitions { - visitor.visit_variable_definition(var_def); - walk_variable_definition(visitor, var_def); + visitor.visit_variable_definition(var_def, type_info); + walk_variable_definition(visitor, var_def, type_info); } - visitor.visit_selection_set(&node.selection_set); - walk_selection_set(visitor, &node.selection_set); + visitor.visit_selection_set(&node.selection_set, type_info); + walk_selection_set(visitor, &node.selection_set, type_info); } -fn walk_subscription<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Subscription<'ast, &'ast str>) { - visitor.visit_subscription(node); +fn walk_subscription<'ast, T: Text<'ast>, V: QueryVisitor<'ast, T>>( + visitor: &mut V, + node: &'ast Subscription<'ast, T>, + type_info: &mut Option>, +) { + if let Some(info) = type_info { + info.visit_subscription(node, &mut None); + } + + visitor.visit_subscription(node, type_info); for var_def in &node.variable_definitions { - visitor.visit_variable_definition(var_def); - walk_variable_definition(visitor, var_def); + visitor.visit_variable_definition(var_def, type_info); + walk_variable_definition(visitor, var_def, type_info); } - visitor.visit_selection_set(&node.selection_set); - walk_selection_set(visitor, &node.selection_set); + visitor.visit_selection_set(&node.selection_set, type_info); + walk_selection_set(visitor, &node.selection_set, type_info); } -fn walk_selection_set<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast SelectionSet<'ast, &'ast str>) { - visitor.visit_selection_set(node); +fn walk_selection_set<'ast, T: Text<'ast>, V: QueryVisitor<'ast, T>>( + visitor: &mut V, + node: &'ast SelectionSet<'ast, T>, + type_info: &mut Option>, +) { + if let Some(info) = type_info { + info.visit_selection_set(node, &mut None); + } + + visitor.visit_selection_set(node, type_info); for selection in &node.items { - visitor.visit_selection(selection); - walk_selection(visitor, selection); + visitor.visit_selection(selection, type_info); + walk_selection(visitor, selection, type_info); } } -fn walk_variable_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast VariableDefinition<'ast, &'ast str>) { - visitor.visit_variable_definition(node) +fn walk_variable_definition<'ast, T: Text<'ast>, V: QueryVisitor<'ast, T>>( + visitor: &mut V, + node: &'ast VariableDefinition<'ast, T>, + type_info: &mut Option>, +) { + if let Some(info) = type_info { + info.visit_variable_definition(node, &mut None); + } + + visitor.visit_variable_definition(node, type_info); } -fn walk_selection<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Selection<'ast, &'ast str>) { +fn walk_selection<'ast, T: Text<'ast>, V: QueryVisitor<'ast, T>>( + visitor: &mut V, + node: &'ast Selection<'ast, T>, + type_info: &mut Option>, +) { use super::ast::Selection::*; - visitor.visit_selection(node); + if let Some(info) = type_info { + info.visit_selection(node, &mut None); + } + + visitor.visit_selection(node, type_info); + match node { Field(inner) => { - visitor.visit_field(inner); - walk_field(visitor, inner); + visitor.visit_field(inner, type_info); + walk_field(visitor, inner, type_info); } FragmentSpread(inner) => { - visitor.visit_fragment_spread(inner); - walk_fragment_spread(visitor, inner); + visitor.visit_fragment_spread(inner, type_info); + walk_fragment_spread(visitor, inner, type_info); } InlineFragment(inner) => { - visitor.visit_inline_fragment(inner); - walk_inline_fragment(visitor, inner); + visitor.visit_inline_fragment(inner, type_info); + walk_inline_fragment(visitor, inner, type_info); } } } -fn walk_field<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Field<'ast, &'ast str>) { - visitor.visit_field(node) +fn walk_field<'ast, T: Text<'ast>, V: QueryVisitor<'ast, T>>( + visitor: &mut V, + node: &'ast Field<'ast, T>, + type_info: &mut Option>, +) { + if let Some(info) = type_info { + info.visit_field(node, &mut None); + } + + visitor.visit_field(node, type_info); } -fn walk_fragment_spread<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast FragmentSpread<'ast, &'ast str>) { - visitor.visit_fragment_spread(node) +fn walk_fragment_spread<'ast, T: Text<'ast>, V: QueryVisitor<'ast, T>>( + visitor: &mut V, + node: &'ast FragmentSpread<'ast, T>, + type_info: &mut Option>, +) { + if let Some(info) = type_info { + info.visit_fragment_spread(node, &mut None); + } + + visitor.visit_fragment_spread(node, type_info); } -fn walk_inline_fragment<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast InlineFragment<'ast, &'ast str>) { - visitor.visit_inline_fragment(node); - walk_selection_set(visitor, &node.selection_set); +fn walk_inline_fragment<'ast, T: Text<'ast>, V: QueryVisitor<'ast, T>>( + visitor: &mut V, + node: &'ast InlineFragment<'ast, T>, + type_info: &mut Option>, +) { + if let Some(info) = type_info { + info.visit_inline_fragment(node, &mut None); + } + + visitor.visit_inline_fragment(node, type_info); + + walk_selection_set(visitor, &node.selection_set, type_info); } diff --git a/src/schema/schema_visitor.rs b/src/schema/schema_visitor.rs index b1a6839..ffe8758 100644 --- a/src/schema/schema_visitor.rs +++ b/src/schema/schema_visitor.rs @@ -64,45 +64,45 @@ use super::ast::*; /// /// See [module docs](/graphql_parser/schema/schema_visitor/index.html) for more info. pub trait SchemaVisitor<'ast> { - fn visit_document(&mut self, doc: &'ast Document) {} + fn visit_document(&mut self, doc: &'ast Document<'ast, &'ast str>) {} - fn visit_schema_definition(&mut self, node: &'ast SchemaDefinition) {} + fn visit_schema_definition(&mut self, node: &'ast SchemaDefinition<'ast, &'ast str>) {} - fn visit_directive_definition(&mut self, node: &'ast DirectiveDefinition) {} + fn visit_directive_definition(&mut self, node: &'ast DirectiveDefinition<'ast, &'ast str>) {} - fn visit_type_definition(&mut self, node: &'ast TypeDefinition) {} + fn visit_type_definition(&mut self, node: &'ast TypeDefinition<'ast, &'ast str>) {} - fn visit_scalar_type(&mut self, node: &'ast ScalarType) {} + fn visit_scalar_type(&mut self, node: &'ast ScalarType<'ast, &'ast str>) {} - fn visit_object_type(&mut self, node: &'ast ObjectType) {} + fn visit_object_type(&mut self, node: &'ast ObjectType<'ast, &'ast str>) {} - fn visit_interface_type(&mut self, node: &'ast InterfaceType) {} + fn visit_interface_type(&mut self, node: &'ast InterfaceType<'ast, &'ast str>) {} - fn visit_union_type(&mut self, node: &'ast UnionType) {} + fn visit_union_type(&mut self, node: &'ast UnionType<'ast, &'ast str>) {} - fn visit_enum_type(&mut self, node: &'ast EnumType) {} + fn visit_enum_type(&mut self, node: &'ast EnumType<'ast, &'ast str>) {} - fn visit_input_object_type(&mut self, node: &'ast InputObjectType) {} + fn visit_input_object_type(&mut self, node: &'ast InputObjectType<'ast, &'ast str>) {} - fn visit_type_extension(&mut self, node: &'ast TypeExtension) {} + fn visit_type_extension(&mut self, node: &'ast TypeExtension<'ast, &'ast str>) {} - fn visit_scalar_type_extension(&mut self, node: &'ast ScalarTypeExtension) {} + fn visit_scalar_type_extension(&mut self, node: &'ast ScalarTypeExtension<'ast, &'ast str>) {} - fn visit_object_type_extension(&mut self, node: &'ast ObjectTypeExtension) {} + fn visit_object_type_extension(&mut self, node: &'ast ObjectTypeExtension<'ast, &'ast str>) {} - fn visit_interface_type_extension(&mut self, node: &'ast InterfaceTypeExtension) {} + fn visit_interface_type_extension(&mut self, node: &'ast InterfaceTypeExtension<'ast, &'ast str>) {} - fn visit_union_type_extension(&mut self, node: &'ast UnionTypeExtension) {} + fn visit_union_type_extension(&mut self, node: &'ast UnionTypeExtension<'ast, &'ast str>) {} - fn visit_enum_type_extension(&mut self, node: &'ast EnumTypeExtension) {} + fn visit_enum_type_extension(&mut self, node: &'ast EnumTypeExtension<'ast, &'ast str>) {} - fn visit_input_object_type_extension(&mut self, node: &'ast InputObjectTypeExtension) {} + fn visit_input_object_type_extension(&mut self, node: &'ast InputObjectTypeExtension<'ast, &'ast str>) {} } /// Walk a schema syntax tree and call the visitor methods for each type of node. /// /// This function is how you should initiate a visitor. -pub fn walk_document<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, doc: &'ast Document) { +pub fn walk_document<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, doc: &'ast Document<'ast, &'ast str>) { use super::ast::Definition::*; for def in &doc.definitions { @@ -127,11 +127,11 @@ pub fn walk_document<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, doc: &'ast D } } -fn walk_schema_definition<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast SchemaDefinition) {} +fn walk_schema_definition<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast SchemaDefinition<'ast, &'ast str>) {} -fn walk_directive_definition<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast DirectiveDefinition) {} +fn walk_directive_definition<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast DirectiveDefinition<'ast, &'ast str>) {} -fn walk_type_definition<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast TypeDefinition) { +fn walk_type_definition<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast TypeDefinition<'ast, &'ast str>) { use super::ast::TypeDefinition::*; match node { @@ -162,19 +162,19 @@ fn walk_type_definition<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'a } } -fn walk_scalar_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast ScalarType) {} +fn walk_scalar_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast ScalarType<'ast, &'ast str>) {} -fn walk_object_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast ObjectType) {} +fn walk_object_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast ObjectType<'ast, &'ast str>) {} -fn walk_interface_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast InterfaceType) {} +fn walk_interface_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast InterfaceType<'ast, &'ast str>) {} -fn walk_union_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast UnionType) {} +fn walk_union_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast UnionType<'ast, &'ast str>) {} -fn walk_enum_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast EnumType) {} +fn walk_enum_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast EnumType<'ast, &'ast str>) {} -fn walk_input_object_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast InputObjectType) {} +fn walk_input_object_type<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast InputObjectType<'ast, &'ast str>) {} -fn walk_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast TypeExtension) { +fn walk_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast TypeExtension<'ast, &'ast str>) { use super::ast::TypeExtension::*; match node { @@ -205,14 +205,14 @@ fn walk_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'as } } -fn walk_scalar_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast ScalarTypeExtension) {} +fn walk_scalar_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast ScalarTypeExtension<'ast, &'ast str>) {} -fn walk_object_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast ObjectTypeExtension) {} +fn walk_object_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast ObjectTypeExtension<'ast, &'ast str>) {} -fn walk_interface_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast InterfaceTypeExtension) {} +fn walk_interface_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast InterfaceTypeExtension<'ast, &'ast str>) {} -fn walk_union_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast UnionTypeExtension) {} +fn walk_union_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast UnionTypeExtension<'ast, &'ast str>) {} -fn walk_enum_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast EnumTypeExtension) {} +fn walk_enum_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast EnumTypeExtension<'ast, &'ast str>) {} -fn walk_input_object_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast InputObjectTypeExtension) {} +fn walk_input_object_type_extension<'ast, V: SchemaVisitor<'ast>>(visitor: &mut V, node: &'ast InputObjectTypeExtension<'ast, &'ast str>) {} diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 82ddb56..d5bc458 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -1 +1,2 @@ -mod type_info; \ No newline at end of file +pub mod type_info; +pub use type_info::TypeInfo; \ No newline at end of file diff --git a/src/validation/type_info.rs b/src/validation/type_info.rs index c089f80..c39dbcb 100644 --- a/src/validation/type_info.rs +++ b/src/validation/type_info.rs @@ -2,9 +2,13 @@ //! of the current field and type definitions at any point in a GraphQL document //! AST during a recursive descent by calling `enter(node)` and `leave(node)`. -use crate::{common::{CompositeType, InputType}, query::{Field, query_visitor::QueryVisitor}, schema::{Directive, EnumValue, SchemaDefinition, Text, Type, Value}}; +use crate::{ + common::{CompositeType, InputType}, + query::{query_visitor::QueryVisitor, Field}, + schema::{Directive, Document, EnumValue, Text, Type, Value}, +}; pub struct TypeInfo<'ast, T: Text<'ast>> { - schema: SchemaDefinition<'ast, T>, + schema: Document<'ast, T>, type_stack: Vec>>, parent_type_stack: Vec>>, input_type_stack: Vec>>, @@ -16,7 +20,7 @@ pub struct TypeInfo<'ast, T: Text<'ast>> { } impl<'ast, T: Text<'ast>> TypeInfo<'ast, T> { - pub fn new(schema: SchemaDefinition<'ast, T>) -> TypeInfo<'ast, T> { + pub fn new(schema: Document<'ast, T>) -> TypeInfo<'ast, T> { TypeInfo { schema, type_stack: Vec::new(), @@ -29,16 +33,62 @@ impl<'ast, T: Text<'ast>> TypeInfo<'ast, T> { enum_value: None, } } -} -impl<'ast> QueryVisitor<'ast> for TypeInfo<'ast, &'ast str> { + pub fn get_type(&self) -> &Option> { + let type_stack_length = self.type_stack.len(); + + if type_stack_length > 0 { + &self.type_stack[type_stack_length - 1] + } else { + &None + } + } + + pub fn get_parent_type(&self) -> &Option> { + // if (this._parentTypeStack.length > 0) { + // return this._parentTypeStack[this._parentTypeStack.length - 1]; + // } + let parent_type_stack_length = self.parent_type_stack.len(); + + if parent_type_stack_length > 0 { + &self.parent_type_stack[parent_type_stack_length - 1] + } else { + &None + } + } + + pub fn get_input_type(&self) -> &Option> { + let input_type_stack = self.input_type_stack.len(); + if input_type_stack > 0 { + &self.input_type_stack[input_type_stack - 1] + } else { + &None + } + } } +impl<'ast, T: Text<'ast>> QueryVisitor<'ast, T> for TypeInfo<'ast, T> {} #[cfg(test)] mod type_info_tests { - const TEST_SCHEMA: &str = r#" + use std::{borrow::Borrow, convert::TryInto}; + + use k9::assert_equal; + use query::{Text, Type}; + + use crate::{ + common::{CompositeType, InputType}, + parse_query, parse_schema, + query::{ + self, + query_visitor::{walk_document, QueryVisitor}, + }, + schema::{self, ObjectType}, + validation::TypeInfo, + }; + + const TEST_SCHEMA: &str = r#" interface Pet { name: String } @@ -69,4 +119,68 @@ mod type_info_tests { query: QueryRoot } "#; -} \ No newline at end of file + + #[test] + pub fn visit_document_maintains_type_info() { + let schema_ast: schema::Document = parse_schema(TEST_SCHEMA).unwrap(); + let mut type_info = TypeInfo::new(schema_ast); + + let query_ast: query::Document = parse_query( + r#"{ + human(id: 4) { + name, + pets { + ... { name } + }, + unknown + } + }"#, + ) + .unwrap(); + + #[derive(Debug, PartialEq)] + struct TestVisitor<'ast, T: Text<'ast>> { + parent_types: Vec<&'ast Option>>, + current_types: Vec<&'ast Option>>, + input_types: Vec<&'ast Option>>, + } + + impl<'ast, T: Text<'ast>> TestVisitor<'ast, T> { + pub fn new() -> Self { + Self { + parent_types: Vec::new(), + current_types: Vec::new(), + input_types: Vec::new(), + } + } + } + + impl<'ast, T: Text<'ast>> QueryVisitor<'ast, T> for TestVisitor<'ast, T> { + fn visit_document( + &mut self, + node: &'ast query::Document<'ast, T>, + type_info: &mut Option>, + ) { + if let Some(info) = type_info { + self.parent_types.push(info.get_parent_type()); + self.current_types.push(info.get_type()); + self.input_types.push(info.get_input_type()); + } + } + } + + let mut visitor = TestVisitor::new(); + + walk_document(&mut visitor, &query_ast, &mut Some(type_info)); + + let expected = TestVisitor { + parent_types: vec![&Some(CompositeType::Object(ObjectType::new( + "Document".to_string(), + )))], + current_types: Vec::new(), + input_types: Vec::new(), + }; + + assert_equal!(visitor, expected); + } +}