graphql_tools/ast/
operation_visitor.rs

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