From e6bb9b82ed534a27604e32302754f08b5a8a22ec Mon Sep 17 00:00:00 2001 From: sunli Date: Sun, 22 Mar 2020 16:45:59 +0800 Subject: [PATCH] Add CacheControl for object --- README.md | 4 + .../examples/subscription.rs | 2 +- async-graphql-actix-web/src/lib.rs | 23 ++- async-graphql-derive/src/args.rs | 131 ++++++++++--- async-graphql-derive/src/interface.rs | 1 + async-graphql-derive/src/object.rs | 24 +++ async-graphql-derive/src/subscription.rs | 2 + src/lib.rs | 22 ++- src/query.rs | 42 +++- src/registry.rs | 82 ++++++++ src/types/connection/connection_type.rs | 7 +- src/types/connection/edge.rs | 3 + src/types/empty_mutation.rs | 1 + src/types/empty_subscription.rs | 1 + src/types/query_root.rs | 2 + src/validation/context.rs | 67 ------- src/validation/mod.rs | 7 +- .../rules/arguments_of_correct_type.rs | 13 +- .../rules/default_values_of_correct_type.rs | 5 +- .../rules/fields_on_correct_type.rs | 5 +- .../rules/fragments_on_composite_types.rs | 7 +- src/validation/rules/known_argument_names.rs | 13 +- src/validation/rules/known_directives.rs | 25 ++- src/validation/rules/known_fragment_names.rs | 5 +- src/validation/rules/known_type_names.rs | 11 +- .../rules/lone_anonymous_operation.rs | 7 +- src/validation/rules/no_compose_leafs.rs | 5 +- src/validation/rules/no_fragment_cycles.rs | 11 +- .../rules/no_undefined_variables.rs | 9 +- src/validation/rules/no_unused_fragments.rs | 7 +- src/validation/rules/no_unused_variables.rs | 11 +- .../rules/overlapping_fields_can_be_merged.rs | 7 +- .../rules/possible_fragment_spreads.rs | 9 +- .../rules/provided_non_null_arguments.rs | 7 +- src/validation/rules/scalar_leafs.rs | 5 +- src/validation/rules/unique_argument_names.rs | 9 +- src/validation/rules/unique_fragment_names.rs | 5 +- .../rules/unique_operation_names.rs | 5 +- src/validation/rules/unique_variable_names.rs | 7 +- src/validation/rules/upload_file.rs | 5 +- .../rules/variables_are_input_types.rs | 5 +- .../rules/variables_in_allowed_position.rs | 13 +- src/{validation => }/visitor.rs | 182 ++++++++++++------ 43 files changed, 522 insertions(+), 292 deletions(-) delete mode 100644 src/validation/context.rs rename src/{validation => }/visitor.rs (73%) diff --git a/README.md b/README.md index 1fd48e82..2ab06905 100644 --- a/README.md +++ b/README.md @@ -165,3 +165,7 @@ Licensed under either of * [GraphQL Multipart Request](https://github.com/jaydenseric/graphql-multipart-request-spec) * [GraphQL Cursor Connections Specification](https://facebook.github.io/relay/graphql/connections.htm) * [GraphQL over WebSocket Protocol](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md) + +## Contribute + +Welcome to contribute ! \ No newline at end of file diff --git a/async-graphql-actix-web/examples/subscription.rs b/async-graphql-actix-web/examples/subscription.rs index dbfccfb5..903eb459 100644 --- a/async-graphql-actix-web/examples/subscription.rs +++ b/async-graphql-actix-web/examples/subscription.rs @@ -34,7 +34,7 @@ type Storage = Arc>>; struct QueryRoot; -#[async_graphql::Object] +#[async_graphql::Object(cache_control(max_age = 5))] impl QueryRoot { #[field] async fn books(&self, ctx: &Context<'_>) -> Vec { diff --git a/async-graphql-actix-web/src/lib.rs b/async-graphql-actix-web/src/lib.rs index c50afc8c..e97fa614 100644 --- a/async-graphql-actix-web/src/lib.rs +++ b/async-graphql-actix-web/src/lib.rs @@ -247,10 +247,25 @@ where .respond_to(&req) .await?) } else if ct.essence_str() == mime::APPLICATION_JSON { - let gql_req = web::Json::::from_request(&req, &mut payload.0).await?; - Ok(web::Json(gql_req.into_inner().execute(&schema).await) - .respond_to(&req) - .await?) + let mut gql_req = web::Json::::from_request(&req, &mut payload.0) + .await? + .into_inner(); + let prepared = gql_req + .prepare(&schema) + .map_err(actix_web::error::ErrorBadRequest)?; + let mut cache_control = dbg!(prepared.cache_control()).value(); + let gql_resp = prepared.execute().await; + if gql_resp.is_err() { + cache_control = None; + } + let mut resp = web::Json(GQLResponse(gql_resp)).respond_to(&req).await?; + if let Some(cache_control) = cache_control { + resp.headers_mut().insert( + header::CACHE_CONTROL, + header::HeaderValue::from_str(&cache_control).unwrap(), + ); + } + Ok(resp) } else { Ok(HttpResponse::UnsupportedMediaType().finish()) } diff --git a/async-graphql-derive/src/args.rs b/async-graphql-derive/src/args.rs index 13f4b6f8..e92eb4b9 100644 --- a/async-graphql-derive/src/args.rs +++ b/async-graphql-derive/src/args.rs @@ -2,13 +2,70 @@ use crate::utils::{parse_validator, parse_value}; use graphql_parser::query::Value; use proc_macro2::TokenStream; use quote::quote; -use syn::{Attribute, AttributeArgs, Error, Meta, MetaList, NestedMeta, Result, Type}; +use syn::{Attribute, AttributeArgs, Error, Lit, Meta, MetaList, NestedMeta, Result, Type}; + +#[derive(Debug)] +pub struct CacheControl { + pub public: bool, + pub max_age: usize, +} + +impl Default for CacheControl { + fn default() -> Self { + Self { + public: true, + max_age: 0, + } + } +} + +impl CacheControl { + pub fn parse(ls: &MetaList) -> Result { + let mut cache_control = Self { + public: true, + max_age: 0, + }; + + for meta in &ls.nested { + match meta { + NestedMeta::Meta(Meta::NameValue(nv)) => { + if nv.path.is_ident("max_age") { + if let Lit::Int(n) = &nv.lit { + match n.base10_parse::() { + Ok(n) => cache_control.max_age = n, + Err(err) => { + return Err(Error::new_spanned(&nv.lit, err)); + } + } + } else { + return Err(Error::new_spanned( + &nv.lit, + "Attribute 'max_age' must be integer.", + )); + } + } + } + NestedMeta::Meta(Meta::Path(p)) => { + if p.is_ident("public") { + cache_control.public = true; + } else if p.is_ident("private") { + cache_control.public = false; + } + } + _ => {} + } + } + + Ok(cache_control) + } +} #[derive(Debug)] pub struct Object { pub internal: bool, pub name: Option, pub desc: Option, + pub cache_control: CacheControl, } impl Object { @@ -16,6 +73,7 @@ impl Object { let mut internal = false; let mut name = None; let mut desc = None; + let mut cache_control = CacheControl::default(); for arg in args { match arg { @@ -43,6 +101,11 @@ impl Object { } } } + NestedMeta::Meta(Meta::List(ls)) => { + if ls.path.is_ident("cache_control") { + cache_control = CacheControl::parse(&ls)?; + } + } _ => {} } } @@ -51,6 +114,7 @@ impl Object { internal, name, desc, + cache_control, }) } } @@ -140,6 +204,7 @@ pub struct Field { pub name: Option, pub desc: Option, pub deprecation: Option, + pub cache_control: CacheControl, } impl Field { @@ -148,6 +213,7 @@ impl Field { let mut name = None; let mut desc = None; let mut deprecation = None; + let mut cache_control = CacheControl::default(); for attr in attrs { match attr.parse_meta()? { @@ -157,35 +223,43 @@ impl Field { Meta::List(ls) if ls.path.is_ident("field") => { is_field = true; for meta in &ls.nested { - if let NestedMeta::Meta(Meta::NameValue(nv)) = meta { - if nv.path.is_ident("name") { - if let syn::Lit::Str(lit) = &nv.lit { - name = Some(lit.value()); - } else { - return Err(Error::new_spanned( - &nv.lit, - "Attribute 'name' should be a string.", - )); - } - } else if nv.path.is_ident("desc") { - if let syn::Lit::Str(lit) = &nv.lit { - desc = Some(lit.value()); - } else { - return Err(Error::new_spanned( - &nv.lit, - "Attribute 'desc' should be a string.", - )); - } - } else if nv.path.is_ident("deprecation") { - if let syn::Lit::Str(lit) = &nv.lit { - deprecation = Some(lit.value()); - } else { - return Err(Error::new_spanned( - &nv.lit, - "Attribute 'deprecation' should be a string.", - )); + match meta { + NestedMeta::Meta(Meta::NameValue(nv)) => { + if nv.path.is_ident("name") { + if let syn::Lit::Str(lit) = &nv.lit { + name = Some(lit.value()); + } else { + return Err(Error::new_spanned( + &nv.lit, + "Attribute 'name' should be a string.", + )); + } + } else if nv.path.is_ident("desc") { + if let syn::Lit::Str(lit) = &nv.lit { + desc = Some(lit.value()); + } else { + return Err(Error::new_spanned( + &nv.lit, + "Attribute 'desc' should be a string.", + )); + } + } else if nv.path.is_ident("deprecation") { + if let syn::Lit::Str(lit) = &nv.lit { + deprecation = Some(lit.value()); + } else { + return Err(Error::new_spanned( + &nv.lit, + "Attribute 'deprecation' should be a string.", + )); + } } } + NestedMeta::Meta(Meta::List(ls)) => { + if ls.path.is_ident("cache_control") { + cache_control = CacheControl::parse(ls)?; + } + } + _ => {} } } } @@ -198,6 +272,7 @@ impl Field { name, desc, deprecation, + cache_control, })) } else { Ok(None) diff --git a/async-graphql-derive/src/interface.rs b/async-graphql-derive/src/interface.rs index 06d0e261..1d4ff7f4 100644 --- a/async-graphql-derive/src/interface.rs +++ b/async-graphql-derive/src/interface.rs @@ -185,6 +185,7 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result }, ty: <#schema_ty as #crate_name::Type>::create_type_info(registry), deprecation: #deprecation, + cache_control: Default::default(), }); }); diff --git a/async-graphql-derive/src/object.rs b/async-graphql-derive/src/object.rs index 2b988f11..f1bc9350 100644 --- a/async-graphql-derive/src/object.rs +++ b/async-graphql-derive/src/object.rs @@ -59,6 +59,16 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result< return Err(Error::new_spanned(&method.sig.output, "Missing type")) } }; + let cache_control = { + let public = field.cache_control.public; + let max_age = field.cache_control.max_age; + quote! { + #crate_name::CacheControl { + public: #public, + max_age: #max_age, + } + } + }; let mut arg_ctx = false; let mut args = Vec::new(); @@ -162,6 +172,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result< } let schema_ty = ty.value_type(); + schema_fields.push(quote! { fields.insert(#field_name.to_string(), #crate_name::registry::Field { name: #field_name.to_string(), @@ -173,6 +184,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result< }, ty: <#schema_ty as #crate_name::Type>::create_type_info(registry), deprecation: #field_deprecation, + cache_control: #cache_control, }); }); @@ -209,6 +221,17 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result< } } + let cache_control = { + let public = object_args.cache_control.public; + let max_age = object_args.cache_control.max_age; + quote! { + #crate_name::CacheControl { + public: #public, + max_age: #max_age, + } + } + }; + let expanded = quote! { #item_impl @@ -226,6 +249,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result< #(#schema_fields)* fields }, + cache_control: #cache_control, }) } } diff --git a/async-graphql-derive/src/subscription.rs b/async-graphql-derive/src/subscription.rs index bb2b8f1d..a24d21d7 100644 --- a/async-graphql-derive/src/subscription.rs +++ b/async-graphql-derive/src/subscription.rs @@ -196,6 +196,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result< }, ty: <#ty as #crate_name::Type>::create_type_info(registry), deprecation: #field_deprecation, + cache_control: Default::default(), }); }); @@ -240,6 +241,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result< #(#schema_fields)* fields }, + cache_control: Default::default(), }) } } diff --git a/src/lib.rs b/src/lib.rs index b79a61d5..3a27e2d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,6 +77,7 @@ mod schema; mod subscription; mod types; mod validation; +mod visitor; /// Input value validators pub mod validators; @@ -98,6 +99,7 @@ pub use context::{Context, Variables}; pub use error::{ErrorWithPosition, PositionError, QueryError, QueryParseError}; pub use graphql_parser::query::Value; pub use query::{PreparedQuery, QueryBuilder}; +pub use registry::CacheControl; pub use scalars::ID; pub use schema::Schema; pub use subscription::SubscribeBuilder; @@ -132,18 +134,20 @@ pub use types::{EnumItem, EnumType}; /// /// # Macro parameters /// -/// | Attribute | description | Type | Optional | -/// |-------------|---------------------------|----------|----------| -/// | name | Object name | string | Y | -/// | desc | Object description | string | Y | +/// | Attribute | description | Type | Optional | +/// |---------------|---------------------------|----------|----------| +/// | name | Object name | string | Y | +/// | desc | Object description | string | Y | +/// | cache_control | Object cache control | [`CacheControl`](struct.CacheControl.html) | Y | /// /// # Field parameters /// -/// | Attribute | description | Type | Optional | -/// |-------------|---------------------------|----------|----------| -/// | name | Field name | string | Y | -/// | desc | Field description | string | Y | -/// | deprecation | Field deprecation reason | string | Y | +/// | Attribute | description | Type | Optional | +/// |---------------|---------------------------|----------|----------| +/// | name | Field name | string | Y | +/// | desc | Field description | string | Y | +/// | deprecation | Field deprecation reason | string | Y | +/// | cache_control | Field cache control | [`CacheControl`](struct.CacheControl.html) | Y | /// /// # Field argument parameters /// diff --git a/src/query.rs b/src/query.rs index c8fe754b..cbb55d20 100644 --- a/src/query.rs +++ b/src/query.rs @@ -1,13 +1,14 @@ use crate::context::Data; -use crate::registry::Registry; +use crate::registry::{CacheControl, Registry, Type}; use crate::types::QueryRoot; use crate::validation::check_rules; +use crate::visitor::{visit, Visitor, VisitorContext}; use crate::{ContextBase, OutputValueType, Result}; use crate::{ObjectType, QueryError, QueryParseError, Variables}; use bytes::Bytes; use graphql_parser::parse_query; use graphql_parser::query::{ - Definition, FragmentDefinition, OperationDefinition, SelectionSet, VariableDefinition, + Definition, Field, FragmentDefinition, OperationDefinition, SelectionSet, VariableDefinition, }; use std::collections::HashMap; @@ -49,6 +50,13 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> { let document = parse_query(self.source).map_err(|err| QueryParseError(err.to_string()))?; check_rules(self.registry, &document)?; + let cache_control = { + let mut ctx = VisitorContext::new(self.registry, &document); + let mut visitor = CacheControlVisitor::default(); + visit(&mut visitor, &mut ctx, &document); + visitor.cache_control + }; + let mut fragments = HashMap::new(); let mut selection_set = None; let mut variable_definitions = None; @@ -106,6 +114,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> { })?, root: root.unwrap(), variable_definitions, + cache_control, }) } @@ -128,6 +137,7 @@ pub struct PreparedQuery<'a, Query, Mutation> { fragments: HashMap, selection_set: SelectionSet, variable_definitions: Option>, + cache_control: CacheControl, } impl<'a, Query, Mutation> PreparedQuery<'a, Query, Mutation> { @@ -177,4 +187,32 @@ impl<'a, Query, Mutation> PreparedQuery<'a, Query, Mutation> { Root::Mutation(mutation) => OutputValueType::resolve(mutation, &ctx).await, } } + + /// Get cache control value + pub fn cache_control(&self) -> CacheControl { + self.cache_control + } +} + +#[derive(Default)] +struct CacheControlVisitor { + cache_control: CacheControl, +} + +impl<'a> Visitor<'a> for CacheControlVisitor { + fn enter_selection_set( + &mut self, + ctx: &mut VisitorContext<'a>, + _selection_set: &'a SelectionSet, + ) { + if let Type::Object { cache_control, .. } = ctx.current_type() { + self.cache_control = self.cache_control.merge(cache_control); + } + } + + fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Field) { + if let Some(registry_field) = ctx.parent_type().unwrap().field_by_name(&field.name) { + self.cache_control = self.cache_control.merge(®istry_field.cache_control); + } + } } diff --git a/src/registry.rs b/src/registry.rs index 041a1a28..e04294aa 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -70,6 +70,7 @@ pub struct Field { pub args: HashMap<&'static str, InputValue>, pub ty: String, pub deprecation: Option<&'static str>, + pub cache_control: CacheControl, } #[derive(Clone)] @@ -79,6 +80,84 @@ pub struct EnumValue { pub deprecation: Option<&'static str>, } +/// Cache control values +/// +/// # Examples +/// +/// ```rust +/// use async_graphql::*; +/// +/// struct QueryRoot; +/// +/// #[Object(cache_control(max_age = 60))] +/// impl QueryRoot { +/// #[field(cache_control(max_age = 30))] +/// async fn value1(&self) -> i32 { +/// unimplemented!() +/// } +/// +/// #[field(cache_control(private))] +/// async fn value2(&self) -> i32 { +/// unimplemented!() +/// } +/// } +/// +/// #[async_std::main] +/// async fn main() { +/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); +/// assert_eq!(schema.query("{ value1 }").prepare().unwrap().cache_control(), CacheControl { public: true, max_age: 30 }); +/// assert_eq!(schema.query("{ value2 }").prepare().unwrap().cache_control(), CacheControl { public: false, max_age: 60 }); +/// assert_eq!(schema.query("{ value1 value2 }").prepare().unwrap().cache_control(), CacheControl { public: false, max_age: 30 }); +/// } +/// ``` +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct CacheControl { + /// Scope is public, default is true + pub public: bool, + + /// Cache max age, default is 0. + pub max_age: usize, +} + +impl Default for CacheControl { + fn default() -> Self { + Self { + public: true, + max_age: 0, + } + } +} + +impl CacheControl { + /// Get 'Cache-Control' header value. + pub fn value(&self) -> Option { + if self.max_age > 0 { + if !self.public { + Some(format!("max-age={}, private", self.max_age)) + } else { + Some(format!("max-age={}", self.max_age)) + } + } else { + None + } + } +} + +impl CacheControl { + pub(crate) fn merge(self, other: &CacheControl) -> Self { + CacheControl { + public: self.public && other.public, + max_age: if self.max_age == 0 { + other.max_age + } else if other.max_age == 0 { + self.max_age + } else { + self.max_age.min(other.max_age) + }, + } + } +} + pub enum Type { Scalar { name: String, @@ -89,6 +168,7 @@ pub enum Type { name: String, description: Option<&'static str>, fields: HashMap, + cache_control: CacheControl, }, Interface { name: String, @@ -201,6 +281,7 @@ impl Registry { name: "".to_string(), description: None, fields: Default::default(), + cache_control: Default::default(), }, ); let mut ty = f(self); @@ -213,6 +294,7 @@ impl Registry { args: Default::default(), ty: "String!".to_string(), deprecation: None, + cache_control: Default::default(), }, ); } diff --git a/src/types/connection/connection_type.rs b/src/types/connection/connection_type.rs index 3b7978be..1d357828 100644 --- a/src/types/connection/connection_type.rs +++ b/src/types/connection/connection_type.rs @@ -78,6 +78,7 @@ impl Type for Con args: Default::default(), ty: PageInfo::create_type_info(registry), deprecation: None, + cache_control: Default::default(), }, ); @@ -89,6 +90,7 @@ impl Type for Con args: Default::default(), ty: >>> as Type>::create_type_info(registry), deprecation: None, + cache_control: Default::default(), }, ); @@ -100,6 +102,7 @@ impl Type for Con args: Default::default(), ty: Option::::create_type_info(registry), deprecation: None, + cache_control: Default::default(), }, ); @@ -109,11 +112,13 @@ impl Type for Con description: Some(r#"A list of all of the objects returned in the connection. This is a convenience field provided for quickly exploring the API; rather than querying for "{ edges { node } }" when no edge data is needed, this field can be be used instead. Note that when clients like Relay need to fetch the "cursor" field on the edge to enable efficient pagination, this shortcut cannot be used, and the full "{ edges { node } }" version should be used instead."#), args: Default::default(), ty: Vec::::type_name().to_string(), - deprecation: None + deprecation: None, + cache_control: Default::default(), }); fields }, + cache_control: Default::default(), }) } } diff --git a/src/types/connection/edge.rs b/src/types/connection/edge.rs index 33af2cc9..62556125 100644 --- a/src/types/connection/edge.rs +++ b/src/types/connection/edge.rs @@ -47,6 +47,7 @@ where args: Default::default(), ty: T::create_type_info(registry), deprecation: None, + cache_control: Default::default(), }, ); @@ -58,12 +59,14 @@ where args: Default::default(), ty: String::create_type_info(registry), deprecation: None, + cache_control: Default::default(), }, ); fields.extend(extra_fields); fields }, + cache_control: Default::default(), } }) } diff --git a/src/types/empty_mutation.rs b/src/types/empty_mutation.rs index f0d9a9f7..52174d55 100644 --- a/src/types/empty_mutation.rs +++ b/src/types/empty_mutation.rs @@ -35,6 +35,7 @@ impl Type for EmptyMutation { name: "EmptyMutation".to_string(), description: None, fields: Default::default(), + cache_control: Default::default(), }) } } diff --git a/src/types/empty_subscription.rs b/src/types/empty_subscription.rs index f8ebbed5..2bd35dad 100644 --- a/src/types/empty_subscription.rs +++ b/src/types/empty_subscription.rs @@ -24,6 +24,7 @@ impl Type for EmptySubscription { name: "EmptySubscription".to_string(), description: None, fields: Default::default(), + cache_control: Default::default(), }) } } diff --git a/src/types/query_root.rs b/src/types/query_root.rs index 74f0724d..3b67de9c 100644 --- a/src/types/query_root.rs +++ b/src/types/query_root.rs @@ -30,6 +30,7 @@ impl Type for QueryRoot { args: Default::default(), ty: schema_type, deprecation: None, + cache_control: Default::default(), }, ); @@ -54,6 +55,7 @@ impl Type for QueryRoot { }, ty: "__Type".to_string(), deprecation: None, + cache_control: Default::default(), }, ); } diff --git a/src/validation/context.rs b/src/validation/context.rs deleted file mode 100644 index 78d111cb..00000000 --- a/src/validation/context.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::error::RuleError; -use crate::registry; -use graphql_parser::query::{Definition, Document, FragmentDefinition}; -use graphql_parser::Pos; -use std::collections::HashMap; - -pub struct ValidatorContext<'a> { - pub registry: &'a registry::Registry, - pub errors: Vec, - type_stack: Vec<&'a registry::Type>, - fragments: HashMap<&'a str, &'a FragmentDefinition>, -} - -impl<'a> ValidatorContext<'a> { - pub fn new(registry: &'a registry::Registry, doc: &'a Document) -> Self { - Self { - registry, - errors: Default::default(), - type_stack: Default::default(), - fragments: doc - .definitions - .iter() - .filter_map(|d| match d { - Definition::Fragment(fragment) => Some((fragment.name.as_str(), fragment)), - _ => None, - }) - .collect(), - } - } - - pub fn report_error>(&mut self, locations: Vec, msg: T) { - self.errors.push(RuleError { - locations, - message: msg.into(), - }) - } - - pub fn append_errors(&mut self, errors: Vec) { - self.errors.extend(errors); - } - - pub fn with_type)>( - &mut self, - ty: &'a registry::Type, - mut f: F, - ) { - self.type_stack.push(ty); - f(self); - self.type_stack.pop(); - } - - pub fn parent_type(&self) -> Option<&'a registry::Type> { - self.type_stack.get(self.type_stack.len() - 2).copied() - } - - pub fn current_type(&self) -> &'a registry::Type { - self.type_stack.last().unwrap() - } - - pub fn is_known_fragment(&self, name: &str) -> bool { - self.fragments.contains_key(name) - } - - pub fn fragment(&self, name: &str) -> Option<&'a FragmentDefinition> { - self.fragments.get(name).copied() - } -} diff --git a/src/validation/mod.rs b/src/validation/mod.rs index f7a47784..90cc437e 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -1,17 +1,14 @@ use crate::error::RuleErrors; use crate::registry::Registry; -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::{visit, VisitorNil}; +use crate::visitor::{visit, VisitorContext, VisitorNil}; use crate::Result; use graphql_parser::query::Document; -mod context; mod rules; mod utils; -mod visitor; pub fn check_rules(registry: &Registry, doc: &Document) -> Result<()> { - let mut ctx = ValidatorContext::new(registry, doc); + let mut ctx = VisitorContext::new(registry, doc); let mut visitor = VisitorNil .with(rules::ArgumentsOfCorrectType::default()) .with(rules::DefaultValuesOfCorrectType) diff --git a/src/validation/rules/arguments_of_correct_type.rs b/src/validation/rules/arguments_of_correct_type.rs index 47c98ee9..f8a9d3b4 100644 --- a/src/validation/rules/arguments_of_correct_type.rs +++ b/src/validation/rules/arguments_of_correct_type.rs @@ -1,7 +1,6 @@ use crate::registry::InputValue; -use crate::validation::context::ValidatorContext; use crate::validation::utils::is_valid_input_value; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::Field; use graphql_parser::schema::{Directive, Value}; use graphql_parser::Pos; @@ -13,7 +12,7 @@ pub struct ArgumentsOfCorrectType<'a> { } impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> { - fn enter_directive(&mut self, ctx: &mut ValidatorContext<'a>, directive: &'a Directive) { + fn enter_directive(&mut self, ctx: &mut VisitorContext<'a>, directive: &'a Directive) { self.current_args = ctx .registry .directives @@ -21,13 +20,13 @@ impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> { .map(|d| &d.args); } - fn exit_directive(&mut self, _ctx: &mut ValidatorContext<'a>, _directive: &'a Directive) { + fn exit_directive(&mut self, _ctx: &mut VisitorContext<'a>, _directive: &'a Directive) { self.current_args = None; } fn enter_argument( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, pos: Pos, name: &str, value: &'a Value, @@ -58,14 +57,14 @@ impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> { } } - fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Field) { + fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Field) { self.current_args = ctx .parent_type() .and_then(|p| p.field_by_name(&field.name)) .map(|f| &f.args); } - fn exit_field(&mut self, _ctx: &mut ValidatorContext<'a>, _field: &'a Field) { + fn exit_field(&mut self, _ctx: &mut VisitorContext<'a>, _field: &'a Field) { self.current_args = None; } } diff --git a/src/validation/rules/default_values_of_correct_type.rs b/src/validation/rules/default_values_of_correct_type.rs index 7873c696..cfc8accd 100644 --- a/src/validation/rules/default_values_of_correct_type.rs +++ b/src/validation/rules/default_values_of_correct_type.rs @@ -1,6 +1,5 @@ -use crate::validation::context::ValidatorContext; use crate::validation::utils::is_valid_input_value; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::{Type, VariableDefinition}; pub struct DefaultValuesOfCorrectType; @@ -8,7 +7,7 @@ pub struct DefaultValuesOfCorrectType; impl<'a> Visitor<'a> for DefaultValuesOfCorrectType { fn enter_variable_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, variable_definition: &'a VariableDefinition, ) { if let Some(value) = &variable_definition.default_value { diff --git a/src/validation/rules/fields_on_correct_type.rs b/src/validation/rules/fields_on_correct_type.rs index b410a024..611aa237 100644 --- a/src/validation/rules/fields_on_correct_type.rs +++ b/src/validation/rules/fields_on_correct_type.rs @@ -1,13 +1,12 @@ use crate::registry; -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::Field; #[derive(Default)] pub struct FieldsOnCorrectType; impl<'a> Visitor<'a> for FieldsOnCorrectType { - fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Field) { + fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Field) { if ctx .parent_type() .unwrap() diff --git a/src/validation/rules/fragments_on_composite_types.rs b/src/validation/rules/fragments_on_composite_types.rs index 759dbfa7..2367084d 100644 --- a/src/validation/rules/fragments_on_composite_types.rs +++ b/src/validation/rules/fragments_on_composite_types.rs @@ -1,5 +1,4 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::{FragmentDefinition, InlineFragment, TypeCondition}; #[derive(Default)] @@ -8,7 +7,7 @@ pub struct FragmentsOnCompositeTypes; impl<'a> Visitor<'a> for FragmentsOnCompositeTypes { fn enter_fragment_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, fragment_definition: &'a FragmentDefinition, ) { if !ctx.current_type().is_composite() { @@ -25,7 +24,7 @@ impl<'a> Visitor<'a> for FragmentsOnCompositeTypes { fn enter_inline_fragment( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, inline_fragment: &'a InlineFragment, ) { if !ctx.current_type().is_composite() { diff --git a/src/validation/rules/known_argument_names.rs b/src/validation/rules/known_argument_names.rs index 0d31a716..7d373fa3 100644 --- a/src/validation/rules/known_argument_names.rs +++ b/src/validation/rules/known_argument_names.rs @@ -1,6 +1,5 @@ use crate::registry::InputValue; -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use crate::Value; use graphql_parser::query::{Directive, Field}; use graphql_parser::Pos; @@ -20,7 +19,7 @@ pub struct KnownArgumentNames<'a> { } impl<'a> Visitor<'a> for KnownArgumentNames<'a> { - fn enter_directive(&mut self, ctx: &mut ValidatorContext<'a>, directive: &'a Directive) { + fn enter_directive(&mut self, ctx: &mut VisitorContext<'a>, directive: &'a Directive) { self.current_args = ctx .registry .directives @@ -28,13 +27,13 @@ impl<'a> Visitor<'a> for KnownArgumentNames<'a> { .map(|d| (&d.args, ArgsType::Directive(&directive.name))); } - fn exit_directive(&mut self, _ctx: &mut ValidatorContext<'a>, _directive: &'a Directive) { + fn exit_directive(&mut self, _ctx: &mut VisitorContext<'a>, _directive: &'a Directive) { self.current_args = None; } fn enter_argument( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, pos: Pos, name: &str, _value: &'a Value, @@ -68,7 +67,7 @@ impl<'a> Visitor<'a> for KnownArgumentNames<'a> { } } - fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Field) { + fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Field) { self.current_args = ctx .parent_type() .and_then(|p| p.field_by_name(&field.name)) @@ -83,7 +82,7 @@ impl<'a> Visitor<'a> for KnownArgumentNames<'a> { }); } - fn exit_field(&mut self, _ctx: &mut ValidatorContext<'a>, _field: &'a Field) { + fn exit_field(&mut self, _ctx: &mut VisitorContext<'a>, _field: &'a Field) { self.current_args = None; } } diff --git a/src/validation/rules/known_directives.rs b/src/validation/rules/known_directives.rs index 85ba5032..6e86a309 100644 --- a/src/validation/rules/known_directives.rs +++ b/src/validation/rules/known_directives.rs @@ -1,6 +1,5 @@ use crate::model::__DirectiveLocation; -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::{ Field, FragmentDefinition, FragmentSpread, InlineFragment, OperationDefinition, }; @@ -14,7 +13,7 @@ pub struct KnownDirectives { impl<'a> Visitor<'a> for KnownDirectives { fn enter_operation_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, operation_definition: &'a OperationDefinition, ) { self.location_stack.push(match operation_definition { @@ -28,7 +27,7 @@ impl<'a> Visitor<'a> for KnownDirectives { fn exit_operation_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _operation_definition: &'a OperationDefinition, ) { self.location_stack.pop(); @@ -36,7 +35,7 @@ impl<'a> Visitor<'a> for KnownDirectives { fn enter_fragment_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _fragment_definition: &'a FragmentDefinition, ) { self.location_stack @@ -45,13 +44,13 @@ impl<'a> Visitor<'a> for KnownDirectives { fn exit_fragment_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _fragment_definition: &'a FragmentDefinition, ) { self.location_stack.pop(); } - fn enter_directive(&mut self, ctx: &mut ValidatorContext<'a>, directive: &'a Directive) { + fn enter_directive(&mut self, ctx: &mut VisitorContext<'a>, directive: &'a Directive) { if let Some(schema_directive) = ctx.registry.directives.get(directive.name.as_str()) { if let Some(current_location) = self.location_stack.last() { if !schema_directive.locations.contains(current_location) { @@ -72,17 +71,17 @@ impl<'a> Visitor<'a> for KnownDirectives { } } - fn enter_field(&mut self, _ctx: &mut ValidatorContext<'a>, _field: &'a Field) { + fn enter_field(&mut self, _ctx: &mut VisitorContext<'a>, _field: &'a Field) { self.location_stack.push(__DirectiveLocation::FIELD); } - fn exit_field(&mut self, _ctx: &mut ValidatorContext<'a>, _field: &'a Field) { + fn exit_field(&mut self, _ctx: &mut VisitorContext<'a>, _field: &'a Field) { self.location_stack.pop(); } fn enter_fragment_spread( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _fragment_spread: &'a FragmentSpread, ) { self.location_stack @@ -91,7 +90,7 @@ impl<'a> Visitor<'a> for KnownDirectives { fn exit_fragment_spread( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _fragment_spread: &'a FragmentSpread, ) { self.location_stack.pop(); @@ -99,7 +98,7 @@ impl<'a> Visitor<'a> for KnownDirectives { fn enter_inline_fragment( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _inline_fragment: &'a InlineFragment, ) { self.location_stack @@ -108,7 +107,7 @@ impl<'a> Visitor<'a> for KnownDirectives { fn exit_inline_fragment( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _inline_fragment: &'a InlineFragment, ) { self.location_stack.pop(); diff --git a/src/validation/rules/known_fragment_names.rs b/src/validation/rules/known_fragment_names.rs index 0fad66c8..34acb835 100644 --- a/src/validation/rules/known_fragment_names.rs +++ b/src/validation/rules/known_fragment_names.rs @@ -1,5 +1,4 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::FragmentSpread; #[derive(Default)] @@ -8,7 +7,7 @@ pub struct KnownFragmentNames; impl<'a> Visitor<'a> for KnownFragmentNames { fn enter_fragment_spread( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, fragment_spread: &'a FragmentSpread, ) { if !ctx.is_known_fragment(&fragment_spread.fragment_name) { diff --git a/src/validation/rules/known_type_names.rs b/src/validation/rules/known_type_names.rs index 85f5aafe..c17befad 100644 --- a/src/validation/rules/known_type_names.rs +++ b/src/validation/rules/known_type_names.rs @@ -1,6 +1,5 @@ use crate::registry::TypeName; -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::{ FragmentDefinition, InlineFragment, TypeCondition, VariableDefinition, }; @@ -12,7 +11,7 @@ pub struct KnownTypeNames; impl<'a> Visitor<'a> for KnownTypeNames { fn enter_fragment_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, fragment_definition: &'a FragmentDefinition, ) { let TypeCondition::On(name) = &fragment_definition.type_condition; @@ -21,7 +20,7 @@ impl<'a> Visitor<'a> for KnownTypeNames { fn enter_variable_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, variable_definition: &'a VariableDefinition, ) { validate_type( @@ -33,7 +32,7 @@ impl<'a> Visitor<'a> for KnownTypeNames { fn enter_inline_fragment( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, inline_fragment: &'a InlineFragment, ) { if let Some(TypeCondition::On(name)) = &inline_fragment.type_condition { @@ -42,7 +41,7 @@ impl<'a> Visitor<'a> for KnownTypeNames { } } -fn validate_type(ctx: &mut ValidatorContext<'_>, type_name: &str, pos: 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)); } diff --git a/src/validation/rules/lone_anonymous_operation.rs b/src/validation/rules/lone_anonymous_operation.rs index 6a9fb337..21a2fbf8 100644 --- a/src/validation/rules/lone_anonymous_operation.rs +++ b/src/validation/rules/lone_anonymous_operation.rs @@ -1,5 +1,4 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::{Definition, Document, OperationDefinition}; #[derive(Default)] @@ -8,7 +7,7 @@ pub struct LoneAnonymousOperation { } impl<'a> Visitor<'a> for LoneAnonymousOperation { - fn enter_document(&mut self, _ctx: &mut ValidatorContext<'a>, doc: &'a Document) { + fn enter_document(&mut self, _ctx: &mut VisitorContext<'a>, doc: &'a Document) { self.operation_count = Some( doc.definitions .iter() @@ -22,7 +21,7 @@ impl<'a> Visitor<'a> for LoneAnonymousOperation { fn enter_operation_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, operation_definition: &'a OperationDefinition, ) { if let Some(operation_count) = self.operation_count { diff --git a/src/validation/rules/no_compose_leafs.rs b/src/validation/rules/no_compose_leafs.rs index 9cf420e1..e64b1722 100644 --- a/src/validation/rules/no_compose_leafs.rs +++ b/src/validation/rules/no_compose_leafs.rs @@ -1,12 +1,11 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::Field; #[derive(Default)] pub struct NoComposeLeafs; impl<'a> Visitor<'a> for NoComposeLeafs { - fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Field) { + fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Field) { if let Some(ty) = ctx.parent_type() { if let Some(schema_field) = ty.field_by_name(&field.name) { if let Some(ty) = ctx.registry.basic_type_by_typename(&schema_field.ty) { diff --git a/src/validation/rules/no_fragment_cycles.rs b/src/validation/rules/no_fragment_cycles.rs index 590fb565..14e73597 100644 --- a/src/validation/rules/no_fragment_cycles.rs +++ b/src/validation/rules/no_fragment_cycles.rs @@ -1,6 +1,5 @@ use crate::error::RuleError; -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::{Document, FragmentDefinition, FragmentSpread}; use graphql_parser::Pos; use std::collections::{HashMap, HashSet}; @@ -55,7 +54,7 @@ pub struct NoFragmentCycles<'a> { } impl<'a> Visitor<'a> for NoFragmentCycles<'a> { - fn exit_document(&mut self, ctx: &mut ValidatorContext<'a>, _doc: &'a Document) { + fn exit_document(&mut self, ctx: &mut VisitorContext<'a>, _doc: &'a Document) { let mut detector = CycleDetector { visited: HashSet::new(), spreads: &self.spreads, @@ -75,7 +74,7 @@ impl<'a> Visitor<'a> for NoFragmentCycles<'a> { fn enter_fragment_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, fragment_definition: &'a FragmentDefinition, ) { self.current_fragment = Some(&fragment_definition.name); @@ -84,7 +83,7 @@ impl<'a> Visitor<'a> for NoFragmentCycles<'a> { fn exit_fragment_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _fragment_definition: &'a FragmentDefinition, ) { self.current_fragment = None; @@ -92,7 +91,7 @@ impl<'a> Visitor<'a> for NoFragmentCycles<'a> { fn enter_fragment_spread( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, fragment_spread: &'a FragmentSpread, ) { if let Some(current_fragment) = self.current_fragment { diff --git a/src/validation/rules/no_undefined_variables.rs b/src/validation/rules/no_undefined_variables.rs index 1c52fbfc..f211eafd 100644 --- a/src/validation/rules/no_undefined_variables.rs +++ b/src/validation/rules/no_undefined_variables.rs @@ -1,5 +1,4 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::{OperationDefinition, VariableDefinition}; use graphql_parser::schema::Value; use graphql_parser::Pos; @@ -13,7 +12,7 @@ pub struct NoUndefinedVariables<'a> { impl<'a> Visitor<'a> for NoUndefinedVariables<'a> { fn enter_operation_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _operation_definition: &'a OperationDefinition, ) { self.vars.clear(); @@ -21,7 +20,7 @@ impl<'a> Visitor<'a> for NoUndefinedVariables<'a> { fn enter_variable_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, variable_definition: &'a VariableDefinition, ) { self.vars.insert(&variable_definition.name); @@ -29,7 +28,7 @@ impl<'a> Visitor<'a> for NoUndefinedVariables<'a> { fn enter_argument( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, pos: Pos, _name: &str, value: &'a Value, diff --git a/src/validation/rules/no_unused_fragments.rs b/src/validation/rules/no_unused_fragments.rs index d6115570..d4888b88 100644 --- a/src/validation/rules/no_unused_fragments.rs +++ b/src/validation/rules/no_unused_fragments.rs @@ -1,5 +1,4 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::{Definition, Document, FragmentSpread}; use std::collections::HashSet; @@ -9,7 +8,7 @@ pub struct NoUnusedFragments<'a> { } impl<'a> Visitor<'a> for NoUnusedFragments<'a> { - fn exit_document(&mut self, ctx: &mut ValidatorContext<'a>, doc: &'a Document) { + fn exit_document(&mut self, ctx: &mut VisitorContext<'a>, doc: &'a Document) { for d in &doc.definitions { if let Definition::Fragment(fragment) = d { if !self.spreads.contains(fragment.name.as_str()) { @@ -24,7 +23,7 @@ impl<'a> Visitor<'a> for NoUnusedFragments<'a> { fn enter_fragment_spread( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, fragment_spread: &'a FragmentSpread, ) { self.spreads.insert(&fragment_spread.fragment_name); diff --git a/src/validation/rules/no_unused_variables.rs b/src/validation/rules/no_unused_variables.rs index 86837e30..4079a5e5 100644 --- a/src/validation/rules/no_unused_variables.rs +++ b/src/validation/rules/no_unused_variables.rs @@ -1,5 +1,4 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::{OperationDefinition, VariableDefinition}; use graphql_parser::schema::Value; use graphql_parser::Pos; @@ -35,7 +34,7 @@ impl<'a> NoUnusedVariables<'a> { impl<'a> Visitor<'a> for NoUnusedVariables<'a> { fn enter_operation_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _operation_definition: &'a OperationDefinition, ) { self.used_vars.clear(); @@ -44,7 +43,7 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> { fn exit_operation_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, _operation_definition: &'a OperationDefinition, ) { for (name, pos) in &self.vars { @@ -56,7 +55,7 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> { fn enter_variable_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, variable_definition: &'a VariableDefinition, ) { self.vars @@ -65,7 +64,7 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> { fn enter_argument( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _pos: Pos, _name: &'a str, value: &'a Value, diff --git a/src/validation/rules/overlapping_fields_can_be_merged.rs b/src/validation/rules/overlapping_fields_can_be_merged.rs index 2a1c065b..c66f6897 100644 --- a/src/validation/rules/overlapping_fields_can_be_merged.rs +++ b/src/validation/rules/overlapping_fields_can_be_merged.rs @@ -1,5 +1,4 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::{Field, Selection, SelectionSet}; use std::collections::HashMap; @@ -9,7 +8,7 @@ pub struct OverlappingFieldsCanBeMerged; impl<'a> Visitor<'a> for OverlappingFieldsCanBeMerged { fn enter_selection_set( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, selection_set: &'a SelectionSet, ) { let mut find_conflicts = FindConflicts { @@ -22,7 +21,7 @@ impl<'a> Visitor<'a> for OverlappingFieldsCanBeMerged { struct FindConflicts<'a, 'ctx> { outputs: HashMap<&'a str, &'a Field>, - ctx: &'a mut ValidatorContext<'ctx>, + ctx: &'a mut VisitorContext<'ctx>, } impl<'a, 'ctx> FindConflicts<'a, 'ctx> { diff --git a/src/validation/rules/possible_fragment_spreads.rs b/src/validation/rules/possible_fragment_spreads.rs index 188cd403..f41a944d 100644 --- a/src/validation/rules/possible_fragment_spreads.rs +++ b/src/validation/rules/possible_fragment_spreads.rs @@ -1,5 +1,4 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::{Definition, Document, FragmentSpread, InlineFragment, TypeCondition}; use std::collections::HashMap; @@ -9,7 +8,7 @@ pub struct PossibleFragmentSpreads<'a> { } impl<'a> Visitor<'a> for PossibleFragmentSpreads<'a> { - fn enter_document(&mut self, _ctx: &mut ValidatorContext<'a>, doc: &'a Document) { + fn enter_document(&mut self, _ctx: &mut VisitorContext<'a>, doc: &'a Document) { for d in &doc.definitions { if let Definition::Fragment(fragment) = d { let TypeCondition::On(type_name) = &fragment.type_condition; @@ -21,7 +20,7 @@ impl<'a> Visitor<'a> for PossibleFragmentSpreads<'a> { fn enter_fragment_spread( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, fragment_spread: &'a FragmentSpread, ) { if let Some(fragment_type) = self @@ -42,7 +41,7 @@ impl<'a> Visitor<'a> for PossibleFragmentSpreads<'a> { fn enter_inline_fragment( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, inline_fragment: &'a InlineFragment, ) { if let Some(parent_type) = ctx.parent_type() { diff --git a/src/validation/rules/provided_non_null_arguments.rs b/src/validation/rules/provided_non_null_arguments.rs index 25115585..bced88e2 100644 --- a/src/validation/rules/provided_non_null_arguments.rs +++ b/src/validation/rules/provided_non_null_arguments.rs @@ -1,6 +1,5 @@ use crate::registry::TypeName; -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::Field; use graphql_parser::schema::Directive; @@ -8,7 +7,7 @@ use graphql_parser::schema::Directive; pub struct ProvidedNonNullArguments; impl<'a> Visitor<'a> for ProvidedNonNullArguments { - fn enter_directive(&mut self, ctx: &mut ValidatorContext<'a>, directive: &'a Directive) { + fn enter_directive(&mut self, ctx: &mut VisitorContext<'a>, directive: &'a Directive) { if let Some(schema_directive) = ctx.registry.directives.get(&directive.name) { for arg in schema_directive.args.values() { if TypeName::create(&arg.ty).is_non_null() @@ -29,7 +28,7 @@ impl<'a> Visitor<'a> for ProvidedNonNullArguments { } } - fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Field) { + fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Field) { if let Some(parent_type) = ctx.parent_type() { if let Some(schema_field) = parent_type.field_by_name(&field.name) { for arg in schema_field.args.values() { diff --git a/src/validation/rules/scalar_leafs.rs b/src/validation/rules/scalar_leafs.rs index a692dd87..19697a43 100644 --- a/src/validation/rules/scalar_leafs.rs +++ b/src/validation/rules/scalar_leafs.rs @@ -1,12 +1,11 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::Field; #[derive(Default)] pub struct ScalarLeafs; impl<'a> Visitor<'a> for ScalarLeafs { - fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Field) { + fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Field) { if let Some(ty) = ctx.parent_type() { if let Some(schema_field) = ty.field_by_name(&field.name) { if let Some(ty) = ctx.registry.basic_type_by_typename(&schema_field.ty) { diff --git a/src/validation/rules/unique_argument_names.rs b/src/validation/rules/unique_argument_names.rs index 4700cca2..68ff3124 100644 --- a/src/validation/rules/unique_argument_names.rs +++ b/src/validation/rules/unique_argument_names.rs @@ -1,5 +1,4 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::Field; use graphql_parser::schema::{Directive, Value}; use graphql_parser::Pos; @@ -11,13 +10,13 @@ pub struct UniqueArgumentNames<'a> { } impl<'a> Visitor<'a> for UniqueArgumentNames<'a> { - fn enter_directive(&mut self, _ctx: &mut ValidatorContext<'a>, _directive: &'a Directive) { + fn enter_directive(&mut self, _ctx: &mut VisitorContext<'a>, _directive: &'a Directive) { self.names.clear(); } fn enter_argument( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, pos: Pos, name: &'a str, _value: &'a Value, @@ -30,7 +29,7 @@ impl<'a> Visitor<'a> for UniqueArgumentNames<'a> { } } - fn enter_field(&mut self, _ctx: &mut ValidatorContext<'a>, _field: &'a Field) { + fn enter_field(&mut self, _ctx: &mut VisitorContext<'a>, _field: &'a Field) { self.names.clear(); } } diff --git a/src/validation/rules/unique_fragment_names.rs b/src/validation/rules/unique_fragment_names.rs index c8b1bc39..ad3673bf 100644 --- a/src/validation/rules/unique_fragment_names.rs +++ b/src/validation/rules/unique_fragment_names.rs @@ -1,5 +1,4 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::FragmentDefinition; use std::collections::HashSet; @@ -11,7 +10,7 @@ pub struct UniqueFragmentNames<'a> { impl<'a> Visitor<'a> for UniqueFragmentNames<'a> { fn enter_fragment_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, fragment_definition: &'a FragmentDefinition, ) { if !self.names.insert(&fragment_definition.name) { diff --git a/src/validation/rules/unique_operation_names.rs b/src/validation/rules/unique_operation_names.rs index 226d3aff..fbb55836 100644 --- a/src/validation/rules/unique_operation_names.rs +++ b/src/validation/rules/unique_operation_names.rs @@ -1,5 +1,4 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::{Mutation, OperationDefinition, Query, Subscription}; use std::collections::HashSet; @@ -11,7 +10,7 @@ pub struct UniqueOperationNames<'a> { impl<'a> Visitor<'a> for UniqueOperationNames<'a> { fn enter_operation_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, operation_definition: &'a OperationDefinition, ) { let name = match operation_definition { diff --git a/src/validation/rules/unique_variable_names.rs b/src/validation/rules/unique_variable_names.rs index 827c9fec..229d8d6d 100644 --- a/src/validation/rules/unique_variable_names.rs +++ b/src/validation/rules/unique_variable_names.rs @@ -1,5 +1,4 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::{OperationDefinition, VariableDefinition}; use std::collections::HashSet; @@ -11,7 +10,7 @@ pub struct UniqueVariableNames<'a> { impl<'a> Visitor<'a> for UniqueVariableNames<'a> { fn enter_operation_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _operation_definition: &'a OperationDefinition, ) { self.names.clear(); @@ -19,7 +18,7 @@ impl<'a> Visitor<'a> for UniqueVariableNames<'a> { fn enter_variable_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, variable_definition: &'a VariableDefinition, ) { if !self.names.insert(variable_definition.name.as_str()) { diff --git a/src/validation/rules/upload_file.rs b/src/validation/rules/upload_file.rs index df7ee78f..2c67ab71 100644 --- a/src/validation/rules/upload_file.rs +++ b/src/validation/rules/upload_file.rs @@ -1,5 +1,4 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::OperationDefinition; #[derive(Default)] @@ -8,7 +7,7 @@ pub struct UploadFile; impl<'a> Visitor<'a> for UploadFile { fn enter_operation_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, operation_definition: &'a OperationDefinition, ) { if let OperationDefinition::Query(query) = operation_definition { diff --git a/src/validation/rules/variables_are_input_types.rs b/src/validation/rules/variables_are_input_types.rs index 8a0283e3..5ae445f8 100644 --- a/src/validation/rules/variables_are_input_types.rs +++ b/src/validation/rules/variables_are_input_types.rs @@ -1,5 +1,4 @@ -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use graphql_parser::query::VariableDefinition; #[derive(Default)] @@ -8,7 +7,7 @@ pub struct VariablesAreInputTypes; impl<'a> Visitor<'a> for VariablesAreInputTypes { fn enter_variable_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, variable_definition: &'a VariableDefinition, ) { if let Some(ty) = ctx diff --git a/src/validation/rules/variables_in_allowed_position.rs b/src/validation/rules/variables_in_allowed_position.rs index d4acb3d1..cf4ba29d 100644 --- a/src/validation/rules/variables_in_allowed_position.rs +++ b/src/validation/rules/variables_in_allowed_position.rs @@ -1,6 +1,5 @@ use crate::registry::TypeName; -use crate::validation::context::ValidatorContext; -use crate::validation::visitor::Visitor; +use crate::visitor::{Visitor, VisitorContext}; use crate::Value; use graphql_parser::query::{Field, OperationDefinition, VariableDefinition}; use graphql_parser::schema::Directive; @@ -15,7 +14,7 @@ pub struct VariableInAllowedPosition<'a> { impl<'a> VariableInAllowedPosition<'a> { fn check_type( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, pos: Pos, except_type: &str, value: &Value, @@ -51,7 +50,7 @@ impl<'a> VariableInAllowedPosition<'a> { impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> { fn enter_operation_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _operation_definition: &'a OperationDefinition, ) { self.var_types.clear(); @@ -59,7 +58,7 @@ impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> { fn enter_variable_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, variable_definition: &'a VariableDefinition, ) { self.var_types.insert( @@ -68,7 +67,7 @@ impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> { ); } - fn enter_directive(&mut self, ctx: &mut ValidatorContext<'a>, directive: &'a Directive) { + fn enter_directive(&mut self, ctx: &mut VisitorContext<'a>, directive: &'a Directive) { if let Some(schema_directive) = ctx.registry.directives.get(directive.name.as_str()) { for (name, value) in &directive.arguments { if let Some(input) = schema_directive.args.get(name.as_str()) { @@ -78,7 +77,7 @@ impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> { } } - fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Field) { + fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Field) { if let Some(parent_type) = ctx.parent_type() { if let Some(schema_field) = parent_type.field_by_name(&field.name) { for (name, value) in &field.arguments { diff --git a/src/validation/visitor.rs b/src/visitor.rs similarity index 73% rename from src/validation/visitor.rs rename to src/visitor.rs index 48ee386f..2bed7694 100644 --- a/src/validation/visitor.rs +++ b/src/visitor.rs @@ -1,59 +1,127 @@ -use crate::validation::context::ValidatorContext; +use crate::error::RuleError; +use crate::registry; use graphql_parser::query::{ Definition, Directive, Document, Field, FragmentDefinition, FragmentSpread, InlineFragment, Name, OperationDefinition, Selection, SelectionSet, TypeCondition, Value, VariableDefinition, }; use graphql_parser::Pos; +use std::collections::HashMap; + +pub struct VisitorContext<'a> { + pub registry: &'a registry::Registry, + pub errors: Vec, + type_stack: Vec<&'a registry::Type>, + fragments: HashMap<&'a str, &'a FragmentDefinition>, +} + +impl<'a> VisitorContext<'a> { + pub fn new(registry: &'a registry::Registry, doc: &'a Document) -> Self { + Self { + registry, + errors: Default::default(), + type_stack: Default::default(), + fragments: doc + .definitions + .iter() + .filter_map(|d| match d { + Definition::Fragment(fragment) => Some((fragment.name.as_str(), fragment)), + _ => None, + }) + .collect(), + } + } + + pub fn report_error>(&mut self, locations: Vec, msg: T) { + self.errors.push(RuleError { + locations, + message: msg.into(), + }) + } + + pub fn append_errors(&mut self, errors: Vec) { + self.errors.extend(errors); + } + + pub fn with_type)>( + &mut self, + ty: &'a registry::Type, + mut f: F, + ) { + self.type_stack.push(ty); + f(self); + self.type_stack.pop(); + } + + pub fn parent_type(&self) -> Option<&'a registry::Type> { + if self.type_stack.len() >= 2 { + self.type_stack.get(self.type_stack.len() - 2).copied() + } else { + None + } + } + + pub fn current_type(&self) -> &'a registry::Type { + self.type_stack.last().unwrap() + } + + pub fn is_known_fragment(&self, name: &str) -> bool { + self.fragments.contains_key(name) + } + + pub fn fragment(&self, name: &str) -> Option<&'a FragmentDefinition> { + self.fragments.get(name).copied() + } +} pub trait Visitor<'a> { - fn enter_document(&mut self, _ctx: &mut ValidatorContext<'a>, _doc: &'a Document) {} - fn exit_document(&mut self, _ctx: &mut ValidatorContext<'a>, _doc: &'a Document) {} + fn enter_document(&mut self, _ctx: &mut VisitorContext<'a>, _doc: &'a Document) {} + fn exit_document(&mut self, _ctx: &mut VisitorContext<'a>, _doc: &'a Document) {} fn enter_operation_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _operation_definition: &'a OperationDefinition, ) { } fn exit_operation_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _operation_definition: &'a OperationDefinition, ) { } fn enter_fragment_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _fragment_definition: &'a FragmentDefinition, ) { } fn exit_fragment_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _fragment_definition: &'a FragmentDefinition, ) { } fn enter_variable_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _variable_definition: &'a VariableDefinition, ) { } fn exit_variable_definition( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _variable_definition: &'a VariableDefinition, ) { } - fn enter_directive(&mut self, _ctx: &mut ValidatorContext<'a>, _directive: &'a Directive) {} - fn exit_directive(&mut self, _ctx: &mut ValidatorContext<'a>, _directive: &'a Directive) {} + fn enter_directive(&mut self, _ctx: &mut VisitorContext<'a>, _directive: &'a Directive) {} + fn exit_directive(&mut self, _ctx: &mut VisitorContext<'a>, _directive: &'a Directive) {} fn enter_argument( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _pos: Pos, _name: &'a str, _value: &'a Value, @@ -61,7 +129,7 @@ pub trait Visitor<'a> { } fn exit_argument( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _pos: Pos, _name: &'a str, _value: &'a Value, @@ -70,45 +138,45 @@ pub trait Visitor<'a> { fn enter_selection_set( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _selection_set: &'a SelectionSet, ) { } fn exit_selection_set( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _selection_set: &'a SelectionSet, ) { } - fn enter_selection(&mut self, _ctx: &mut ValidatorContext<'a>, _selection: &'a Selection) {} - fn exit_selection(&mut self, _ctx: &mut ValidatorContext<'a>, _selection: &'a Selection) {} + fn enter_selection(&mut self, _ctx: &mut VisitorContext<'a>, _selection: &'a Selection) {} + fn exit_selection(&mut self, _ctx: &mut VisitorContext<'a>, _selection: &'a Selection) {} - fn enter_field(&mut self, _ctx: &mut ValidatorContext<'a>, _field: &'a Field) {} - fn exit_field(&mut self, _ctx: &mut ValidatorContext<'a>, _field: &'a Field) {} + fn enter_field(&mut self, _ctx: &mut VisitorContext<'a>, _field: &'a Field) {} + fn exit_field(&mut self, _ctx: &mut VisitorContext<'a>, _field: &'a Field) {} fn enter_fragment_spread( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _fragment_spread: &'a FragmentSpread, ) { } fn exit_fragment_spread( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _fragment_spread: &'a FragmentSpread, ) { } fn enter_inline_fragment( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _inline_fragment: &'a InlineFragment, ) { } fn exit_inline_fragment( &mut self, - _ctx: &mut ValidatorContext<'a>, + _ctx: &mut VisitorContext<'a>, _inline_fragment: &'a InlineFragment, ) { } @@ -137,19 +205,19 @@ where A: Visitor<'a> + 'a, B: Visitor<'a> + 'a, { - fn enter_document(&mut self, ctx: &mut ValidatorContext<'a>, doc: &'a Document) { + fn enter_document(&mut self, ctx: &mut VisitorContext<'a>, doc: &'a Document) { self.0.enter_document(ctx, doc); self.1.enter_document(ctx, doc); } - fn exit_document(&mut self, ctx: &mut ValidatorContext<'a>, doc: &'a Document) { + fn exit_document(&mut self, ctx: &mut VisitorContext<'a>, doc: &'a Document) { self.0.exit_document(ctx, doc); self.1.exit_document(ctx, doc); } fn enter_operation_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, operation_definition: &'a OperationDefinition, ) { self.0.enter_operation_definition(ctx, operation_definition); @@ -158,7 +226,7 @@ where fn exit_operation_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, operation_definition: &'a OperationDefinition, ) { self.0.exit_operation_definition(ctx, operation_definition); @@ -167,7 +235,7 @@ where fn enter_fragment_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, fragment_definition: &'a FragmentDefinition, ) { self.0.enter_fragment_definition(ctx, fragment_definition); @@ -176,7 +244,7 @@ where fn exit_fragment_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, fragment_definition: &'a FragmentDefinition, ) { self.0.exit_fragment_definition(ctx, fragment_definition); @@ -185,7 +253,7 @@ where fn enter_variable_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, variable_definition: &'a VariableDefinition, ) { self.0.enter_variable_definition(ctx, variable_definition); @@ -194,26 +262,26 @@ where fn exit_variable_definition( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, variable_definition: &'a VariableDefinition, ) { self.0.exit_variable_definition(ctx, variable_definition); self.1.exit_variable_definition(ctx, variable_definition); } - fn enter_directive(&mut self, ctx: &mut ValidatorContext<'a>, directive: &'a Directive) { + fn enter_directive(&mut self, ctx: &mut VisitorContext<'a>, directive: &'a Directive) { self.0.enter_directive(ctx, directive); self.1.enter_directive(ctx, directive); } - fn exit_directive(&mut self, ctx: &mut ValidatorContext<'a>, directive: &'a Directive) { + fn exit_directive(&mut self, ctx: &mut VisitorContext<'a>, directive: &'a Directive) { self.0.exit_directive(ctx, directive); self.1.exit_directive(ctx, directive); } fn enter_argument( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, pos: Pos, name: &'a str, value: &'a Value, @@ -224,7 +292,7 @@ where fn exit_argument( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, pos: Pos, name: &'a str, value: &'a Value, @@ -235,7 +303,7 @@ where fn enter_selection_set( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, selection_set: &'a SelectionSet, ) { self.0.enter_selection_set(ctx, selection_set); @@ -244,36 +312,36 @@ where fn exit_selection_set( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, selection_set: &'a SelectionSet, ) { self.0.exit_selection_set(ctx, selection_set); self.1.exit_selection_set(ctx, selection_set); } - fn enter_selection(&mut self, ctx: &mut ValidatorContext<'a>, selection: &'a Selection) { + fn enter_selection(&mut self, ctx: &mut VisitorContext<'a>, selection: &'a Selection) { self.0.enter_selection(ctx, selection); self.1.enter_selection(ctx, selection); } - fn exit_selection(&mut self, ctx: &mut ValidatorContext<'a>, selection: &'a Selection) { + fn exit_selection(&mut self, ctx: &mut VisitorContext<'a>, selection: &'a Selection) { self.0.exit_selection(ctx, selection); self.1.exit_selection(ctx, selection); } - fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Field) { + fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Field) { self.0.enter_field(ctx, field); self.1.enter_field(ctx, field); } - fn exit_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Field) { + fn exit_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Field) { self.0.exit_field(ctx, field); self.1.exit_field(ctx, field); } fn enter_fragment_spread( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, fragment_spread: &'a FragmentSpread, ) { self.0.enter_fragment_spread(ctx, fragment_spread); @@ -282,7 +350,7 @@ where fn exit_fragment_spread( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, fragment_spread: &'a FragmentSpread, ) { self.0.exit_fragment_spread(ctx, fragment_spread); @@ -291,7 +359,7 @@ where fn enter_inline_fragment( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, inline_fragment: &'a InlineFragment, ) { self.0.enter_inline_fragment(ctx, inline_fragment); @@ -300,7 +368,7 @@ where fn exit_inline_fragment( &mut self, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, inline_fragment: &'a InlineFragment, ) { self.0.exit_inline_fragment(ctx, inline_fragment); @@ -308,7 +376,7 @@ where } } -pub fn visit<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, doc: &'a Document) { +pub fn visit<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut VisitorContext<'a>, doc: &'a Document) { v.enter_document(ctx, doc); visit_definitions(v, ctx, doc); v.exit_document(ctx, doc); @@ -316,7 +384,7 @@ pub fn visit<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, doc: fn visit_definitions<'a, V: Visitor<'a>>( v: &mut V, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, doc: &'a Document, ) { for d in &doc.definitions { @@ -341,7 +409,7 @@ fn visit_definitions<'a, V: Visitor<'a>>( fn visit_operation_definition<'a, V: Visitor<'a>>( v: &mut V, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, operation: &'a OperationDefinition, ) { v.enter_operation_definition(ctx, operation); @@ -392,7 +460,7 @@ fn visit_operation_definition<'a, V: Visitor<'a>>( fn visit_selection_set<'a, V: Visitor<'a>>( v: &mut V, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, selection_set: &'a SelectionSet, ) { v.enter_selection_set(ctx, selection_set); @@ -404,7 +472,7 @@ fn visit_selection_set<'a, V: Visitor<'a>>( fn visit_selection<'a, V: Visitor<'a>>( v: &mut V, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, selection: &'a Selection, ) { v.enter_selection(ctx, selection); @@ -449,7 +517,7 @@ fn visit_selection<'a, V: Visitor<'a>>( v.exit_selection(ctx, selection); } -fn visit_field<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, field: &'a Field) { +fn visit_field<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut VisitorContext<'a>, field: &'a Field) { v.enter_field(ctx, field); visit_arguments(v, ctx, field.position, &field.arguments); visit_directives(v, ctx, &field.directives); @@ -459,7 +527,7 @@ fn visit_field<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, fi fn visit_arguments<'a, V: Visitor<'a>>( v: &mut V, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, pos: Pos, arguments: &'a [(Name, Value)], ) { @@ -471,7 +539,7 @@ fn visit_arguments<'a, V: Visitor<'a>>( fn visit_variable_definitions<'a, V: Visitor<'a>>( v: &mut V, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, variable_definitions: &'a [VariableDefinition], ) { for d in variable_definitions { @@ -482,7 +550,7 @@ fn visit_variable_definitions<'a, V: Visitor<'a>>( fn visit_directives<'a, V: Visitor<'a>>( v: &mut V, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, directives: &'a [Directive], ) { for d in directives { @@ -494,7 +562,7 @@ fn visit_directives<'a, V: Visitor<'a>>( fn visit_fragment_definition<'a, V: Visitor<'a>>( v: &mut V, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, fragment: &'a FragmentDefinition, ) { v.enter_fragment_definition(ctx, fragment); @@ -505,7 +573,7 @@ fn visit_fragment_definition<'a, V: Visitor<'a>>( fn visit_fragment_spread<'a, V: Visitor<'a>>( v: &mut V, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, fragment_spread: &'a FragmentSpread, ) { v.enter_fragment_spread(ctx, fragment_spread); @@ -515,7 +583,7 @@ fn visit_fragment_spread<'a, V: Visitor<'a>>( fn visit_inline_fragment<'a, V: Visitor<'a>>( v: &mut V, - ctx: &mut ValidatorContext<'a>, + ctx: &mut VisitorContext<'a>, inline_fragment: &'a InlineFragment, ) { v.enter_inline_fragment(ctx, inline_fragment);