Add CacheControl for object
This commit is contained in:
parent
788a3b558b
commit
e6bb9b82ed
|
@ -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 !
|
|
@ -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> {
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
22
src/lib.rs
22
src/lib.rs
|
@ -77,6 +77,7 @@ mod schema;
|
|||
mod subscription;
|
||||
mod types;
|
||||
mod validation;
|
||||
mod visitor;
|
||||
|
||||
/// Input value validators
|
||||
pub mod validators;
|
||||
|
@ -98,6 +99,7 @@ pub use context::{Context, Variables};
|
|||
pub use error::{ErrorWithPosition, PositionError, QueryError, QueryParseError};
|
||||
pub use graphql_parser::query::Value;
|
||||
pub use query::{PreparedQuery, QueryBuilder};
|
||||
pub use registry::CacheControl;
|
||||
pub use scalars::ID;
|
||||
pub use schema::Schema;
|
||||
pub use subscription::SubscribeBuilder;
|
||||
|
@ -132,18 +134,20 @@ pub use types::{EnumItem, EnumType};
|
|||
///
|
||||
/// # Macro parameters
|
||||
///
|
||||
/// | Attribute | description | Type | Optional |
|
||||
/// |-------------|---------------------------|----------|----------|
|
||||
/// | name | Object name | string | Y |
|
||||
/// | desc | Object description | string | Y |
|
||||
/// | Attribute | description | Type | Optional |
|
||||
/// |---------------|---------------------------|----------|----------|
|
||||
/// | name | Object name | string | Y |
|
||||
/// | desc | Object description | string | Y |
|
||||
/// | cache_control | Object cache control | [`CacheControl`](struct.CacheControl.html) | Y |
|
||||
///
|
||||
/// # Field parameters
|
||||
///
|
||||
/// | Attribute | description | Type | Optional |
|
||||
/// |-------------|---------------------------|----------|----------|
|
||||
/// | name | Field name | string | Y |
|
||||
/// | desc | Field description | string | Y |
|
||||
/// | deprecation | Field deprecation reason | string | Y |
|
||||
/// | Attribute | description | Type | Optional |
|
||||
/// |---------------|---------------------------|----------|----------|
|
||||
/// | name | Field name | string | Y |
|
||||
/// | desc | Field description | string | Y |
|
||||
/// | deprecation | Field deprecation reason | string | Y |
|
||||
/// | cache_control | Field cache control | [`CacheControl`](struct.CacheControl.html) | Y |
|
||||
///
|
||||
/// # Field argument parameters
|
||||
///
|
||||
|
|
42
src/query.rs
42
src/query.rs
|
@ -1,13 +1,14 @@
|
|||
use crate::context::Data;
|
||||
use crate::registry::Registry;
|
||||
use crate::registry::{CacheControl, Registry, Type};
|
||||
use crate::types::QueryRoot;
|
||||
use crate::validation::check_rules;
|
||||
use crate::visitor::{visit, Visitor, VisitorContext};
|
||||
use crate::{ContextBase, OutputValueType, Result};
|
||||
use crate::{ObjectType, QueryError, QueryParseError, Variables};
|
||||
use bytes::Bytes;
|
||||
use graphql_parser::parse_query;
|
||||
use graphql_parser::query::{
|
||||
Definition, FragmentDefinition, OperationDefinition, SelectionSet, VariableDefinition,
|
||||
Definition, Field, FragmentDefinition, OperationDefinition, SelectionSet, VariableDefinition,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
@ -49,6 +50,13 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
|
|||
let document = parse_query(self.source).map_err(|err| QueryParseError(err.to_string()))?;
|
||||
check_rules(self.registry, &document)?;
|
||||
|
||||
let cache_control = {
|
||||
let mut ctx = VisitorContext::new(self.registry, &document);
|
||||
let mut visitor = CacheControlVisitor::default();
|
||||
visit(&mut visitor, &mut ctx, &document);
|
||||
visitor.cache_control
|
||||
};
|
||||
|
||||
let mut fragments = HashMap::new();
|
||||
let mut selection_set = None;
|
||||
let mut variable_definitions = None;
|
||||
|
@ -106,6 +114,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
|
|||
})?,
|
||||
root: root.unwrap(),
|
||||
variable_definitions,
|
||||
cache_control,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -128,6 +137,7 @@ pub struct PreparedQuery<'a, Query, Mutation> {
|
|||
fragments: HashMap<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(®istry_field.cache_control);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ impl Type for EmptyMutation {
|
|||
name: "EmptyMutation".to_string(),
|
||||
description: None,
|
||||
fields: Default::default(),
|
||||
cache_control: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ impl Type for EmptySubscription {
|
|||
name: "EmptySubscription".to_string(),
|
||||
description: None,
|
||||
fields: Default::default(),
|
||||
cache_control: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
Loading…
Reference in New Issue
Block a user