Skip to main content

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