Add CacheControl for object

This commit is contained in:
sunli 2020-03-22 16:45:59 +08:00
parent 788a3b558b
commit e6bb9b82ed
43 changed files with 522 additions and 292 deletions

View File

@ -165,3 +165,7 @@ Licensed under either of
* [GraphQL Multipart Request](https://github.com/jaydenseric/graphql-multipart-request-spec) * [GraphQL Multipart Request](https://github.com/jaydenseric/graphql-multipart-request-spec)
* [GraphQL Cursor Connections Specification](https://facebook.github.io/relay/graphql/connections.htm) * [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) * [GraphQL over WebSocket Protocol](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md)
## Contribute
Welcome to contribute !

View File

@ -34,7 +34,7 @@ type Storage = Arc<Mutex<Slab<Book>>>;
struct QueryRoot; struct QueryRoot;
#[async_graphql::Object] #[async_graphql::Object(cache_control(max_age = 5))]
impl QueryRoot { impl QueryRoot {
#[field] #[field]
async fn books(&self, ctx: &Context<'_>) -> Vec<Book> { async fn books(&self, ctx: &Context<'_>) -> Vec<Book> {

View File

@ -247,10 +247,25 @@ where
.respond_to(&req) .respond_to(&req)
.await?) .await?)
} else if ct.essence_str() == mime::APPLICATION_JSON { } else if ct.essence_str() == mime::APPLICATION_JSON {
let gql_req = web::Json::<GQLRequest>::from_request(&req, &mut payload.0).await?; let mut gql_req = web::Json::<GQLRequest>::from_request(&req, &mut payload.0)
Ok(web::Json(gql_req.into_inner().execute(&schema).await) .await?
.respond_to(&req) .into_inner();
.await?) 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 { } else {
Ok(HttpResponse::UnsupportedMediaType().finish()) Ok(HttpResponse::UnsupportedMediaType().finish())
} }

View File

@ -2,13 +2,70 @@ use crate::utils::{parse_validator, parse_value};
use graphql_parser::query::Value; use graphql_parser::query::Value;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; 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<Self> {
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::<usize>() {
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)] #[derive(Debug)]
pub struct Object { pub struct Object {
pub internal: bool, pub internal: bool,
pub name: Option<String>, pub name: Option<String>,
pub desc: Option<String>, pub desc: Option<String>,
pub cache_control: CacheControl,
} }
impl Object { impl Object {
@ -16,6 +73,7 @@ impl Object {
let mut internal = false; let mut internal = false;
let mut name = None; let mut name = None;
let mut desc = None; let mut desc = None;
let mut cache_control = CacheControl::default();
for arg in args { for arg in args {
match arg { 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, internal,
name, name,
desc, desc,
cache_control,
}) })
} }
} }
@ -140,6 +204,7 @@ pub struct Field {
pub name: Option<String>, pub name: Option<String>,
pub desc: Option<String>, pub desc: Option<String>,
pub deprecation: Option<String>, pub deprecation: Option<String>,
pub cache_control: CacheControl,
} }
impl Field { impl Field {
@ -148,6 +213,7 @@ impl Field {
let mut name = None; let mut name = None;
let mut desc = None; let mut desc = None;
let mut deprecation = None; let mut deprecation = None;
let mut cache_control = CacheControl::default();
for attr in attrs { for attr in attrs {
match attr.parse_meta()? { match attr.parse_meta()? {
@ -157,35 +223,43 @@ impl Field {
Meta::List(ls) if ls.path.is_ident("field") => { Meta::List(ls) if ls.path.is_ident("field") => {
is_field = true; is_field = true;
for meta in &ls.nested { for meta in &ls.nested {
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta { match meta {
if nv.path.is_ident("name") { NestedMeta::Meta(Meta::NameValue(nv)) => {
if let syn::Lit::Str(lit) = &nv.lit { if nv.path.is_ident("name") {
name = Some(lit.value()); if let syn::Lit::Str(lit) = &nv.lit {
} else { name = Some(lit.value());
return Err(Error::new_spanned( } else {
&nv.lit, return Err(Error::new_spanned(
"Attribute 'name' should be a string.", &nv.lit,
)); "Attribute 'name' should be a string.",
} ));
} else if nv.path.is_ident("desc") { }
if let syn::Lit::Str(lit) = &nv.lit { } else if nv.path.is_ident("desc") {
desc = Some(lit.value()); if let syn::Lit::Str(lit) = &nv.lit {
} else { desc = Some(lit.value());
return Err(Error::new_spanned( } else {
&nv.lit, return Err(Error::new_spanned(
"Attribute 'desc' should be a string.", &nv.lit,
)); "Attribute 'desc' should be a string.",
} ));
} else if nv.path.is_ident("deprecation") { }
if let syn::Lit::Str(lit) = &nv.lit { } else if nv.path.is_ident("deprecation") {
deprecation = Some(lit.value()); if let syn::Lit::Str(lit) = &nv.lit {
} else { deprecation = Some(lit.value());
return Err(Error::new_spanned( } else {
&nv.lit, return Err(Error::new_spanned(
"Attribute 'deprecation' should be a string.", &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, name,
desc, desc,
deprecation, deprecation,
cache_control,
})) }))
} else { } else {
Ok(None) Ok(None)

View File

@ -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), ty: <#schema_ty as #crate_name::Type>::create_type_info(registry),
deprecation: #deprecation, deprecation: #deprecation,
cache_control: Default::default(),
}); });
}); });

View File

@ -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")) 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 arg_ctx = false;
let mut args = Vec::new(); 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(); let schema_ty = ty.value_type();
schema_fields.push(quote! { schema_fields.push(quote! {
fields.insert(#field_name.to_string(), #crate_name::registry::Field { fields.insert(#field_name.to_string(), #crate_name::registry::Field {
name: #field_name.to_string(), 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), ty: <#schema_ty as #crate_name::Type>::create_type_info(registry),
deprecation: #field_deprecation, 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! { let expanded = quote! {
#item_impl #item_impl
@ -226,6 +249,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
#(#schema_fields)* #(#schema_fields)*
fields fields
}, },
cache_control: #cache_control,
}) })
} }
} }

View File

@ -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), ty: <#ty as #crate_name::Type>::create_type_info(registry),
deprecation: #field_deprecation, 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)* #(#schema_fields)*
fields fields
}, },
cache_control: Default::default(),
}) })
} }
} }

View File

@ -77,6 +77,7 @@ mod schema;
mod subscription; mod subscription;
mod types; mod types;
mod validation; mod validation;
mod visitor;
/// Input value validators /// Input value validators
pub mod validators; pub mod validators;
@ -98,6 +99,7 @@ pub use context::{Context, Variables};
pub use error::{ErrorWithPosition, PositionError, QueryError, QueryParseError}; pub use error::{ErrorWithPosition, PositionError, QueryError, QueryParseError};
pub use graphql_parser::query::Value; pub use graphql_parser::query::Value;
pub use query::{PreparedQuery, QueryBuilder}; pub use query::{PreparedQuery, QueryBuilder};
pub use registry::CacheControl;
pub use scalars::ID; pub use scalars::ID;
pub use schema::Schema; pub use schema::Schema;
pub use subscription::SubscribeBuilder; pub use subscription::SubscribeBuilder;
@ -132,18 +134,20 @@ pub use types::{EnumItem, EnumType};
/// ///
/// # Macro parameters /// # Macro parameters
/// ///
/// | Attribute | description | Type | Optional | /// | Attribute | description | Type | Optional |
/// |-------------|---------------------------|----------|----------| /// |---------------|---------------------------|----------|----------|
/// | name | Object name | string | Y | /// | name | Object name | string | Y |
/// | desc | Object description | string | Y | /// | desc | Object description | string | Y |
/// | cache_control | Object cache control | [`CacheControl`](struct.CacheControl.html) | Y |
/// ///
/// # Field parameters /// # Field parameters
/// ///
/// | Attribute | description | Type | Optional | /// | Attribute | description | Type | Optional |
/// |-------------|---------------------------|----------|----------| /// |---------------|---------------------------|----------|----------|
/// | name | Field name | string | Y | /// | name | Field name | string | Y |
/// | desc | Field description | string | Y | /// | desc | Field description | string | Y |
/// | deprecation | Field deprecation reason | string | Y | /// | deprecation | Field deprecation reason | string | Y |
/// | cache_control | Field cache control | [`CacheControl`](struct.CacheControl.html) | Y |
/// ///
/// # Field argument parameters /// # Field argument parameters
/// ///

View File

@ -1,13 +1,14 @@
use crate::context::Data; use crate::context::Data;
use crate::registry::Registry; use crate::registry::{CacheControl, Registry, Type};
use crate::types::QueryRoot; use crate::types::QueryRoot;
use crate::validation::check_rules; use crate::validation::check_rules;
use crate::visitor::{visit, Visitor, VisitorContext};
use crate::{ContextBase, OutputValueType, Result}; use crate::{ContextBase, OutputValueType, Result};
use crate::{ObjectType, QueryError, QueryParseError, Variables}; use crate::{ObjectType, QueryError, QueryParseError, Variables};
use bytes::Bytes; use bytes::Bytes;
use graphql_parser::parse_query; use graphql_parser::parse_query;
use graphql_parser::query::{ use graphql_parser::query::{
Definition, FragmentDefinition, OperationDefinition, SelectionSet, VariableDefinition, Definition, Field, FragmentDefinition, OperationDefinition, SelectionSet, VariableDefinition,
}; };
use std::collections::HashMap; 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()))?; let document = parse_query(self.source).map_err(|err| QueryParseError(err.to_string()))?;
check_rules(self.registry, &document)?; 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 fragments = HashMap::new();
let mut selection_set = None; let mut selection_set = None;
let mut variable_definitions = None; let mut variable_definitions = None;
@ -106,6 +114,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
})?, })?,
root: root.unwrap(), root: root.unwrap(),
variable_definitions, variable_definitions,
cache_control,
}) })
} }
@ -128,6 +137,7 @@ pub struct PreparedQuery<'a, Query, Mutation> {
fragments: HashMap<String, FragmentDefinition>, fragments: HashMap<String, FragmentDefinition>,
selection_set: SelectionSet, selection_set: SelectionSet,
variable_definitions: Option<Vec<VariableDefinition>>, variable_definitions: Option<Vec<VariableDefinition>>,
cache_control: CacheControl,
} }
impl<'a, Query, Mutation> PreparedQuery<'a, Query, Mutation> { 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, 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(&registry_field.cache_control);
}
}
} }

View File

@ -70,6 +70,7 @@ pub struct Field {
pub args: HashMap<&'static str, InputValue>, pub args: HashMap<&'static str, InputValue>,
pub ty: String, pub ty: String,
pub deprecation: Option<&'static str>, pub deprecation: Option<&'static str>,
pub cache_control: CacheControl,
} }
#[derive(Clone)] #[derive(Clone)]
@ -79,6 +80,84 @@ pub struct EnumValue {
pub deprecation: Option<&'static str>, 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<String> {
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 { pub enum Type {
Scalar { Scalar {
name: String, name: String,
@ -89,6 +168,7 @@ pub enum Type {
name: String, name: String,
description: Option<&'static str>, description: Option<&'static str>,
fields: HashMap<String, Field>, fields: HashMap<String, Field>,
cache_control: CacheControl,
}, },
Interface { Interface {
name: String, name: String,
@ -201,6 +281,7 @@ impl Registry {
name: "".to_string(), name: "".to_string(),
description: None, description: None,
fields: Default::default(), fields: Default::default(),
cache_control: Default::default(),
}, },
); );
let mut ty = f(self); let mut ty = f(self);
@ -213,6 +294,7 @@ impl Registry {
args: Default::default(), args: Default::default(),
ty: "String!".to_string(), ty: "String!".to_string(),
deprecation: None, deprecation: None,
cache_control: Default::default(),
}, },
); );
} }

View File

@ -78,6 +78,7 @@ impl<T: OutputValueType + Send + Sync, E: ObjectType + Sync + Send> Type for Con
args: Default::default(), args: Default::default(),
ty: PageInfo::create_type_info(registry), ty: PageInfo::create_type_info(registry),
deprecation: None, deprecation: None,
cache_control: Default::default(),
}, },
); );
@ -89,6 +90,7 @@ impl<T: OutputValueType + Send + Sync, E: ObjectType + Sync + Send> Type for Con
args: Default::default(), args: Default::default(),
ty: <Option::<Vec<Option<Edge<T,E>>>> as Type>::create_type_info(registry), ty: <Option::<Vec<Option<Edge<T,E>>>> as Type>::create_type_info(registry),
deprecation: None, deprecation: None,
cache_control: Default::default(),
}, },
); );
@ -100,6 +102,7 @@ impl<T: OutputValueType + Send + Sync, E: ObjectType + Sync + Send> Type for Con
args: Default::default(), args: Default::default(),
ty: Option::<i32>::create_type_info(registry), ty: Option::<i32>::create_type_info(registry),
deprecation: None, deprecation: None,
cache_control: Default::default(),
}, },
); );
@ -109,11 +112,13 @@ impl<T: OutputValueType + Send + Sync, E: ObjectType + Sync + Send> 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."#), 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(), args: Default::default(),
ty: Vec::<T>::type_name().to_string(), ty: Vec::<T>::type_name().to_string(),
deprecation: None deprecation: None,
cache_control: Default::default(),
}); });
fields fields
}, },
cache_control: Default::default(),
}) })
} }
} }

View File

@ -47,6 +47,7 @@ where
args: Default::default(), args: Default::default(),
ty: T::create_type_info(registry), ty: T::create_type_info(registry),
deprecation: None, deprecation: None,
cache_control: Default::default(),
}, },
); );
@ -58,12 +59,14 @@ where
args: Default::default(), args: Default::default(),
ty: String::create_type_info(registry), ty: String::create_type_info(registry),
deprecation: None, deprecation: None,
cache_control: Default::default(),
}, },
); );
fields.extend(extra_fields); fields.extend(extra_fields);
fields fields
}, },
cache_control: Default::default(),
} }
}) })
} }

View File

@ -35,6 +35,7 @@ impl Type for EmptyMutation {
name: "EmptyMutation".to_string(), name: "EmptyMutation".to_string(),
description: None, description: None,
fields: Default::default(), fields: Default::default(),
cache_control: Default::default(),
}) })
} }
} }

View File

@ -24,6 +24,7 @@ impl Type for EmptySubscription {
name: "EmptySubscription".to_string(), name: "EmptySubscription".to_string(),
description: None, description: None,
fields: Default::default(), fields: Default::default(),
cache_control: Default::default(),
}) })
} }
} }

View File

@ -30,6 +30,7 @@ impl<T: Type> Type for QueryRoot<T> {
args: Default::default(), args: Default::default(),
ty: schema_type, ty: schema_type,
deprecation: None, deprecation: None,
cache_control: Default::default(),
}, },
); );
@ -54,6 +55,7 @@ impl<T: Type> Type for QueryRoot<T> {
}, },
ty: "__Type".to_string(), ty: "__Type".to_string(),
deprecation: None, deprecation: None,
cache_control: Default::default(),
}, },
); );
} }

View File

@ -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<RuleError>,
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<T: Into<String>>(&mut self, locations: Vec<Pos>, msg: T) {
self.errors.push(RuleError {
locations,
message: msg.into(),
})
}
pub fn append_errors(&mut self, errors: Vec<RuleError>) {
self.errors.extend(errors);
}
pub fn with_type<F: FnMut(&mut ValidatorContext<'a>)>(
&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()
}
}

View File

@ -1,17 +1,14 @@
use crate::error::RuleErrors; use crate::error::RuleErrors;
use crate::registry::Registry; use crate::registry::Registry;
use crate::validation::context::ValidatorContext; use crate::visitor::{visit, VisitorContext, VisitorNil};
use crate::validation::visitor::{visit, VisitorNil};
use crate::Result; use crate::Result;
use graphql_parser::query::Document; use graphql_parser::query::Document;
mod context;
mod rules; mod rules;
mod utils; mod utils;
mod visitor;
pub fn check_rules(registry: &Registry, doc: &Document) -> Result<()> { 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 let mut visitor = VisitorNil
.with(rules::ArgumentsOfCorrectType::default()) .with(rules::ArgumentsOfCorrectType::default())
.with(rules::DefaultValuesOfCorrectType) .with(rules::DefaultValuesOfCorrectType)

View File

@ -1,7 +1,6 @@
use crate::registry::InputValue; use crate::registry::InputValue;
use crate::validation::context::ValidatorContext;
use crate::validation::utils::is_valid_input_value; 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::query::Field;
use graphql_parser::schema::{Directive, Value}; use graphql_parser::schema::{Directive, Value};
use graphql_parser::Pos; use graphql_parser::Pos;
@ -13,7 +12,7 @@ pub struct ArgumentsOfCorrectType<'a> {
} }
impl<'a> Visitor<'a> for 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 self.current_args = ctx
.registry .registry
.directives .directives
@ -21,13 +20,13 @@ impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> {
.map(|d| &d.args); .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; self.current_args = None;
} }
fn enter_argument( fn enter_argument(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
pos: Pos, pos: Pos,
name: &str, name: &str,
value: &'a Value, 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 self.current_args = ctx
.parent_type() .parent_type()
.and_then(|p| p.field_by_name(&field.name)) .and_then(|p| p.field_by_name(&field.name))
.map(|f| &f.args); .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; self.current_args = None;
} }
} }

View File

@ -1,6 +1,5 @@
use crate::validation::context::ValidatorContext;
use crate::validation::utils::is_valid_input_value; use crate::validation::utils::is_valid_input_value;
use crate::validation::visitor::Visitor; use crate::visitor::{Visitor, VisitorContext};
use graphql_parser::query::{Type, VariableDefinition}; use graphql_parser::query::{Type, VariableDefinition};
pub struct DefaultValuesOfCorrectType; pub struct DefaultValuesOfCorrectType;
@ -8,7 +7,7 @@ pub struct DefaultValuesOfCorrectType;
impl<'a> Visitor<'a> for DefaultValuesOfCorrectType { impl<'a> Visitor<'a> for DefaultValuesOfCorrectType {
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
variable_definition: &'a VariableDefinition, variable_definition: &'a VariableDefinition,
) { ) {
if let Some(value) = &variable_definition.default_value { if let Some(value) = &variable_definition.default_value {

View File

@ -1,13 +1,12 @@
use crate::registry; use crate::registry;
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::Field; use graphql_parser::query::Field;
#[derive(Default)] #[derive(Default)]
pub struct FieldsOnCorrectType; pub struct FieldsOnCorrectType;
impl<'a> Visitor<'a> for 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 if ctx
.parent_type() .parent_type()
.unwrap() .unwrap()

View File

@ -1,5 +1,4 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::{FragmentDefinition, InlineFragment, TypeCondition}; use graphql_parser::query::{FragmentDefinition, InlineFragment, TypeCondition};
#[derive(Default)] #[derive(Default)]
@ -8,7 +7,7 @@ pub struct FragmentsOnCompositeTypes;
impl<'a> Visitor<'a> for FragmentsOnCompositeTypes { impl<'a> Visitor<'a> for FragmentsOnCompositeTypes {
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
fragment_definition: &'a FragmentDefinition, fragment_definition: &'a FragmentDefinition,
) { ) {
if !ctx.current_type().is_composite() { if !ctx.current_type().is_composite() {
@ -25,7 +24,7 @@ impl<'a> Visitor<'a> for FragmentsOnCompositeTypes {
fn enter_inline_fragment( fn enter_inline_fragment(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
inline_fragment: &'a InlineFragment, inline_fragment: &'a InlineFragment,
) { ) {
if !ctx.current_type().is_composite() { if !ctx.current_type().is_composite() {

View File

@ -1,6 +1,5 @@
use crate::registry::InputValue; use crate::registry::InputValue;
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use crate::Value; use crate::Value;
use graphql_parser::query::{Directive, Field}; use graphql_parser::query::{Directive, Field};
use graphql_parser::Pos; use graphql_parser::Pos;
@ -20,7 +19,7 @@ pub struct KnownArgumentNames<'a> {
} }
impl<'a> Visitor<'a> for 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 self.current_args = ctx
.registry .registry
.directives .directives
@ -28,13 +27,13 @@ impl<'a> Visitor<'a> for KnownArgumentNames<'a> {
.map(|d| (&d.args, ArgsType::Directive(&directive.name))); .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; self.current_args = None;
} }
fn enter_argument( fn enter_argument(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
pos: Pos, pos: Pos,
name: &str, name: &str,
_value: &'a Value, _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 self.current_args = ctx
.parent_type() .parent_type()
.and_then(|p| p.field_by_name(&field.name)) .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; self.current_args = None;
} }
} }

View File

@ -1,6 +1,5 @@
use crate::model::__DirectiveLocation; use crate::model::__DirectiveLocation;
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::{ use graphql_parser::query::{
Field, FragmentDefinition, FragmentSpread, InlineFragment, OperationDefinition, Field, FragmentDefinition, FragmentSpread, InlineFragment, OperationDefinition,
}; };
@ -14,7 +13,7 @@ pub struct KnownDirectives {
impl<'a> Visitor<'a> for KnownDirectives { impl<'a> Visitor<'a> for KnownDirectives {
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
operation_definition: &'a OperationDefinition, operation_definition: &'a OperationDefinition,
) { ) {
self.location_stack.push(match operation_definition { self.location_stack.push(match operation_definition {
@ -28,7 +27,7 @@ impl<'a> Visitor<'a> for KnownDirectives {
fn exit_operation_definition( fn exit_operation_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_operation_definition: &'a OperationDefinition, _operation_definition: &'a OperationDefinition,
) { ) {
self.location_stack.pop(); self.location_stack.pop();
@ -36,7 +35,7 @@ impl<'a> Visitor<'a> for KnownDirectives {
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_fragment_definition: &'a FragmentDefinition, _fragment_definition: &'a FragmentDefinition,
) { ) {
self.location_stack self.location_stack
@ -45,13 +44,13 @@ impl<'a> Visitor<'a> for KnownDirectives {
fn exit_fragment_definition( fn exit_fragment_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_fragment_definition: &'a FragmentDefinition, _fragment_definition: &'a FragmentDefinition,
) { ) {
self.location_stack.pop(); 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(schema_directive) = ctx.registry.directives.get(directive.name.as_str()) {
if let Some(current_location) = self.location_stack.last() { if let Some(current_location) = self.location_stack.last() {
if !schema_directive.locations.contains(current_location) { 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); 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(); self.location_stack.pop();
} }
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_fragment_spread: &'a FragmentSpread, _fragment_spread: &'a FragmentSpread,
) { ) {
self.location_stack self.location_stack
@ -91,7 +90,7 @@ impl<'a> Visitor<'a> for KnownDirectives {
fn exit_fragment_spread( fn exit_fragment_spread(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_fragment_spread: &'a FragmentSpread, _fragment_spread: &'a FragmentSpread,
) { ) {
self.location_stack.pop(); self.location_stack.pop();
@ -99,7 +98,7 @@ impl<'a> Visitor<'a> for KnownDirectives {
fn enter_inline_fragment( fn enter_inline_fragment(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_inline_fragment: &'a InlineFragment, _inline_fragment: &'a InlineFragment,
) { ) {
self.location_stack self.location_stack
@ -108,7 +107,7 @@ impl<'a> Visitor<'a> for KnownDirectives {
fn exit_inline_fragment( fn exit_inline_fragment(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_inline_fragment: &'a InlineFragment, _inline_fragment: &'a InlineFragment,
) { ) {
self.location_stack.pop(); self.location_stack.pop();

View File

@ -1,5 +1,4 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::FragmentSpread; use graphql_parser::query::FragmentSpread;
#[derive(Default)] #[derive(Default)]
@ -8,7 +7,7 @@ pub struct KnownFragmentNames;
impl<'a> Visitor<'a> for KnownFragmentNames { impl<'a> Visitor<'a> for KnownFragmentNames {
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
fragment_spread: &'a FragmentSpread, fragment_spread: &'a FragmentSpread,
) { ) {
if !ctx.is_known_fragment(&fragment_spread.fragment_name) { if !ctx.is_known_fragment(&fragment_spread.fragment_name) {

View File

@ -1,6 +1,5 @@
use crate::registry::TypeName; use crate::registry::TypeName;
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::{ use graphql_parser::query::{
FragmentDefinition, InlineFragment, TypeCondition, VariableDefinition, FragmentDefinition, InlineFragment, TypeCondition, VariableDefinition,
}; };
@ -12,7 +11,7 @@ pub struct KnownTypeNames;
impl<'a> Visitor<'a> for KnownTypeNames { impl<'a> Visitor<'a> for KnownTypeNames {
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
fragment_definition: &'a FragmentDefinition, fragment_definition: &'a FragmentDefinition,
) { ) {
let TypeCondition::On(name) = &fragment_definition.type_condition; let TypeCondition::On(name) = &fragment_definition.type_condition;
@ -21,7 +20,7 @@ impl<'a> Visitor<'a> for KnownTypeNames {
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
variable_definition: &'a VariableDefinition, variable_definition: &'a VariableDefinition,
) { ) {
validate_type( validate_type(
@ -33,7 +32,7 @@ impl<'a> Visitor<'a> for KnownTypeNames {
fn enter_inline_fragment( fn enter_inline_fragment(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
inline_fragment: &'a InlineFragment, inline_fragment: &'a InlineFragment,
) { ) {
if let Some(TypeCondition::On(name)) = &inline_fragment.type_condition { 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() { if ctx.registry.types.get(type_name).is_none() {
ctx.report_error(vec![pos], format!(r#"Unknown type "{}""#, type_name)); ctx.report_error(vec![pos], format!(r#"Unknown type "{}""#, type_name));
} }

View File

@ -1,5 +1,4 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::{Definition, Document, OperationDefinition}; use graphql_parser::query::{Definition, Document, OperationDefinition};
#[derive(Default)] #[derive(Default)]
@ -8,7 +7,7 @@ pub struct LoneAnonymousOperation {
} }
impl<'a> Visitor<'a> for 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( self.operation_count = Some(
doc.definitions doc.definitions
.iter() .iter()
@ -22,7 +21,7 @@ impl<'a> Visitor<'a> for LoneAnonymousOperation {
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
operation_definition: &'a OperationDefinition, operation_definition: &'a OperationDefinition,
) { ) {
if let Some(operation_count) = self.operation_count { if let Some(operation_count) = self.operation_count {

View File

@ -1,12 +1,11 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::Field; use graphql_parser::query::Field;
#[derive(Default)] #[derive(Default)]
pub struct NoComposeLeafs; pub struct NoComposeLeafs;
impl<'a> Visitor<'a> for 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(ty) = ctx.parent_type() {
if let Some(schema_field) = ty.field_by_name(&field.name) { if let Some(schema_field) = ty.field_by_name(&field.name) {
if let Some(ty) = ctx.registry.basic_type_by_typename(&schema_field.ty) { if let Some(ty) = ctx.registry.basic_type_by_typename(&schema_field.ty) {

View File

@ -1,6 +1,5 @@
use crate::error::RuleError; use crate::error::RuleError;
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::{Document, FragmentDefinition, FragmentSpread}; use graphql_parser::query::{Document, FragmentDefinition, FragmentSpread};
use graphql_parser::Pos; use graphql_parser::Pos;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -55,7 +54,7 @@ pub struct NoFragmentCycles<'a> {
} }
impl<'a> Visitor<'a> for 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 { let mut detector = CycleDetector {
visited: HashSet::new(), visited: HashSet::new(),
spreads: &self.spreads, spreads: &self.spreads,
@ -75,7 +74,7 @@ impl<'a> Visitor<'a> for NoFragmentCycles<'a> {
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
fragment_definition: &'a FragmentDefinition, fragment_definition: &'a FragmentDefinition,
) { ) {
self.current_fragment = Some(&fragment_definition.name); self.current_fragment = Some(&fragment_definition.name);
@ -84,7 +83,7 @@ impl<'a> Visitor<'a> for NoFragmentCycles<'a> {
fn exit_fragment_definition( fn exit_fragment_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_fragment_definition: &'a FragmentDefinition, _fragment_definition: &'a FragmentDefinition,
) { ) {
self.current_fragment = None; self.current_fragment = None;
@ -92,7 +91,7 @@ impl<'a> Visitor<'a> for NoFragmentCycles<'a> {
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
fragment_spread: &'a FragmentSpread, fragment_spread: &'a FragmentSpread,
) { ) {
if let Some(current_fragment) = self.current_fragment { if let Some(current_fragment) = self.current_fragment {

View File

@ -1,5 +1,4 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::{OperationDefinition, VariableDefinition}; use graphql_parser::query::{OperationDefinition, VariableDefinition};
use graphql_parser::schema::Value; use graphql_parser::schema::Value;
use graphql_parser::Pos; use graphql_parser::Pos;
@ -13,7 +12,7 @@ pub struct NoUndefinedVariables<'a> {
impl<'a> Visitor<'a> for NoUndefinedVariables<'a> { impl<'a> Visitor<'a> for NoUndefinedVariables<'a> {
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_operation_definition: &'a OperationDefinition, _operation_definition: &'a OperationDefinition,
) { ) {
self.vars.clear(); self.vars.clear();
@ -21,7 +20,7 @@ impl<'a> Visitor<'a> for NoUndefinedVariables<'a> {
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
variable_definition: &'a VariableDefinition, variable_definition: &'a VariableDefinition,
) { ) {
self.vars.insert(&variable_definition.name); self.vars.insert(&variable_definition.name);
@ -29,7 +28,7 @@ impl<'a> Visitor<'a> for NoUndefinedVariables<'a> {
fn enter_argument( fn enter_argument(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
pos: Pos, pos: Pos,
_name: &str, _name: &str,
value: &'a Value, value: &'a Value,

View File

@ -1,5 +1,4 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::{Definition, Document, FragmentSpread}; use graphql_parser::query::{Definition, Document, FragmentSpread};
use std::collections::HashSet; use std::collections::HashSet;
@ -9,7 +8,7 @@ pub struct NoUnusedFragments<'a> {
} }
impl<'a> Visitor<'a> for 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 { for d in &doc.definitions {
if let Definition::Fragment(fragment) = d { if let Definition::Fragment(fragment) = d {
if !self.spreads.contains(fragment.name.as_str()) { if !self.spreads.contains(fragment.name.as_str()) {
@ -24,7 +23,7 @@ impl<'a> Visitor<'a> for NoUnusedFragments<'a> {
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
fragment_spread: &'a FragmentSpread, fragment_spread: &'a FragmentSpread,
) { ) {
self.spreads.insert(&fragment_spread.fragment_name); self.spreads.insert(&fragment_spread.fragment_name);

View File

@ -1,5 +1,4 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::{OperationDefinition, VariableDefinition}; use graphql_parser::query::{OperationDefinition, VariableDefinition};
use graphql_parser::schema::Value; use graphql_parser::schema::Value;
use graphql_parser::Pos; use graphql_parser::Pos;
@ -35,7 +34,7 @@ impl<'a> NoUnusedVariables<'a> {
impl<'a> Visitor<'a> for NoUnusedVariables<'a> { impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_operation_definition: &'a OperationDefinition, _operation_definition: &'a OperationDefinition,
) { ) {
self.used_vars.clear(); self.used_vars.clear();
@ -44,7 +43,7 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
fn exit_operation_definition( fn exit_operation_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
_operation_definition: &'a OperationDefinition, _operation_definition: &'a OperationDefinition,
) { ) {
for (name, pos) in &self.vars { for (name, pos) in &self.vars {
@ -56,7 +55,7 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
variable_definition: &'a VariableDefinition, variable_definition: &'a VariableDefinition,
) { ) {
self.vars self.vars
@ -65,7 +64,7 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
fn enter_argument( fn enter_argument(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_pos: Pos, _pos: Pos,
_name: &'a str, _name: &'a str,
value: &'a Value, value: &'a Value,

View File

@ -1,5 +1,4 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::{Field, Selection, SelectionSet}; use graphql_parser::query::{Field, Selection, SelectionSet};
use std::collections::HashMap; use std::collections::HashMap;
@ -9,7 +8,7 @@ pub struct OverlappingFieldsCanBeMerged;
impl<'a> Visitor<'a> for OverlappingFieldsCanBeMerged { impl<'a> Visitor<'a> for OverlappingFieldsCanBeMerged {
fn enter_selection_set( fn enter_selection_set(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
selection_set: &'a SelectionSet, selection_set: &'a SelectionSet,
) { ) {
let mut find_conflicts = FindConflicts { let mut find_conflicts = FindConflicts {
@ -22,7 +21,7 @@ impl<'a> Visitor<'a> for OverlappingFieldsCanBeMerged {
struct FindConflicts<'a, 'ctx> { struct FindConflicts<'a, 'ctx> {
outputs: HashMap<&'a str, &'a Field>, outputs: HashMap<&'a str, &'a Field>,
ctx: &'a mut ValidatorContext<'ctx>, ctx: &'a mut VisitorContext<'ctx>,
} }
impl<'a, 'ctx> FindConflicts<'a, 'ctx> { impl<'a, 'ctx> FindConflicts<'a, 'ctx> {

View File

@ -1,5 +1,4 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::{Definition, Document, FragmentSpread, InlineFragment, TypeCondition}; use graphql_parser::query::{Definition, Document, FragmentSpread, InlineFragment, TypeCondition};
use std::collections::HashMap; use std::collections::HashMap;
@ -9,7 +8,7 @@ pub struct PossibleFragmentSpreads<'a> {
} }
impl<'a> Visitor<'a> for 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 { for d in &doc.definitions {
if let Definition::Fragment(fragment) = d { if let Definition::Fragment(fragment) = d {
let TypeCondition::On(type_name) = &fragment.type_condition; let TypeCondition::On(type_name) = &fragment.type_condition;
@ -21,7 +20,7 @@ impl<'a> Visitor<'a> for PossibleFragmentSpreads<'a> {
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
fragment_spread: &'a FragmentSpread, fragment_spread: &'a FragmentSpread,
) { ) {
if let Some(fragment_type) = self if let Some(fragment_type) = self
@ -42,7 +41,7 @@ impl<'a> Visitor<'a> for PossibleFragmentSpreads<'a> {
fn enter_inline_fragment( fn enter_inline_fragment(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
inline_fragment: &'a InlineFragment, inline_fragment: &'a InlineFragment,
) { ) {
if let Some(parent_type) = ctx.parent_type() { if let Some(parent_type) = ctx.parent_type() {

View File

@ -1,6 +1,5 @@
use crate::registry::TypeName; use crate::registry::TypeName;
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::Field; use graphql_parser::query::Field;
use graphql_parser::schema::Directive; use graphql_parser::schema::Directive;
@ -8,7 +7,7 @@ use graphql_parser::schema::Directive;
pub struct ProvidedNonNullArguments; pub struct ProvidedNonNullArguments;
impl<'a> Visitor<'a> for 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) { if let Some(schema_directive) = ctx.registry.directives.get(&directive.name) {
for arg in schema_directive.args.values() { for arg in schema_directive.args.values() {
if TypeName::create(&arg.ty).is_non_null() 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(parent_type) = ctx.parent_type() {
if let Some(schema_field) = parent_type.field_by_name(&field.name) { if let Some(schema_field) = parent_type.field_by_name(&field.name) {
for arg in schema_field.args.values() { for arg in schema_field.args.values() {

View File

@ -1,12 +1,11 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::Field; use graphql_parser::query::Field;
#[derive(Default)] #[derive(Default)]
pub struct ScalarLeafs; pub struct ScalarLeafs;
impl<'a> Visitor<'a> for 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(ty) = ctx.parent_type() {
if let Some(schema_field) = ty.field_by_name(&field.name) { if let Some(schema_field) = ty.field_by_name(&field.name) {
if let Some(ty) = ctx.registry.basic_type_by_typename(&schema_field.ty) { if let Some(ty) = ctx.registry.basic_type_by_typename(&schema_field.ty) {

View File

@ -1,5 +1,4 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::Field; use graphql_parser::query::Field;
use graphql_parser::schema::{Directive, Value}; use graphql_parser::schema::{Directive, Value};
use graphql_parser::Pos; use graphql_parser::Pos;
@ -11,13 +10,13 @@ pub struct UniqueArgumentNames<'a> {
} }
impl<'a> Visitor<'a> for 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(); self.names.clear();
} }
fn enter_argument( fn enter_argument(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
pos: Pos, pos: Pos,
name: &'a str, name: &'a str,
_value: &'a Value, _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(); self.names.clear();
} }
} }

View File

@ -1,5 +1,4 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::FragmentDefinition; use graphql_parser::query::FragmentDefinition;
use std::collections::HashSet; use std::collections::HashSet;
@ -11,7 +10,7 @@ pub struct UniqueFragmentNames<'a> {
impl<'a> Visitor<'a> for UniqueFragmentNames<'a> { impl<'a> Visitor<'a> for UniqueFragmentNames<'a> {
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
fragment_definition: &'a FragmentDefinition, fragment_definition: &'a FragmentDefinition,
) { ) {
if !self.names.insert(&fragment_definition.name) { if !self.names.insert(&fragment_definition.name) {

View File

@ -1,5 +1,4 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::{Mutation, OperationDefinition, Query, Subscription}; use graphql_parser::query::{Mutation, OperationDefinition, Query, Subscription};
use std::collections::HashSet; use std::collections::HashSet;
@ -11,7 +10,7 @@ pub struct UniqueOperationNames<'a> {
impl<'a> Visitor<'a> for UniqueOperationNames<'a> { impl<'a> Visitor<'a> for UniqueOperationNames<'a> {
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
operation_definition: &'a OperationDefinition, operation_definition: &'a OperationDefinition,
) { ) {
let name = match operation_definition { let name = match operation_definition {

View File

@ -1,5 +1,4 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::{OperationDefinition, VariableDefinition}; use graphql_parser::query::{OperationDefinition, VariableDefinition};
use std::collections::HashSet; use std::collections::HashSet;
@ -11,7 +10,7 @@ pub struct UniqueVariableNames<'a> {
impl<'a> Visitor<'a> for UniqueVariableNames<'a> { impl<'a> Visitor<'a> for UniqueVariableNames<'a> {
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_operation_definition: &'a OperationDefinition, _operation_definition: &'a OperationDefinition,
) { ) {
self.names.clear(); self.names.clear();
@ -19,7 +18,7 @@ impl<'a> Visitor<'a> for UniqueVariableNames<'a> {
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
variable_definition: &'a VariableDefinition, variable_definition: &'a VariableDefinition,
) { ) {
if !self.names.insert(variable_definition.name.as_str()) { if !self.names.insert(variable_definition.name.as_str()) {

View File

@ -1,5 +1,4 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::OperationDefinition; use graphql_parser::query::OperationDefinition;
#[derive(Default)] #[derive(Default)]
@ -8,7 +7,7 @@ pub struct UploadFile;
impl<'a> Visitor<'a> for UploadFile { impl<'a> Visitor<'a> for UploadFile {
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
operation_definition: &'a OperationDefinition, operation_definition: &'a OperationDefinition,
) { ) {
if let OperationDefinition::Query(query) = operation_definition { if let OperationDefinition::Query(query) = operation_definition {

View File

@ -1,5 +1,4 @@
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use graphql_parser::query::VariableDefinition; use graphql_parser::query::VariableDefinition;
#[derive(Default)] #[derive(Default)]
@ -8,7 +7,7 @@ pub struct VariablesAreInputTypes;
impl<'a> Visitor<'a> for VariablesAreInputTypes { impl<'a> Visitor<'a> for VariablesAreInputTypes {
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
variable_definition: &'a VariableDefinition, variable_definition: &'a VariableDefinition,
) { ) {
if let Some(ty) = ctx if let Some(ty) = ctx

View File

@ -1,6 +1,5 @@
use crate::registry::TypeName; use crate::registry::TypeName;
use crate::validation::context::ValidatorContext; use crate::visitor::{Visitor, VisitorContext};
use crate::validation::visitor::Visitor;
use crate::Value; use crate::Value;
use graphql_parser::query::{Field, OperationDefinition, VariableDefinition}; use graphql_parser::query::{Field, OperationDefinition, VariableDefinition};
use graphql_parser::schema::Directive; use graphql_parser::schema::Directive;
@ -15,7 +14,7 @@ pub struct VariableInAllowedPosition<'a> {
impl<'a> VariableInAllowedPosition<'a> { impl<'a> VariableInAllowedPosition<'a> {
fn check_type( fn check_type(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
pos: Pos, pos: Pos,
except_type: &str, except_type: &str,
value: &Value, value: &Value,
@ -51,7 +50,7 @@ impl<'a> VariableInAllowedPosition<'a> {
impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> { impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> {
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_operation_definition: &'a OperationDefinition, _operation_definition: &'a OperationDefinition,
) { ) {
self.var_types.clear(); self.var_types.clear();
@ -59,7 +58,7 @@ impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> {
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
variable_definition: &'a VariableDefinition, variable_definition: &'a VariableDefinition,
) { ) {
self.var_types.insert( 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()) { if let Some(schema_directive) = ctx.registry.directives.get(directive.name.as_str()) {
for (name, value) in &directive.arguments { for (name, value) in &directive.arguments {
if let Some(input) = schema_directive.args.get(name.as_str()) { 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(parent_type) = ctx.parent_type() {
if let Some(schema_field) = parent_type.field_by_name(&field.name) { if let Some(schema_field) = parent_type.field_by_name(&field.name) {
for (name, value) in &field.arguments { for (name, value) in &field.arguments {

View File

@ -1,59 +1,127 @@
use crate::validation::context::ValidatorContext; use crate::error::RuleError;
use crate::registry;
use graphql_parser::query::{ use graphql_parser::query::{
Definition, Directive, Document, Field, FragmentDefinition, FragmentSpread, InlineFragment, Definition, Directive, Document, Field, FragmentDefinition, FragmentSpread, InlineFragment,
Name, OperationDefinition, Selection, SelectionSet, TypeCondition, Value, VariableDefinition, Name, OperationDefinition, Selection, SelectionSet, TypeCondition, Value, VariableDefinition,
}; };
use graphql_parser::Pos; use graphql_parser::Pos;
use std::collections::HashMap;
pub struct VisitorContext<'a> {
pub registry: &'a registry::Registry,
pub errors: Vec<RuleError>,
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<T: Into<String>>(&mut self, locations: Vec<Pos>, msg: T) {
self.errors.push(RuleError {
locations,
message: msg.into(),
})
}
pub fn append_errors(&mut self, errors: Vec<RuleError>) {
self.errors.extend(errors);
}
pub fn with_type<F: FnMut(&mut VisitorContext<'a>)>(
&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> { pub trait Visitor<'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) {}
fn exit_document(&mut self, _ctx: &mut ValidatorContext<'a>, _doc: &'a Document) {} fn exit_document(&mut self, _ctx: &mut VisitorContext<'a>, _doc: &'a Document) {}
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_operation_definition: &'a OperationDefinition, _operation_definition: &'a OperationDefinition,
) { ) {
} }
fn exit_operation_definition( fn exit_operation_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_operation_definition: &'a OperationDefinition, _operation_definition: &'a OperationDefinition,
) { ) {
} }
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_fragment_definition: &'a FragmentDefinition, _fragment_definition: &'a FragmentDefinition,
) { ) {
} }
fn exit_fragment_definition( fn exit_fragment_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_fragment_definition: &'a FragmentDefinition, _fragment_definition: &'a FragmentDefinition,
) { ) {
} }
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_variable_definition: &'a VariableDefinition, _variable_definition: &'a VariableDefinition,
) { ) {
} }
fn exit_variable_definition( fn exit_variable_definition(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_variable_definition: &'a VariableDefinition, _variable_definition: &'a VariableDefinition,
) { ) {
} }
fn enter_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 ValidatorContext<'a>, _directive: &'a Directive) {} fn exit_directive(&mut self, _ctx: &mut VisitorContext<'a>, _directive: &'a Directive) {}
fn enter_argument( fn enter_argument(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_pos: Pos, _pos: Pos,
_name: &'a str, _name: &'a str,
_value: &'a Value, _value: &'a Value,
@ -61,7 +129,7 @@ pub trait Visitor<'a> {
} }
fn exit_argument( fn exit_argument(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_pos: Pos, _pos: Pos,
_name: &'a str, _name: &'a str,
_value: &'a Value, _value: &'a Value,
@ -70,45 +138,45 @@ pub trait Visitor<'a> {
fn enter_selection_set( fn enter_selection_set(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_selection_set: &'a SelectionSet, _selection_set: &'a SelectionSet,
) { ) {
} }
fn exit_selection_set( fn exit_selection_set(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_selection_set: &'a SelectionSet, _selection_set: &'a SelectionSet,
) { ) {
} }
fn enter_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 ValidatorContext<'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 enter_field(&mut self, _ctx: &mut VisitorContext<'a>, _field: &'a 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) {}
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_fragment_spread: &'a FragmentSpread, _fragment_spread: &'a FragmentSpread,
) { ) {
} }
fn exit_fragment_spread( fn exit_fragment_spread(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_fragment_spread: &'a FragmentSpread, _fragment_spread: &'a FragmentSpread,
) { ) {
} }
fn enter_inline_fragment( fn enter_inline_fragment(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_inline_fragment: &'a InlineFragment, _inline_fragment: &'a InlineFragment,
) { ) {
} }
fn exit_inline_fragment( fn exit_inline_fragment(
&mut self, &mut self,
_ctx: &mut ValidatorContext<'a>, _ctx: &mut VisitorContext<'a>,
_inline_fragment: &'a InlineFragment, _inline_fragment: &'a InlineFragment,
) { ) {
} }
@ -137,19 +205,19 @@ where
A: Visitor<'a> + 'a, A: Visitor<'a> + 'a,
B: 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.0.enter_document(ctx, doc);
self.1.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.0.exit_document(ctx, doc);
self.1.exit_document(ctx, doc); self.1.exit_document(ctx, doc);
} }
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
operation_definition: &'a OperationDefinition, operation_definition: &'a OperationDefinition,
) { ) {
self.0.enter_operation_definition(ctx, operation_definition); self.0.enter_operation_definition(ctx, operation_definition);
@ -158,7 +226,7 @@ where
fn exit_operation_definition( fn exit_operation_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
operation_definition: &'a OperationDefinition, operation_definition: &'a OperationDefinition,
) { ) {
self.0.exit_operation_definition(ctx, operation_definition); self.0.exit_operation_definition(ctx, operation_definition);
@ -167,7 +235,7 @@ where
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
fragment_definition: &'a FragmentDefinition, fragment_definition: &'a FragmentDefinition,
) { ) {
self.0.enter_fragment_definition(ctx, fragment_definition); self.0.enter_fragment_definition(ctx, fragment_definition);
@ -176,7 +244,7 @@ where
fn exit_fragment_definition( fn exit_fragment_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
fragment_definition: &'a FragmentDefinition, fragment_definition: &'a FragmentDefinition,
) { ) {
self.0.exit_fragment_definition(ctx, fragment_definition); self.0.exit_fragment_definition(ctx, fragment_definition);
@ -185,7 +253,7 @@ where
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
variable_definition: &'a VariableDefinition, variable_definition: &'a VariableDefinition,
) { ) {
self.0.enter_variable_definition(ctx, variable_definition); self.0.enter_variable_definition(ctx, variable_definition);
@ -194,26 +262,26 @@ where
fn exit_variable_definition( fn exit_variable_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
variable_definition: &'a VariableDefinition, variable_definition: &'a VariableDefinition,
) { ) {
self.0.exit_variable_definition(ctx, variable_definition); self.0.exit_variable_definition(ctx, variable_definition);
self.1.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.0.enter_directive(ctx, directive);
self.1.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.0.exit_directive(ctx, directive);
self.1.exit_directive(ctx, directive); self.1.exit_directive(ctx, directive);
} }
fn enter_argument( fn enter_argument(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
pos: Pos, pos: Pos,
name: &'a str, name: &'a str,
value: &'a Value, value: &'a Value,
@ -224,7 +292,7 @@ where
fn exit_argument( fn exit_argument(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
pos: Pos, pos: Pos,
name: &'a str, name: &'a str,
value: &'a Value, value: &'a Value,
@ -235,7 +303,7 @@ where
fn enter_selection_set( fn enter_selection_set(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
selection_set: &'a SelectionSet, selection_set: &'a SelectionSet,
) { ) {
self.0.enter_selection_set(ctx, selection_set); self.0.enter_selection_set(ctx, selection_set);
@ -244,36 +312,36 @@ where
fn exit_selection_set( fn exit_selection_set(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
selection_set: &'a SelectionSet, selection_set: &'a SelectionSet,
) { ) {
self.0.exit_selection_set(ctx, selection_set); self.0.exit_selection_set(ctx, selection_set);
self.1.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.0.enter_selection(ctx, selection);
self.1.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.0.exit_selection(ctx, selection);
self.1.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.0.enter_field(ctx, field);
self.1.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.0.exit_field(ctx, field);
self.1.exit_field(ctx, field); self.1.exit_field(ctx, field);
} }
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
fragment_spread: &'a FragmentSpread, fragment_spread: &'a FragmentSpread,
) { ) {
self.0.enter_fragment_spread(ctx, fragment_spread); self.0.enter_fragment_spread(ctx, fragment_spread);
@ -282,7 +350,7 @@ where
fn exit_fragment_spread( fn exit_fragment_spread(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
fragment_spread: &'a FragmentSpread, fragment_spread: &'a FragmentSpread,
) { ) {
self.0.exit_fragment_spread(ctx, fragment_spread); self.0.exit_fragment_spread(ctx, fragment_spread);
@ -291,7 +359,7 @@ where
fn enter_inline_fragment( fn enter_inline_fragment(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
inline_fragment: &'a InlineFragment, inline_fragment: &'a InlineFragment,
) { ) {
self.0.enter_inline_fragment(ctx, inline_fragment); self.0.enter_inline_fragment(ctx, inline_fragment);
@ -300,7 +368,7 @@ where
fn exit_inline_fragment( fn exit_inline_fragment(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
inline_fragment: &'a InlineFragment, inline_fragment: &'a InlineFragment,
) { ) {
self.0.exit_inline_fragment(ctx, inline_fragment); 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); v.enter_document(ctx, doc);
visit_definitions(v, ctx, doc); visit_definitions(v, ctx, doc);
v.exit_document(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>>( fn visit_definitions<'a, V: Visitor<'a>>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
doc: &'a Document, doc: &'a Document,
) { ) {
for d in &doc.definitions { for d in &doc.definitions {
@ -341,7 +409,7 @@ fn visit_definitions<'a, V: Visitor<'a>>(
fn visit_operation_definition<'a, V: Visitor<'a>>( fn visit_operation_definition<'a, V: Visitor<'a>>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
operation: &'a OperationDefinition, operation: &'a OperationDefinition,
) { ) {
v.enter_operation_definition(ctx, operation); 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>>( fn visit_selection_set<'a, V: Visitor<'a>>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
selection_set: &'a SelectionSet, selection_set: &'a SelectionSet,
) { ) {
v.enter_selection_set(ctx, selection_set); 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>>( fn visit_selection<'a, V: Visitor<'a>>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
selection: &'a Selection, selection: &'a Selection,
) { ) {
v.enter_selection(ctx, selection); v.enter_selection(ctx, selection);
@ -449,7 +517,7 @@ fn visit_selection<'a, V: Visitor<'a>>(
v.exit_selection(ctx, selection); 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); v.enter_field(ctx, field);
visit_arguments(v, ctx, field.position, &field.arguments); visit_arguments(v, ctx, field.position, &field.arguments);
visit_directives(v, ctx, &field.directives); 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>>( fn visit_arguments<'a, V: Visitor<'a>>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
pos: Pos, pos: Pos,
arguments: &'a [(Name, Value)], arguments: &'a [(Name, Value)],
) { ) {
@ -471,7 +539,7 @@ fn visit_arguments<'a, V: Visitor<'a>>(
fn visit_variable_definitions<'a, V: Visitor<'a>>( fn visit_variable_definitions<'a, V: Visitor<'a>>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
variable_definitions: &'a [VariableDefinition], variable_definitions: &'a [VariableDefinition],
) { ) {
for d in variable_definitions { for d in variable_definitions {
@ -482,7 +550,7 @@ fn visit_variable_definitions<'a, V: Visitor<'a>>(
fn visit_directives<'a, V: Visitor<'a>>( fn visit_directives<'a, V: Visitor<'a>>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
directives: &'a [Directive], directives: &'a [Directive],
) { ) {
for d in directives { for d in directives {
@ -494,7 +562,7 @@ fn visit_directives<'a, V: Visitor<'a>>(
fn visit_fragment_definition<'a, V: Visitor<'a>>( fn visit_fragment_definition<'a, V: Visitor<'a>>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
fragment: &'a FragmentDefinition, fragment: &'a FragmentDefinition,
) { ) {
v.enter_fragment_definition(ctx, fragment); 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>>( fn visit_fragment_spread<'a, V: Visitor<'a>>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
fragment_spread: &'a FragmentSpread, fragment_spread: &'a FragmentSpread,
) { ) {
v.enter_fragment_spread(ctx, fragment_spread); 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>>( fn visit_inline_fragment<'a, V: Visitor<'a>>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut VisitorContext<'a>,
inline_fragment: &'a InlineFragment, inline_fragment: &'a InlineFragment,
) { ) {
v.enter_inline_fragment(ctx, inline_fragment); v.enter_inline_fragment(ctx, inline_fragment);