Skip to main content

graphql_tools/ast/
operation_visitor.rs

1use std::collections::HashMap;
2
3use crate::parser::query::TypeCondition;
4
5use crate::static_graphql::{
6    query::{self, *},
7    schema::{self},
8};
9
10/// OperationVisitor
11pub struct OperationVisitorContext<'a> {
12    pub schema: &'a schema::Document,
13    pub operation: &'a query::Document,
14    pub known_fragments: HashMap<&'a str, &'a FragmentDefinition>,
15    pub directives: HashMap<String, schema::DirectiveDefinition>,
16
17    type_stack: Vec<Option<&'a schema::TypeDefinition>>,
18    parent_type_stack: Vec<Option<&'a schema::TypeDefinition>>,
19    input_type_stack: Vec<Option<&'a schema::TypeDefinition>>,
20    type_literal_stack: Vec<Option<Type>>,
21    input_type_literal_stack: Vec<Option<&'a Type>>,
22    input_type_default_stack: Vec<bool>,
23    field_stack: Vec<Option<&'a schema::Field>>,
24}
25
26impl<'a> OperationVisitorContext<'a> {
27    pub fn new(operation: &'a Document, schema: &'a schema::Document) -> Self {
28        OperationVisitorContext {
29            schema,
30            operation,
31            type_stack: vec![],
32            parent_type_stack: vec![],
33            input_type_stack: vec![],
34            type_literal_stack: vec![],
35            input_type_literal_stack: vec![],
36            input_type_default_stack: vec![],
37            field_stack: vec![],
38            known_fragments: HashMap::from_iter(operation.definitions.iter().filter_map(|def| {
39                match def {
40                    Definition::Fragment(fragment) => Some((fragment.name.as_str(), fragment)),
41                    _ => None,
42                }
43            })),
44            directives: HashMap::<String, schema::DirectiveDefinition>::from_iter(
45                schema.definitions.iter().filter_map(|def| match def {
46                    schema::Definition::DirectiveDefinition(directive_def) => {
47                        Some((directive_def.name.clone(), directive_def.clone()))
48                    }
49                    _ => None,
50                }),
51            ),
52        }
53    }
54
55    pub fn with_type<Func>(&mut self, t: Option<&Type>, func: Func)
56    where
57        Func: FnOnce(&mut OperationVisitorContext<'a>),
58    {
59        if let Some(t) = t {
60            self.type_stack
61                .push(self.schema.type_by_name(t.inner_type()));
62        } else {
63            self.type_stack.push(None);
64        }
65
66        self.type_literal_stack.push(t.cloned());
67        func(self);
68        self.type_literal_stack.pop();
69        self.type_stack.pop();
70    }
71
72    pub fn with_parent_type<Func>(&mut self, func: Func)
73    where
74        Func: FnOnce(&mut OperationVisitorContext<'a>),
75    {
76        self.parent_type_stack
77            .push(*self.type_stack.last().unwrap_or(&None));
78        func(self);
79        self.parent_type_stack.pop();
80    }
81
82    pub fn with_field<'f, Func>(&mut self, f: Option<&'f schema::Field>, func: Func)
83    where
84        Func: FnOnce(&mut OperationVisitorContext<'a>),
85        'f: 'a,
86    {
87        if let Some(f) = f {
88            self.field_stack.push(Some(f));
89        } else {
90            self.field_stack.push(None);
91        }
92
93        func(self);
94        self.field_stack.pop();
95    }
96
97    pub fn with_input_type<Func>(&mut self, t: Option<&'a Type>, func: Func)
98    where
99        Func: FnOnce(&mut OperationVisitorContext<'a>),
100    {
101        self.with_input_type_and_default(t, false, func);
102    }
103
104    pub fn with_input_type_and_default<Func>(
105        &mut self,
106        t: Option<&'a Type>,
107        has_default: bool,
108        func: Func,
109    ) where
110        Func: FnOnce(&mut OperationVisitorContext<'a>),
111    {
112        if let Some(t) = t {
113            self.input_type_stack
114                .push(self.schema.type_by_name(t.inner_type()));
115        } else {
116            self.input_type_stack.push(None);
117        }
118
119        self.input_type_literal_stack.push(t);
120        self.input_type_default_stack.push(has_default);
121        func(self);
122        self.input_type_default_stack.pop();
123        self.input_type_literal_stack.pop();
124        self.input_type_stack.pop();
125    }
126
127    pub fn current_input_type_has_default(&self) -> bool {
128        *self.input_type_default_stack.last().unwrap_or(&false)
129    }
130
131    pub fn current_type(&self) -> Option<&schema::TypeDefinition> {
132        self.type_stack.last().unwrap_or(&None).as_deref()
133    }
134
135    pub fn current_input_type(&self) -> Option<&schema::TypeDefinition> {
136        self.input_type_stack.last().unwrap_or(&None).as_deref()
137    }
138
139    pub fn current_parent_type(&self) -> Option<&'a schema::TypeDefinition> {
140        *self.parent_type_stack.last().unwrap_or(&None)
141    }
142
143    pub fn current_type_literal(&self) -> Option<&Type> {
144        self.type_literal_stack.last().unwrap_or(&None).as_ref()
145    }
146
147    pub fn current_input_type_literal(&self) -> Option<&'a Type> {
148        *self.input_type_literal_stack.last().unwrap_or(&None)
149    }
150
151    pub fn current_field(&self) -> Option<&schema::Field> {
152        self.field_stack.last().unwrap_or(&None).as_deref()
153    }
154}
155
156pub fn visit_document<'a, Visitor, UserContext>(
157    visitor: &mut Visitor,
158    document: &'a Document,
159    context: &mut OperationVisitorContext<'a>,
160    user_context: &mut UserContext,
161) where
162    Visitor: OperationVisitor<'a, UserContext>,
163{
164    visitor.enter_document(context, user_context, document);
165    visit_definitions(visitor, &document.definitions, context, user_context);
166    visitor.leave_document(context, user_context, document);
167}
168
169fn visit_definitions<'a, Visitor, UserContext>(
170    visitor: &mut Visitor,
171    definitions: &'a Vec<Definition>,
172    context: &mut OperationVisitorContext<'a>,
173    user_context: &mut UserContext,
174) where
175    Visitor: OperationVisitor<'a, UserContext>,
176{
177    for definition in definitions {
178        let schema_type_name = match definition {
179            Definition::Fragment(fragment) => {
180                let TypeCondition::On(name) = &fragment.type_condition;
181                Some(name)
182            }
183            Definition::Operation(operation) => match operation {
184                OperationDefinition::Query(_) => Some(&context.schema.query_type().name),
185                OperationDefinition::SelectionSet(_) => Some(&context.schema.query_type().name),
186                OperationDefinition::Mutation(_) => {
187                    context.schema.mutation_type().map(|t| &t.name).or_else(|| {
188                        // Awkward hack but enables me to move forward
189                        // Somehow the `mutation_type()` gives None, even though `Mutation` type is defined in the schema.
190                        if let Some(type_definition) = context.schema.type_by_name("Mutation") {
191                            return match type_definition {
192                                crate::parser::schema::TypeDefinition::Object(object_type) => {
193                                    Some(&object_type.name)
194                                }
195                                _ => None,
196                            };
197                        }
198
199                        None
200                    })
201                }
202                OperationDefinition::Subscription(_) => {
203                    context
204                        .schema
205                        .subscription_type()
206                        .map(|t| &t.name)
207                        .or_else(|| {
208                            // Awkward hack but enables me to move forward
209                            // Somehow the `subscription_type()` gives None, even though `Subscription` type is defined in the schema.
210                            if let Some(type_definition) =
211                                context.schema.type_by_name("Subscription")
212                            {
213                                return match type_definition {
214                                    crate::parser::schema::TypeDefinition::Object(object_type) => {
215                                        Some(&object_type.name)
216                                    }
217                                    _ => None,
218                                };
219                            }
220
221                            None
222                        })
223                }
224            },
225        };
226
227        let schema_type = schema_type_name.map(|v| Type::NamedType(v.clone()));
228        context.with_type(schema_type.as_ref(), |context| match definition {
229            Definition::Fragment(fragment) => {
230                visit_fragment_definition(visitor, fragment, context, user_context)
231            }
232            Definition::Operation(operation) => {
233                visit_operation_definition(visitor, operation, context, user_context)
234            }
235        });
236    }
237}
238
239fn visit_directives<'a, Visitor, UserContext>(
240    visitor: &mut Visitor,
241    directives: &'a [Directive],
242    context: &mut OperationVisitorContext<'a>,
243    user_context: &mut UserContext,
244) where
245    Visitor: OperationVisitor<'a, UserContext>,
246{
247    for directive in directives {
248        let directive_def_args = context
249            .schema
250            .directive_by_name(&directive.name)
251            .map(|def| &def.arguments);
252
253        visitor.enter_directive(context, user_context, directive);
254        visit_arguments(
255            visitor,
256            directive_def_args,
257            &directive.arguments,
258            context,
259            user_context,
260        );
261        visitor.leave_directive(context, user_context, directive);
262    }
263}
264
265fn visit_arguments<'a, Visitor, UserContext>(
266    visitor: &mut Visitor,
267    arguments_definition: Option<&'a Vec<schema::InputValue>>,
268    arguments: &'a Vec<(String, Value)>,
269    context: &mut OperationVisitorContext<'a>,
270    user_context: &mut UserContext,
271) where
272    Visitor: OperationVisitor<'a, UserContext>,
273{
274    for argument in arguments {
275        let arg_def = arguments_definition
276            .and_then(|argument_defs| argument_defs.iter().find(|a| a.name.eq(&argument.0)));
277
278        let arg_type = arg_def.map(|a| &a.value_type);
279        let has_default = arg_def.and_then(|a| a.default_value.as_ref()).is_some();
280
281        context.with_input_type_and_default(arg_type, has_default, |context| {
282            visitor.enter_argument(context, user_context, argument);
283            visit_input_value(visitor, &argument.1, context, user_context);
284            visitor.leave_argument(context, user_context, argument);
285        })
286    }
287}
288
289fn visit_input_value<'a, Visitor, UserContext>(
290    visitor: &mut Visitor,
291    input_value: &'a Value,
292    context: &mut OperationVisitorContext<'a>,
293    user_context: &mut UserContext,
294) where
295    Visitor: OperationVisitor<'a, UserContext>,
296{
297    match input_value {
298        Value::Boolean(_) | Value::Float(_) | Value::Int(_) | Value::String(_) => {
299            visitor.enter_scalar_value(context, user_context, input_value);
300            visitor.leave_scalar_value(context, user_context, input_value);
301        }
302        Value::Null => {
303            visitor.enter_null_value(context, user_context, ());
304            visitor.leave_null_value(context, user_context, ());
305        }
306        Value::Enum(v) => {
307            visitor.enter_enum_value(context, user_context, v);
308            visitor.leave_enum_value(context, user_context, v);
309        }
310        Value::List(v) => {
311            visitor.enter_list_value(context, user_context, v);
312
313            let input_type = context.current_input_type_literal().and_then(|t| match t {
314                Type::ListType(inner_type) => Some(inner_type.as_ref()),
315                _ => None,
316            });
317
318            context.with_input_type(input_type, |context| {
319                for item in v {
320                    visit_input_value(visitor, item, context, user_context)
321                }
322            });
323
324            visitor.leave_list_value(context, user_context, v);
325        }
326        Value::Object(v) => {
327            visitor.enter_object_value(context, user_context, v);
328
329            for pair in v {
330                let input_field = context
331                    .current_input_type_literal()
332                    .and_then(|v| context.schema.type_by_name(v.inner_type()))
333                    .and_then(|v| v.input_field_by_name(&pair.0));
334                let input_type = input_field.map(|field| &field.value_type);
335                let has_default = input_field
336                    .and_then(|field| field.default_value.as_ref())
337                    .is_some();
338
339                context.with_input_type_and_default(input_type, has_default, |context| {
340                    visitor.enter_object_field(context, user_context, pair);
341                    visit_input_value(visitor, &pair.1, context, user_context);
342                    visitor.leave_object_field(context, user_context, pair);
343                });
344            }
345
346            visitor.leave_object_value(context, user_context, v);
347        }
348        Value::Variable(v) => {
349            visitor.enter_variable_value(context, user_context, v);
350            visitor.leave_variable_value(context, user_context, v);
351        }
352    }
353}
354
355fn visit_variable_definitions<'a, Visitor, UserContext>(
356    visitor: &mut Visitor,
357    variables: &'a [VariableDefinition],
358    context: &mut OperationVisitorContext<'a>,
359    user_context: &mut UserContext,
360) where
361    Visitor: OperationVisitor<'a, UserContext>,
362{
363    for variable in variables {
364        context.with_input_type(Some(&variable.var_type), |context| {
365            visitor.enter_variable_definition(context, user_context, variable);
366
367            if let Some(default_value) = &variable.default_value {
368                visit_input_value(visitor, default_value, context, user_context);
369            }
370
371            // DOTAN: We should visit the directives as well here, but it's extracted in graphql_parser.
372
373            visitor.leave_variable_definition(context, user_context, variable);
374        })
375    }
376}
377
378fn visit_selection<'a, Visitor, UserContext>(
379    visitor: &mut Visitor,
380    selection: &'a Selection,
381    context: &mut OperationVisitorContext<'a>,
382    user_context: &mut UserContext,
383) where
384    Visitor: OperationVisitor<'a, UserContext>,
385{
386    match selection {
387        Selection::Field(field) => {
388            let parent_type_def = context
389                .current_parent_type()
390                .and_then(|t| t.field_by_name(&field.name));
391
392            let field_type = parent_type_def.map(|f| &f.field_type);
393            let field_args = parent_type_def.map(|f| &f.arguments);
394
395            context.with_type(field_type, |context| {
396                visitor.enter_field(context, user_context, field);
397                context.with_field(
398                    context
399                        .current_parent_type()
400                        .and_then(|t| t.field_by_name(&field.name)),
401                    |context| {
402                        visit_arguments(
403                            visitor,
404                            field_args,
405                            &field.arguments,
406                            context,
407                            user_context,
408                        );
409                        visit_directives(visitor, &field.directives, context, user_context);
410                        visit_selection_set(visitor, &field.selection_set, context, user_context);
411                    },
412                );
413                visitor.leave_field(context, user_context, field);
414            });
415        }
416        Selection::FragmentSpread(fragment_spread) => {
417            visitor.enter_fragment_spread(context, user_context, fragment_spread);
418            visit_directives(visitor, &fragment_spread.directives, context, user_context);
419            visitor.leave_fragment_spread(context, user_context, fragment_spread);
420        }
421        Selection::InlineFragment(inline_fragment) => {
422            if let Some(TypeCondition::On(fragment_condition)) = &inline_fragment.type_condition {
423                context.with_type(
424                    Some(&Type::NamedType(fragment_condition.clone())),
425                    |context| {
426                        visitor.enter_inline_fragment(context, user_context, inline_fragment);
427                        visit_directives(
428                            visitor,
429                            &inline_fragment.directives,
430                            context,
431                            user_context,
432                        );
433                        visit_selection_set(
434                            visitor,
435                            &inline_fragment.selection_set,
436                            context,
437                            user_context,
438                        );
439                        visitor.leave_inline_fragment(context, user_context, inline_fragment);
440                    },
441                );
442            } else {
443                visitor.enter_inline_fragment(context, user_context, inline_fragment);
444                visit_directives(visitor, &inline_fragment.directives, context, user_context);
445                visit_selection_set(
446                    visitor,
447                    &inline_fragment.selection_set,
448                    context,
449                    user_context,
450                );
451                visitor.leave_inline_fragment(context, user_context, inline_fragment);
452            }
453        }
454    }
455}
456
457fn visit_selection_set<'a, Visitor, UserContext>(
458    visitor: &mut Visitor,
459    selection_set: &'a SelectionSet,
460    context: &mut OperationVisitorContext<'a>,
461    user_context: &mut UserContext,
462) where
463    Visitor: OperationVisitor<'a, UserContext>,
464{
465    context.with_parent_type(|context| {
466        visitor.enter_selection_set(context, user_context, selection_set);
467
468        for selection in &selection_set.items {
469            visit_selection(visitor, selection, context, user_context);
470        }
471
472        visitor.leave_selection_set(context, user_context, selection_set);
473    });
474}
475
476fn visit_fragment_definition<'a, Visitor, UserContext>(
477    visitor: &mut Visitor,
478    fragment: &'a FragmentDefinition,
479    context: &mut OperationVisitorContext<'a>,
480    user_context: &mut UserContext,
481) where
482    Visitor: OperationVisitor<'a, UserContext>,
483{
484    visitor.enter_fragment_definition(context, user_context, fragment);
485    visit_directives(visitor, &fragment.directives, context, user_context);
486    visit_selection_set(visitor, &fragment.selection_set, context, user_context);
487    visitor.leave_fragment_definition(context, user_context, fragment);
488}
489
490fn visit_operation_definition<'a, Visitor, UserContext>(
491    visitor: &mut Visitor,
492    operation: &'a OperationDefinition,
493    context: &mut OperationVisitorContext<'a>,
494    user_context: &mut UserContext,
495) where
496    Visitor: OperationVisitor<'a, UserContext>,
497{
498    visitor.enter_operation_definition(context, user_context, operation);
499    visit_directives(visitor, operation.directives(), context, user_context);
500    visit_variable_definitions(
501        visitor,
502        operation.variable_definitions(),
503        context,
504        user_context,
505    );
506    visit_selection_set(visitor, operation.selection_set(), context, user_context);
507    visitor.leave_operation_definition(context, user_context, operation);
508}
509
510// Trait
511pub trait OperationVisitor<'a, UserContext = ()> {
512    fn enter_document(
513        &mut self,
514        _: &mut OperationVisitorContext<'a>,
515        _: &mut UserContext,
516        _: &'a Document,
517    ) {
518    }
519    fn leave_document(
520        &mut self,
521        _: &mut OperationVisitorContext<'a>,
522        _: &mut UserContext,
523        _: &Document,
524    ) {
525    }
526
527    fn enter_operation_definition(
528        &mut self,
529        _: &mut OperationVisitorContext<'a>,
530        _: &mut UserContext,
531        _: &'a OperationDefinition,
532    ) {
533    }
534    fn leave_operation_definition(
535        &mut self,
536        _: &mut OperationVisitorContext<'a>,
537        _: &mut UserContext,
538        _: &OperationDefinition,
539    ) {
540    }
541
542    fn enter_fragment_definition(
543        &mut self,
544        _: &mut OperationVisitorContext<'a>,
545        _: &mut UserContext,
546        _: &'a FragmentDefinition,
547    ) {
548    }
549    fn leave_fragment_definition(
550        &mut self,
551        _: &mut OperationVisitorContext<'a>,
552        _: &mut UserContext,
553        _: &FragmentDefinition,
554    ) {
555    }
556
557    fn enter_variable_definition(
558        &mut self,
559        _: &mut OperationVisitorContext<'a>,
560        _: &mut UserContext,
561        _: &'a VariableDefinition,
562    ) {
563    }
564    fn leave_variable_definition(
565        &mut self,
566        _: &mut OperationVisitorContext<'a>,
567        _: &mut UserContext,
568        _: &VariableDefinition,
569    ) {
570    }
571
572    fn enter_directive(
573        &mut self,
574        _: &mut OperationVisitorContext<'a>,
575        _: &mut UserContext,
576        _: &Directive,
577    ) {
578    }
579    fn leave_directive(
580        &mut self,
581        _: &mut OperationVisitorContext<'a>,
582        _: &mut UserContext,
583        _: &Directive,
584    ) {
585    }
586
587    fn enter_argument(
588        &mut self,
589        _: &mut OperationVisitorContext<'a>,
590        _: &mut UserContext,
591        _: &'a (String, Value),
592    ) {
593    }
594    fn leave_argument(
595        &mut self,
596        _: &mut OperationVisitorContext<'a>,
597        _: &mut UserContext,
598        _: &(String, Value),
599    ) {
600    }
601
602    fn enter_selection_set(
603        &mut self,
604        _: &mut OperationVisitorContext<'a>,
605        _: &mut UserContext,
606        _: &'a SelectionSet,
607    ) {
608    }
609    fn leave_selection_set(
610        &mut self,
611        _: &mut OperationVisitorContext<'a>,
612        _: &mut UserContext,
613        _: &SelectionSet,
614    ) {
615    }
616
617    fn enter_field(&mut self, _: &mut OperationVisitorContext<'a>, _: &mut UserContext, _: &Field) {
618    }
619    fn leave_field(&mut self, _: &mut OperationVisitorContext<'a>, _: &mut UserContext, _: &Field) {
620    }
621
622    fn enter_fragment_spread(
623        &mut self,
624        _: &mut OperationVisitorContext<'a>,
625        _: &mut UserContext,
626        _: &'a FragmentSpread,
627    ) {
628    }
629    fn leave_fragment_spread(
630        &mut self,
631        _: &mut OperationVisitorContext<'a>,
632        _: &mut UserContext,
633        _: &FragmentSpread,
634    ) {
635    }
636
637    fn enter_inline_fragment(
638        &mut self,
639        _: &mut OperationVisitorContext<'a>,
640        _: &mut UserContext,
641        _: &InlineFragment,
642    ) {
643    }
644    fn leave_inline_fragment(
645        &mut self,
646        _: &mut OperationVisitorContext<'a>,
647        _: &mut UserContext,
648        _: &InlineFragment,
649    ) {
650    }
651
652    fn enter_null_value(
653        &mut self,
654        _: &mut OperationVisitorContext<'a>,
655        _: &mut UserContext,
656        _: (),
657    ) {
658    }
659    fn leave_null_value(
660        &mut self,
661        _: &mut OperationVisitorContext<'a>,
662        _: &mut UserContext,
663        _: (),
664    ) {
665    }
666
667    fn enter_scalar_value(
668        &mut self,
669        _: &mut OperationVisitorContext<'a>,
670        _: &mut UserContext,
671        _: &Value,
672    ) {
673    }
674    fn leave_scalar_value(
675        &mut self,
676        _: &mut OperationVisitorContext<'a>,
677        _: &mut UserContext,
678        _: &Value,
679    ) {
680    }
681
682    fn enter_enum_value(
683        &mut self,
684        _: &mut OperationVisitorContext<'a>,
685        _: &mut UserContext,
686        _: &String,
687    ) {
688    }
689    fn leave_enum_value(
690        &mut self,
691        _: &mut OperationVisitorContext<'a>,
692        _: &mut UserContext,
693        _: &String,
694    ) {
695    }
696
697    fn enter_variable_value(
698        &mut self,
699        _: &mut OperationVisitorContext<'a>,
700        _: &mut UserContext,
701        _: &'a str,
702    ) {
703    }
704    fn leave_variable_value(
705        &mut self,
706        _: &mut OperationVisitorContext<'a>,
707        _: &mut UserContext,
708        _: &String,
709    ) {
710    }
711
712    fn enter_list_value(
713        &mut self,
714        _: &mut OperationVisitorContext<'a>,
715        _: &mut UserContext,
716        _: &Vec<Value>,
717    ) {
718    }
719    fn leave_list_value(
720        &mut self,
721        _: &mut OperationVisitorContext<'a>,
722        _: &mut UserContext,
723        _: &Vec<Value>,
724    ) {
725    }
726
727    fn enter_object_value(
728        &mut self,
729        _: &mut OperationVisitorContext<'a>,
730        _: &mut UserContext,
731        _: &[(String, Value)],
732    ) {
733    }
734    fn leave_object_value(
735        &mut self,
736        _: &mut OperationVisitorContext<'a>,
737        _: &mut UserContext,
738        _: &[(String, Value)],
739    ) {
740    }
741
742    fn enter_object_field(
743        &mut self,
744        _: &mut OperationVisitorContext<'a>,
745        _: &mut UserContext,
746        _: &(String, Value),
747    ) {
748    }
749    fn leave_object_field(
750        &mut self,
751        _: &mut OperationVisitorContext<'a>,
752        _: &mut UserContext,
753        _: &(String, Value),
754    ) {
755    }
756}