2020-03-22 08:45:59 +00:00
|
|
|
use crate::error::RuleError;
|
2020-09-06 05:38:31 +00:00
|
|
|
use crate::parser::types::{
|
2020-09-22 18:59:48 +00:00
|
|
|
Directive, ExecutableDocument, Field, FragmentDefinition, FragmentSpread, InlineFragment, Name,
|
|
|
|
OperationDefinition, OperationType, Selection, SelectionSet, TypeCondition, Value,
|
|
|
|
VariableDefinition,
|
2020-03-08 12:35:36 +00:00
|
|
|
};
|
2020-05-15 02:08:37 +00:00
|
|
|
use crate::registry::{self, MetaType, MetaTypeName};
|
2020-09-08 08:21:27 +00:00
|
|
|
use crate::{Pos, Positioned, Variables};
|
2020-03-22 08:45:59 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
pub struct VisitorContext<'a> {
|
|
|
|
pub registry: &'a registry::Registry,
|
2020-07-11 02:05:30 +00:00
|
|
|
pub variables: Option<&'a Variables>,
|
2020-03-22 08:45:59 +00:00
|
|
|
pub errors: Vec<RuleError>,
|
2020-05-15 02:08:37 +00:00
|
|
|
type_stack: Vec<Option<&'a registry::MetaType>>,
|
|
|
|
input_type: Vec<Option<MetaTypeName<'a>>>,
|
2020-09-22 18:59:48 +00:00
|
|
|
fragments: &'a HashMap<Name, Positioned<FragmentDefinition>>,
|
2020-03-22 08:45:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> VisitorContext<'a> {
|
2020-07-11 02:05:30 +00:00
|
|
|
pub fn new(
|
|
|
|
registry: &'a registry::Registry,
|
2020-09-08 08:21:27 +00:00
|
|
|
doc: &'a ExecutableDocument,
|
2020-07-11 02:05:30 +00:00
|
|
|
variables: Option<&'a Variables>,
|
|
|
|
) -> Self {
|
2020-03-22 08:45:59 +00:00
|
|
|
Self {
|
|
|
|
registry,
|
2020-07-11 02:05:30 +00:00
|
|
|
variables,
|
2020-03-22 08:45:59 +00:00
|
|
|
errors: Default::default(),
|
|
|
|
type_stack: Default::default(),
|
2020-04-05 08:00:26 +00:00
|
|
|
input_type: Default::default(),
|
2020-09-22 18:59:48 +00:00
|
|
|
fragments: &doc.fragments,
|
2020-03-22 08:45:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2020-05-15 02:08:37 +00:00
|
|
|
ty: Option<&'a registry::MetaType>,
|
2020-03-22 08:45:59 +00:00
|
|
|
mut f: F,
|
|
|
|
) {
|
|
|
|
self.type_stack.push(ty);
|
|
|
|
f(self);
|
|
|
|
self.type_stack.pop();
|
|
|
|
}
|
|
|
|
|
2020-04-05 08:00:26 +00:00
|
|
|
pub fn with_input_type<F: FnMut(&mut VisitorContext<'a>)>(
|
|
|
|
&mut self,
|
2020-05-15 02:08:37 +00:00
|
|
|
ty: Option<MetaTypeName<'a>>,
|
2020-04-05 08:00:26 +00:00
|
|
|
mut f: F,
|
|
|
|
) {
|
|
|
|
self.input_type.push(ty);
|
|
|
|
f(self);
|
|
|
|
self.input_type.pop();
|
|
|
|
}
|
|
|
|
|
2020-05-15 02:08:37 +00:00
|
|
|
pub fn parent_type(&self) -> Option<&'a registry::MetaType> {
|
2020-03-22 08:45:59 +00:00
|
|
|
if self.type_stack.len() >= 2 {
|
2020-04-05 08:00:26 +00:00
|
|
|
self.type_stack
|
|
|
|
.get(self.type_stack.len() - 2)
|
|
|
|
.copied()
|
|
|
|
.flatten()
|
2020-03-22 08:45:59 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-15 02:08:37 +00:00
|
|
|
pub fn current_type(&self) -> Option<&'a registry::MetaType> {
|
2020-04-05 08:00:26 +00:00
|
|
|
self.type_stack.last().copied().flatten()
|
2020-03-22 08:45:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_known_fragment(&self, name: &str) -> bool {
|
|
|
|
self.fragments.contains_key(name)
|
|
|
|
}
|
|
|
|
|
2020-05-10 02:59:51 +00:00
|
|
|
pub fn fragment(&self, name: &str) -> Option<&'a Positioned<FragmentDefinition>> {
|
2020-09-22 18:59:48 +00:00
|
|
|
self.fragments.get(name)
|
2020-03-22 08:45:59 +00:00
|
|
|
}
|
|
|
|
}
|
2020-03-08 12:35:36 +00:00
|
|
|
|
|
|
|
pub trait Visitor<'a> {
|
2020-09-08 08:21:27 +00:00
|
|
|
fn enter_document(&mut self, _ctx: &mut VisitorContext<'a>, _doc: &'a ExecutableDocument) {}
|
|
|
|
fn exit_document(&mut self, _ctx: &mut VisitorContext<'a>, _doc: &'a ExecutableDocument) {}
|
2020-03-08 12:35:36 +00:00
|
|
|
|
|
|
|
fn enter_operation_definition(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-09-22 18:59:48 +00:00
|
|
|
_name: Option<&'a Name>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_operation_definition: &'a Positioned<OperationDefinition>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
fn exit_operation_definition(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-09-22 18:59:48 +00:00
|
|
|
_name: Option<&'a Name>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_operation_definition: &'a Positioned<OperationDefinition>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
|
|
|
|
fn enter_fragment_definition(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-09-22 18:59:48 +00:00
|
|
|
_name: &'a Name,
|
2020-05-10 02:59:51 +00:00
|
|
|
_fragment_definition: &'a Positioned<FragmentDefinition>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
fn exit_fragment_definition(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-09-22 18:59:48 +00:00
|
|
|
_name: &'a Name,
|
2020-05-10 02:59:51 +00:00
|
|
|
_fragment_definition: &'a Positioned<FragmentDefinition>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
|
|
|
|
fn enter_variable_definition(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_variable_definition: &'a Positioned<VariableDefinition>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
fn exit_variable_definition(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_variable_definition: &'a Positioned<VariableDefinition>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
|
2020-05-09 09:55:04 +00:00
|
|
|
fn enter_directive(
|
|
|
|
&mut self,
|
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_directive: &'a Positioned<Directive>,
|
2020-05-09 09:55:04 +00:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
fn exit_directive(
|
|
|
|
&mut self,
|
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_directive: &'a Positioned<Directive>,
|
2020-05-09 09:55:04 +00:00
|
|
|
) {
|
|
|
|
}
|
2020-03-08 12:35:36 +00:00
|
|
|
|
2020-03-09 12:39:46 +00:00
|
|
|
fn enter_argument(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-09-08 08:21:27 +00:00
|
|
|
_name: &'a Positioned<Name>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_value: &'a Positioned<Value>,
|
2020-03-09 12:39:46 +00:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
fn exit_argument(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-09-08 08:21:27 +00:00
|
|
|
_name: &'a Positioned<Name>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_value: &'a Positioned<Value>,
|
2020-03-09 12:39:46 +00:00
|
|
|
) {
|
|
|
|
}
|
2020-03-08 12:35:36 +00:00
|
|
|
|
2020-03-12 09:11:02 +00:00
|
|
|
fn enter_selection_set(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_selection_set: &'a Positioned<SelectionSet>,
|
2020-03-12 09:11:02 +00:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
fn exit_selection_set(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_selection_set: &'a Positioned<SelectionSet>,
|
2020-03-12 09:11:02 +00:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
|
2020-05-09 09:55:04 +00:00
|
|
|
fn enter_selection(
|
|
|
|
&mut self,
|
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_selection: &'a Positioned<Selection>,
|
2020-05-09 09:55:04 +00:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
fn exit_selection(
|
|
|
|
&mut self,
|
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_selection: &'a Positioned<Selection>,
|
2020-05-09 09:55:04 +00:00
|
|
|
) {
|
|
|
|
}
|
2020-03-08 12:35:36 +00:00
|
|
|
|
2020-05-10 02:59:51 +00:00
|
|
|
fn enter_field(&mut self, _ctx: &mut VisitorContext<'a>, _field: &'a Positioned<Field>) {}
|
|
|
|
fn exit_field(&mut self, _ctx: &mut VisitorContext<'a>, _field: &'a Positioned<Field>) {}
|
2020-03-08 12:35:36 +00:00
|
|
|
|
|
|
|
fn enter_fragment_spread(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_fragment_spread: &'a Positioned<FragmentSpread>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
fn exit_fragment_spread(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_fragment_spread: &'a Positioned<FragmentSpread>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
|
|
|
|
fn enter_inline_fragment(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_inline_fragment: &'a Positioned<InlineFragment>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
fn exit_inline_fragment(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
_ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
_inline_fragment: &'a Positioned<InlineFragment>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
}
|
2020-04-05 08:00:26 +00:00
|
|
|
|
|
|
|
fn enter_input_value(
|
|
|
|
&mut self,
|
|
|
|
_ctx: &mut VisitorContext<'a>,
|
|
|
|
_pos: Pos,
|
2020-05-15 02:08:37 +00:00
|
|
|
_expected_type: &Option<MetaTypeName<'a>>,
|
2020-04-05 08:00:26 +00:00
|
|
|
_value: &'a Value,
|
|
|
|
) {
|
|
|
|
}
|
|
|
|
fn exit_input_value(
|
|
|
|
&mut self,
|
|
|
|
_ctx: &mut VisitorContext<'a>,
|
|
|
|
_pos: Pos,
|
2020-05-15 02:08:37 +00:00
|
|
|
_expected_type: &Option<MetaTypeName<'a>>,
|
2020-04-05 08:00:26 +00:00
|
|
|
_value: &Value,
|
|
|
|
) {
|
|
|
|
}
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct VisitorNil;
|
|
|
|
|
|
|
|
impl VisitorNil {
|
|
|
|
pub fn with<V>(self, visitor: V) -> VisitorCons<V, Self> {
|
|
|
|
VisitorCons(visitor, self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct VisitorCons<A, B>(A, B);
|
|
|
|
|
|
|
|
impl<A, B> VisitorCons<A, B> {
|
|
|
|
pub fn with<V>(self, visitor: V) -> VisitorCons<V, Self> {
|
|
|
|
VisitorCons(visitor, self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Visitor<'a> for VisitorNil {}
|
|
|
|
|
|
|
|
impl<'a, A, B> Visitor<'a> for VisitorCons<A, B>
|
|
|
|
where
|
|
|
|
A: Visitor<'a> + 'a,
|
|
|
|
B: Visitor<'a> + 'a,
|
|
|
|
{
|
2020-09-08 08:21:27 +00:00
|
|
|
fn enter_document(&mut self, ctx: &mut VisitorContext<'a>, doc: &'a ExecutableDocument) {
|
2020-03-08 12:35:36 +00:00
|
|
|
self.0.enter_document(ctx, doc);
|
|
|
|
self.1.enter_document(ctx, doc);
|
|
|
|
}
|
|
|
|
|
2020-09-08 08:21:27 +00:00
|
|
|
fn exit_document(&mut self, ctx: &mut VisitorContext<'a>, doc: &'a ExecutableDocument) {
|
2020-03-08 12:35:36 +00:00
|
|
|
self.0.exit_document(ctx, doc);
|
|
|
|
self.1.exit_document(ctx, doc);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn enter_operation_definition(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-09-22 18:59:48 +00:00
|
|
|
name: Option<&'a Name>,
|
2020-05-10 02:59:51 +00:00
|
|
|
operation_definition: &'a Positioned<OperationDefinition>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
2020-09-22 18:59:48 +00:00
|
|
|
self.0
|
|
|
|
.enter_operation_definition(ctx, name, operation_definition);
|
|
|
|
self.1
|
|
|
|
.enter_operation_definition(ctx, name, operation_definition);
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn exit_operation_definition(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-09-22 18:59:48 +00:00
|
|
|
name: Option<&'a Name>,
|
2020-05-10 02:59:51 +00:00
|
|
|
operation_definition: &'a Positioned<OperationDefinition>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
2020-09-22 18:59:48 +00:00
|
|
|
self.0
|
|
|
|
.exit_operation_definition(ctx, name, operation_definition);
|
|
|
|
self.1
|
|
|
|
.exit_operation_definition(ctx, name, operation_definition);
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn enter_fragment_definition(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-09-22 18:59:48 +00:00
|
|
|
name: &'a Name,
|
2020-05-10 02:59:51 +00:00
|
|
|
fragment_definition: &'a Positioned<FragmentDefinition>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
2020-09-22 18:59:48 +00:00
|
|
|
self.0
|
|
|
|
.enter_fragment_definition(ctx, name, fragment_definition);
|
|
|
|
self.1
|
|
|
|
.enter_fragment_definition(ctx, name, fragment_definition);
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn exit_fragment_definition(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-09-22 18:59:48 +00:00
|
|
|
name: &'a Name,
|
2020-05-10 02:59:51 +00:00
|
|
|
fragment_definition: &'a Positioned<FragmentDefinition>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
2020-09-22 18:59:48 +00:00
|
|
|
self.0
|
|
|
|
.exit_fragment_definition(ctx, name, fragment_definition);
|
|
|
|
self.1
|
|
|
|
.exit_fragment_definition(ctx, name, fragment_definition);
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn enter_variable_definition(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
variable_definition: &'a Positioned<VariableDefinition>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
self.0.enter_variable_definition(ctx, variable_definition);
|
|
|
|
self.1.enter_variable_definition(ctx, variable_definition);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn exit_variable_definition(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
variable_definition: &'a Positioned<VariableDefinition>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
self.0.exit_variable_definition(ctx, variable_definition);
|
|
|
|
self.1.exit_variable_definition(ctx, variable_definition);
|
|
|
|
}
|
|
|
|
|
2020-05-10 02:59:51 +00:00
|
|
|
fn enter_directive(
|
|
|
|
&mut self,
|
|
|
|
ctx: &mut VisitorContext<'a>,
|
|
|
|
directive: &'a Positioned<Directive>,
|
|
|
|
) {
|
2020-03-08 12:35:36 +00:00
|
|
|
self.0.enter_directive(ctx, directive);
|
|
|
|
self.1.enter_directive(ctx, directive);
|
|
|
|
}
|
|
|
|
|
2020-05-10 02:59:51 +00:00
|
|
|
fn exit_directive(
|
|
|
|
&mut self,
|
|
|
|
ctx: &mut VisitorContext<'a>,
|
|
|
|
directive: &'a Positioned<Directive>,
|
|
|
|
) {
|
2020-03-08 12:35:36 +00:00
|
|
|
self.0.exit_directive(ctx, directive);
|
|
|
|
self.1.exit_directive(ctx, directive);
|
|
|
|
}
|
|
|
|
|
2020-03-09 12:39:46 +00:00
|
|
|
fn enter_argument(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-09-08 08:21:27 +00:00
|
|
|
name: &'a Positioned<Name>,
|
2020-05-10 02:59:51 +00:00
|
|
|
value: &'a Positioned<Value>,
|
2020-03-09 12:39:46 +00:00
|
|
|
) {
|
2020-05-09 09:55:04 +00:00
|
|
|
self.0.enter_argument(ctx, name, value);
|
|
|
|
self.1.enter_argument(ctx, name, value);
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
|
|
|
|
2020-03-09 12:39:46 +00:00
|
|
|
fn exit_argument(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-09-08 08:21:27 +00:00
|
|
|
name: &'a Positioned<Name>,
|
2020-05-10 02:59:51 +00:00
|
|
|
value: &'a Positioned<Value>,
|
2020-03-09 12:39:46 +00:00
|
|
|
) {
|
2020-05-09 09:55:04 +00:00
|
|
|
self.0.exit_argument(ctx, name, value);
|
|
|
|
self.1.exit_argument(ctx, name, value);
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
|
|
|
|
2020-03-12 09:11:02 +00:00
|
|
|
fn enter_selection_set(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
selection_set: &'a Positioned<SelectionSet>,
|
2020-03-12 09:11:02 +00:00
|
|
|
) {
|
|
|
|
self.0.enter_selection_set(ctx, selection_set);
|
|
|
|
self.1.enter_selection_set(ctx, selection_set);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn exit_selection_set(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
selection_set: &'a Positioned<SelectionSet>,
|
2020-03-12 09:11:02 +00:00
|
|
|
) {
|
|
|
|
self.0.exit_selection_set(ctx, selection_set);
|
|
|
|
self.1.exit_selection_set(ctx, selection_set);
|
|
|
|
}
|
|
|
|
|
2020-05-10 02:59:51 +00:00
|
|
|
fn enter_selection(
|
|
|
|
&mut self,
|
|
|
|
ctx: &mut VisitorContext<'a>,
|
|
|
|
selection: &'a Positioned<Selection>,
|
|
|
|
) {
|
2020-03-08 12:35:36 +00:00
|
|
|
self.0.enter_selection(ctx, selection);
|
|
|
|
self.1.enter_selection(ctx, selection);
|
|
|
|
}
|
|
|
|
|
2020-05-10 02:59:51 +00:00
|
|
|
fn exit_selection(
|
|
|
|
&mut self,
|
|
|
|
ctx: &mut VisitorContext<'a>,
|
|
|
|
selection: &'a Positioned<Selection>,
|
|
|
|
) {
|
2020-03-08 12:35:36 +00:00
|
|
|
self.0.exit_selection(ctx, selection);
|
|
|
|
self.1.exit_selection(ctx, selection);
|
|
|
|
}
|
|
|
|
|
2020-05-10 02:59:51 +00:00
|
|
|
fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Positioned<Field>) {
|
2020-03-08 12:35:36 +00:00
|
|
|
self.0.enter_field(ctx, field);
|
|
|
|
self.1.enter_field(ctx, field);
|
|
|
|
}
|
|
|
|
|
2020-05-10 02:59:51 +00:00
|
|
|
fn exit_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Positioned<Field>) {
|
2020-03-08 12:35:36 +00:00
|
|
|
self.0.exit_field(ctx, field);
|
|
|
|
self.1.exit_field(ctx, field);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn enter_fragment_spread(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
fragment_spread: &'a Positioned<FragmentSpread>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
self.0.enter_fragment_spread(ctx, fragment_spread);
|
|
|
|
self.1.enter_fragment_spread(ctx, fragment_spread);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn exit_fragment_spread(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
fragment_spread: &'a Positioned<FragmentSpread>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
self.0.exit_fragment_spread(ctx, fragment_spread);
|
|
|
|
self.1.exit_fragment_spread(ctx, fragment_spread);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn enter_inline_fragment(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
inline_fragment: &'a Positioned<InlineFragment>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
self.0.enter_inline_fragment(ctx, inline_fragment);
|
|
|
|
self.1.enter_inline_fragment(ctx, inline_fragment);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn exit_inline_fragment(
|
|
|
|
&mut self,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
inline_fragment: &'a Positioned<InlineFragment>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
self.0.exit_inline_fragment(ctx, inline_fragment);
|
|
|
|
self.1.exit_inline_fragment(ctx, inline_fragment);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-08 08:30:29 +00:00
|
|
|
pub fn visit<'a, V: Visitor<'a>>(
|
|
|
|
v: &mut V,
|
|
|
|
ctx: &mut VisitorContext<'a>,
|
|
|
|
doc: &'a ExecutableDocument,
|
|
|
|
) {
|
2020-03-08 12:35:36 +00:00
|
|
|
v.enter_document(ctx, doc);
|
|
|
|
|
2020-09-22 18:59:48 +00:00
|
|
|
for (name, fragment) in &doc.fragments {
|
|
|
|
ctx.with_type(
|
|
|
|
ctx.registry
|
|
|
|
.types
|
|
|
|
.get(fragment.node.type_condition.node.on.node.as_str()),
|
|
|
|
|ctx| visit_fragment_definition(v, ctx, name, fragment),
|
|
|
|
)
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
2020-09-22 18:59:48 +00:00
|
|
|
|
|
|
|
for (name, operation) in doc.operations.iter() {
|
|
|
|
visit_operation_definition(v, ctx, name, operation);
|
|
|
|
}
|
|
|
|
|
|
|
|
v.exit_document(ctx, doc);
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_operation_definition<'a, V: Visitor<'a>>(
|
|
|
|
v: &mut V,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-09-22 18:59:48 +00:00
|
|
|
name: Option<&'a Name>,
|
2020-05-10 02:59:51 +00:00
|
|
|
operation: &'a Positioned<OperationDefinition>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
2020-09-22 18:59:48 +00:00
|
|
|
v.enter_operation_definition(ctx, name, operation);
|
2020-09-06 05:38:31 +00:00
|
|
|
let root_name = match &operation.node.ty {
|
|
|
|
OperationType::Query => Some(&*ctx.registry.query_type),
|
|
|
|
OperationType::Mutation => ctx.registry.mutation_type.as_deref(),
|
|
|
|
OperationType::Subscription => ctx.registry.subscription_type.as_deref(),
|
|
|
|
};
|
|
|
|
if let Some(root_name) = root_name {
|
|
|
|
ctx.with_type(Some(&ctx.registry.types[&*root_name]), |ctx| {
|
|
|
|
visit_variable_definitions(v, ctx, &operation.node.variable_definitions);
|
|
|
|
visit_directives(v, ctx, &operation.node.directives);
|
|
|
|
visit_selection_set(v, ctx, &operation.node.selection_set);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
ctx.report_error(
|
|
|
|
vec![operation.pos],
|
|
|
|
// The only one with an irregular plural, "query", is always present
|
|
|
|
format!("Schema is not configured for {}s.", operation.node.ty),
|
|
|
|
);
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
2020-09-22 18:59:48 +00:00
|
|
|
v.exit_operation_definition(ctx, name, operation);
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_selection_set<'a, V: Visitor<'a>>(
|
|
|
|
v: &mut V,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
selection_set: &'a Positioned<SelectionSet>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
2020-09-06 05:38:31 +00:00
|
|
|
if !selection_set.node.items.is_empty() {
|
2020-03-25 07:07:16 +00:00
|
|
|
v.enter_selection_set(ctx, selection_set);
|
2020-09-06 05:38:31 +00:00
|
|
|
for selection in &selection_set.node.items {
|
2020-03-25 07:07:16 +00:00
|
|
|
visit_selection(v, ctx, selection);
|
|
|
|
}
|
|
|
|
v.exit_selection_set(ctx, selection_set);
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_selection<'a, V: Visitor<'a>>(
|
|
|
|
v: &mut V,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
selection: &'a Positioned<Selection>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
v.enter_selection(ctx, selection);
|
2020-05-09 09:55:04 +00:00
|
|
|
match &selection.node {
|
2020-03-09 04:08:50 +00:00
|
|
|
Selection::Field(field) => {
|
2020-09-06 05:38:31 +00:00
|
|
|
if field.node.name.node != "__typename" {
|
2020-04-05 08:00:26 +00:00
|
|
|
ctx.with_type(
|
|
|
|
ctx.current_type()
|
2020-09-06 05:38:31 +00:00
|
|
|
.and_then(|ty| ty.field_by_name(&field.node.name.node))
|
2020-04-05 08:00:26 +00:00
|
|
|
.and_then(|schema_field| {
|
2020-04-06 10:30:38 +00:00
|
|
|
ctx.registry.concrete_type_by_name(&schema_field.ty)
|
2020-04-05 08:00:26 +00:00
|
|
|
}),
|
|
|
|
|ctx| {
|
|
|
|
visit_field(v, ctx, field);
|
|
|
|
},
|
|
|
|
);
|
2020-03-09 04:08:50 +00:00
|
|
|
}
|
|
|
|
}
|
2020-03-08 12:35:36 +00:00
|
|
|
Selection::FragmentSpread(fragment_spread) => {
|
|
|
|
visit_fragment_spread(v, ctx, fragment_spread)
|
|
|
|
}
|
|
|
|
Selection::InlineFragment(inline_fragment) => {
|
2020-09-06 06:16:36 +00:00
|
|
|
if let Some(TypeCondition { on: name }) = &inline_fragment
|
|
|
|
.node
|
|
|
|
.type_condition
|
|
|
|
.as_ref()
|
|
|
|
.map(|c| &c.node)
|
2020-05-09 09:55:04 +00:00
|
|
|
{
|
2020-09-08 08:21:27 +00:00
|
|
|
ctx.with_type(ctx.registry.types.get(name.node.as_str()), |ctx| {
|
2020-04-05 08:00:26 +00:00
|
|
|
visit_inline_fragment(v, ctx, inline_fragment)
|
|
|
|
});
|
2020-03-09 04:08:50 +00:00
|
|
|
}
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
v.exit_selection(ctx, selection);
|
|
|
|
}
|
|
|
|
|
2020-05-09 09:55:04 +00:00
|
|
|
fn visit_field<'a, V: Visitor<'a>>(
|
|
|
|
v: &mut V,
|
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
field: &'a Positioned<Field>,
|
2020-05-09 09:55:04 +00:00
|
|
|
) {
|
2020-03-08 12:35:36 +00:00
|
|
|
v.enter_field(ctx, field);
|
2020-04-05 08:00:26 +00:00
|
|
|
|
2020-09-06 05:38:31 +00:00
|
|
|
for (name, value) in &field.node.arguments {
|
2020-05-09 09:55:04 +00:00
|
|
|
v.enter_argument(ctx, name, value);
|
2020-04-05 08:00:26 +00:00
|
|
|
let expected_ty = ctx
|
|
|
|
.parent_type()
|
2020-09-06 05:38:31 +00:00
|
|
|
.and_then(|ty| ty.field_by_name(&field.node.name.node))
|
|
|
|
.and_then(|schema_field| schema_field.args.get(&*name.node))
|
2020-05-15 02:08:37 +00:00
|
|
|
.map(|input_ty| MetaTypeName::create(&input_ty.ty));
|
2020-04-05 08:00:26 +00:00
|
|
|
ctx.with_input_type(expected_ty, |ctx| {
|
2020-09-06 05:38:31 +00:00
|
|
|
visit_input_value(v, ctx, field.pos, expected_ty, &value.node)
|
2020-04-05 08:00:26 +00:00
|
|
|
});
|
2020-05-09 09:55:04 +00:00
|
|
|
v.exit_argument(ctx, name, value);
|
2020-04-05 08:00:26 +00:00
|
|
|
}
|
|
|
|
|
2020-09-06 05:38:31 +00:00
|
|
|
visit_directives(v, ctx, &field.node.directives);
|
|
|
|
visit_selection_set(v, ctx, &field.node.selection_set);
|
2020-03-08 12:35:36 +00:00
|
|
|
v.exit_field(ctx, field);
|
|
|
|
}
|
|
|
|
|
2020-04-05 08:00:26 +00:00
|
|
|
fn visit_input_value<'a, V: Visitor<'a>>(
|
2020-03-08 12:35:36 +00:00
|
|
|
v: &mut V,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-03-09 12:39:46 +00:00
|
|
|
pos: Pos,
|
2020-05-15 02:08:37 +00:00
|
|
|
expected_ty: Option<MetaTypeName<'a>>,
|
2020-04-05 08:00:26 +00:00
|
|
|
value: &'a Value,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
2020-04-05 08:00:26 +00:00
|
|
|
v.enter_input_value(ctx, pos, &expected_ty, value);
|
|
|
|
|
|
|
|
match value {
|
|
|
|
Value::List(values) => {
|
|
|
|
if let Some(expected_ty) = expected_ty {
|
|
|
|
let elem_ty = expected_ty.unwrap_non_null();
|
2020-05-15 02:08:37 +00:00
|
|
|
if let MetaTypeName::List(expected_ty) = elem_ty {
|
2020-04-05 08:00:26 +00:00
|
|
|
values.iter().for_each(|value| {
|
2020-05-15 02:08:37 +00:00
|
|
|
visit_input_value(
|
|
|
|
v,
|
|
|
|
ctx,
|
|
|
|
pos,
|
|
|
|
Some(MetaTypeName::create(expected_ty)),
|
|
|
|
value,
|
|
|
|
)
|
2020-04-05 08:00:26 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Value::Object(values) => {
|
|
|
|
if let Some(expected_ty) = expected_ty {
|
|
|
|
let expected_ty = expected_ty.unwrap_non_null();
|
2020-05-15 02:08:37 +00:00
|
|
|
if let MetaTypeName::Named(expected_ty) = expected_ty {
|
2020-04-05 08:00:26 +00:00
|
|
|
if let Some(ty) = ctx
|
|
|
|
.registry
|
|
|
|
.types
|
2020-05-15 02:08:37 +00:00
|
|
|
.get(MetaTypeName::concrete_typename(expected_ty))
|
2020-04-05 08:00:26 +00:00
|
|
|
{
|
2020-05-15 02:08:37 +00:00
|
|
|
if let MetaType::InputObject { input_fields, .. } = ty {
|
2020-04-05 08:00:26 +00:00
|
|
|
for (item_key, item_value) in values {
|
2020-09-08 08:21:27 +00:00
|
|
|
if let Some(input_value) = input_fields.get(item_key.as_str()) {
|
2020-04-05 08:00:26 +00:00
|
|
|
visit_input_value(
|
|
|
|
v,
|
|
|
|
ctx,
|
|
|
|
pos,
|
2020-05-15 02:08:37 +00:00
|
|
|
Some(MetaTypeName::create(&input_value.ty)),
|
2020-04-05 08:00:26 +00:00
|
|
|
item_value,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
2020-04-05 08:00:26 +00:00
|
|
|
|
|
|
|
v.exit_input_value(ctx, pos, &expected_ty, value);
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_variable_definitions<'a, V: Visitor<'a>>(
|
|
|
|
v: &mut V,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
variable_definitions: &'a [Positioned<VariableDefinition>],
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
for d in variable_definitions {
|
|
|
|
v.enter_variable_definition(ctx, d);
|
|
|
|
v.exit_variable_definition(ctx, d);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_directives<'a, V: Visitor<'a>>(
|
|
|
|
v: &mut V,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
directives: &'a [Positioned<Directive>],
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
for d in directives {
|
|
|
|
v.enter_directive(ctx, d);
|
2020-04-05 08:00:26 +00:00
|
|
|
|
2020-09-08 08:21:27 +00:00
|
|
|
let schema_directive = ctx.registry.directives.get(d.node.name.node.as_str());
|
2020-04-05 08:00:26 +00:00
|
|
|
|
2020-09-06 05:38:31 +00:00
|
|
|
for (name, value) in &d.node.arguments {
|
2020-05-09 09:55:04 +00:00
|
|
|
v.enter_argument(ctx, name, value);
|
2020-04-05 08:00:26 +00:00
|
|
|
let expected_ty = schema_directive
|
2020-09-06 05:38:31 +00:00
|
|
|
.and_then(|schema_directive| schema_directive.args.get(&*name.node))
|
2020-05-15 02:08:37 +00:00
|
|
|
.map(|input_ty| MetaTypeName::create(&input_ty.ty));
|
2020-04-05 08:00:26 +00:00
|
|
|
ctx.with_input_type(expected_ty, |ctx| {
|
2020-09-06 05:38:31 +00:00
|
|
|
visit_input_value(v, ctx, d.pos, expected_ty, &value.node)
|
2020-04-05 08:00:26 +00:00
|
|
|
});
|
2020-05-09 09:55:04 +00:00
|
|
|
v.exit_argument(ctx, name, value);
|
2020-04-05 08:00:26 +00:00
|
|
|
}
|
|
|
|
|
2020-03-08 12:35:36 +00:00
|
|
|
v.exit_directive(ctx, d);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_fragment_definition<'a, V: Visitor<'a>>(
|
|
|
|
v: &mut V,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-09-22 18:59:48 +00:00
|
|
|
name: &'a Name,
|
2020-05-10 02:59:51 +00:00
|
|
|
fragment: &'a Positioned<FragmentDefinition>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
2020-09-22 18:59:48 +00:00
|
|
|
v.enter_fragment_definition(ctx, name, fragment);
|
2020-09-06 05:38:31 +00:00
|
|
|
visit_directives(v, ctx, &fragment.node.directives);
|
|
|
|
visit_selection_set(v, ctx, &fragment.node.selection_set);
|
2020-09-22 18:59:48 +00:00
|
|
|
v.exit_fragment_definition(ctx, name, fragment);
|
2020-03-08 12:35:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_fragment_spread<'a, V: Visitor<'a>>(
|
|
|
|
v: &mut V,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
fragment_spread: &'a Positioned<FragmentSpread>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
v.enter_fragment_spread(ctx, fragment_spread);
|
2020-09-06 05:38:31 +00:00
|
|
|
visit_directives(v, ctx, &fragment_spread.node.directives);
|
2020-03-08 12:35:36 +00:00
|
|
|
v.exit_fragment_spread(ctx, fragment_spread);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_inline_fragment<'a, V: Visitor<'a>>(
|
|
|
|
v: &mut V,
|
2020-03-22 08:45:59 +00:00
|
|
|
ctx: &mut VisitorContext<'a>,
|
2020-05-10 02:59:51 +00:00
|
|
|
inline_fragment: &'a Positioned<InlineFragment>,
|
2020-03-08 12:35:36 +00:00
|
|
|
) {
|
|
|
|
v.enter_inline_fragment(ctx, inline_fragment);
|
2020-09-06 05:38:31 +00:00
|
|
|
visit_directives(v, ctx, &inline_fragment.node.directives);
|
|
|
|
visit_selection_set(v, ctx, &inline_fragment.node.selection_set);
|
2020-03-08 12:35:36 +00:00
|
|
|
v.exit_inline_fragment(ctx, inline_fragment);
|
|
|
|
}
|