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 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 !

View File

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

View File

@ -247,10 +247,25 @@ where
.respond_to(&req)
.await?)
} else if ct.essence_str() == mime::APPLICATION_JSON {
let gql_req = web::Json::<GQLRequest>::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::<GQLRequest>::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())
}

View File

@ -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<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)]
pub struct Object {
pub internal: bool,
pub name: Option<String>,
pub desc: Option<String>,
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<String>,
pub desc: Option<String>,
pub deprecation: Option<String>,
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)

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),
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"))
}
};
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,
})
}
}

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),
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(),
})
}
}

View File

@ -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
///

View File

@ -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<String, FragmentDefinition>,
selection_set: SelectionSet,
variable_definitions: Option<Vec<VariableDefinition>>,
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(&registry_field.cache_control);
}
}
}

View File

@ -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<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 {
Scalar {
name: String,
@ -89,6 +168,7 @@ pub enum Type {
name: String,
description: Option<&'static str>,
fields: HashMap<String, Field>,
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(),
},
);
}

View File

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

View File

@ -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(),
}
})
}

View File

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

View File

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

View File

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

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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()

View File

@ -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() {

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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) {

View File

@ -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));
}

View File

@ -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 {

View File

@ -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) {

View File

@ -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 {

View File

@ -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,

View File

@ -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);

View File

@ -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,

View File

@ -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> {

View File

@ -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() {

View File

@ -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() {

View File

@ -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) {

View File

@ -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();
}
}

View File

@ -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) {

View File

@ -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 {

View File

@ -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()) {

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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<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> {
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);