Skip to main content

graphql_query/visit/
visitor.rs

1use super::{compose::ComposedVisitor, path::Path, PathSegment};
2use crate::ast::*;
3
4pub(crate) mod private {
5    use super::{VisitFlow, VisitInfo, Visitor};
6
7    pub trait VisitNodeWithInfo<'a>: Sized {
8        fn visit_with_info<'b, C, V: Visitor<'a, C>>(
9            &'a self,
10            ctx: &'b mut C,
11            visitor: &'b mut V,
12            info: &mut VisitInfo,
13        ) -> VisitFlow;
14    }
15}
16
17/// A visitor signal that is returned from [Visitor] callbacks to alter the flow of traversal.
18///
19/// The default callbacks all return `VisitFlow::Next`, which continues the depth-first traversal. The
20/// other signals may be used to skip over a node in a `before_` callback or to abort traversal
21/// entirely without visiting any more AST Nodes.
22#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
23pub enum VisitFlow {
24    /// Continue visiting nodes as usual.
25    Next,
26    /// Abort the traversal without performing any subsequent visits.
27    Break,
28    /// Skip over the current node without performing any deeper traversal.
29    /// (Only applies to `enter_` callbacks)
30    Skip,
31}
32
33#[derive(Debug, Default)]
34pub struct VisitInfo {
35    pub path: Path,
36}
37
38/// Trait for a visitor that carries methods that are called as callback while AST nodes
39/// implementing the visitor pattern are traversed.
40///
41/// While the AST is traversed in depth-first order, callbacks that are prefixed with `enter_` are
42/// called from top-to-bottom while the traversal is recursing, while callbacks that are prefixed
43/// with `leave_` are called from bottom-to-top while the traversal is returning.
44///
45/// All callbacks have a default no-op implementation that returns `VisitFlow::Next`. The
46/// [`VisitFlow`] signals are returned from callbacks to alter the traversal and either continue it
47/// (`Next`), skip over a node during an `enter_` callback with (`Skip`), or abort traversal
48/// entirely (`Break`).
49///
50/// The visitor must implement the [Visitor] trait which may also define a custom context structure
51/// that can be passed to the `visit` method. By default the context is an empty unit `()`.
52///
53/// This pattern is applicable to any AST node that implements the [`VisitNode`] trait.
54pub trait Visitor<'a, Context = ()>: Sized {
55    /// Combines two visitors into one that will run both the original and passed visitor concurrently.
56    ///
57    /// Both visitors must accept the same `Context` type.
58    #[inline]
59    fn compose<V: Visitor<'a, Context>>(self, other: V) -> ComposedVisitor<'a, Context, Self, V> {
60        ComposedVisitor::new(self, other)
61    }
62
63    /// Called when a [Document] is visited and before its child nodes are visited.
64    fn enter_document(
65        &mut self,
66        _ctx: &mut Context,
67        _document: &'a Document<'a>,
68        _info: &VisitInfo,
69    ) -> VisitFlow {
70        VisitFlow::Next
71    }
72    /// Called after a [Document] and its child nodes were visited.
73    fn leave_document(
74        &mut self,
75        _ctx: &mut Context,
76        _document: &'a Document<'a>,
77        _info: &VisitInfo,
78    ) -> VisitFlow {
79        VisitFlow::Next
80    }
81
82    /// Called when an [`OperationDefinition`] node is visited and before its child nodes are visited.
83    fn enter_operation(
84        &mut self,
85        _ctx: &mut Context,
86        _operation: &'a OperationDefinition<'a>,
87        _info: &VisitInfo,
88    ) -> VisitFlow {
89        VisitFlow::Next
90    }
91    /// Called after an [`OperationDefinition`] and its child node were visited.
92    fn leave_operation(
93        &mut self,
94        _ctx: &mut Context,
95        _operation: &'a OperationDefinition<'a>,
96        _info: &VisitInfo,
97    ) -> VisitFlow {
98        VisitFlow::Next
99    }
100
101    /// Called when a [`FragmentDefinition`] node is visited and before its child nodes are visited.
102    fn enter_fragment(
103        &mut self,
104        _ctx: &mut Context,
105        _fragment: &'a FragmentDefinition<'a>,
106        _info: &VisitInfo,
107    ) -> VisitFlow {
108        VisitFlow::Next
109    }
110    /// Called after a [`FragmentDefinition`] node and its child nodes were visited.
111    fn leave_fragment(
112        &mut self,
113        _ctx: &mut Context,
114        _fragment: &'a FragmentDefinition<'a>,
115        _info: &VisitInfo,
116    ) -> VisitFlow {
117        VisitFlow::Next
118    }
119
120    /// Called when a [`VariableDefinition`] node is visited and before its child nodes are visited.
121    fn enter_variable_definition(
122        &mut self,
123        _ctx: &mut Context,
124        _var_def: &'a VariableDefinition<'a>,
125        _info: &VisitInfo,
126    ) -> VisitFlow {
127        VisitFlow::Next
128    }
129    /// Called after a [`VariableDefinition`] node and its child nodes were visited.
130    fn leave_variable_definition(
131        &mut self,
132        _ctx: &mut Context,
133        _var_def: &'a VariableDefinition<'a>,
134        _info: &VisitInfo,
135    ) -> VisitFlow {
136        VisitFlow::Next
137    }
138
139    /// Called when a [`SelectionSet`] node is visited and before its child nodes are visited.
140    fn enter_selection_set(
141        &mut self,
142        _ctx: &mut Context,
143        _selection_set: &'a SelectionSet<'a>,
144        _info: &VisitInfo,
145    ) -> VisitFlow {
146        VisitFlow::Next
147    }
148    /// Called after a [`SelectionSet`] node and its child nodes were visited.
149    fn leave_selection_set(
150        &mut self,
151        _ctx: &mut Context,
152        _selection_set: &'a SelectionSet<'a>,
153        _info: &VisitInfo,
154    ) -> VisitFlow {
155        VisitFlow::Next
156    }
157
158    /// Called when a [`FragmentSpread`] node is visited and before its child nodes are visited.
159    fn enter_fragment_spread(
160        &mut self,
161        _ctx: &mut Context,
162        _fragment_spread: &'a FragmentSpread<'a>,
163        _info: &VisitInfo,
164    ) -> VisitFlow {
165        VisitFlow::Next
166    }
167    /// Called after a [`FragmentSpread`] node and its child nodes were visited.
168    fn leave_fragment_spread(
169        &mut self,
170        _ctx: &mut Context,
171        _fragment_spread: &'a FragmentSpread<'a>,
172        _info: &VisitInfo,
173    ) -> VisitFlow {
174        VisitFlow::Next
175    }
176
177    /// Called when an [`InlineFragment`] node is visited and before its child nodes are visited.
178    fn enter_inline_fragment(
179        &mut self,
180        _ctx: &mut Context,
181        _inline_fragment: &'a InlineFragment<'a>,
182        _info: &VisitInfo,
183    ) -> VisitFlow {
184        VisitFlow::Next
185    }
186    /// Called after an [`InlineFragment`] node and its child nodes were visited.
187    fn leave_inline_fragment(
188        &mut self,
189        _ctx: &mut Context,
190        _inline_fragment: &'a InlineFragment<'a>,
191        _info: &VisitInfo,
192    ) -> VisitFlow {
193        VisitFlow::Next
194    }
195
196    /// Called when a [Field] node is visited and before its child nodes are visited.
197    fn enter_field(
198        &mut self,
199        _ctx: &mut Context,
200        _field: &'a Field<'a>,
201        _info: &VisitInfo,
202    ) -> VisitFlow {
203        VisitFlow::Next
204    }
205    /// Called after a [Field] node and its child nodes were visited.
206    fn leave_field(
207        &mut self,
208        _ctx: &mut Context,
209        _field: &'a Field<'a>,
210        _info: &VisitInfo,
211    ) -> VisitFlow {
212        VisitFlow::Next
213    }
214
215    /// Called when a [Directive] node is visited and before its child nodes are visited.
216    fn enter_directive(
217        &mut self,
218        _ctx: &mut Context,
219        _directive: &'a Directive<'a>,
220        _info: &VisitInfo,
221    ) -> VisitFlow {
222        VisitFlow::Next
223    }
224
225    /// Called after a [Directive] node and its child nodes were visited.
226    fn leave_directive(
227        &mut self,
228        _ctx: &mut Context,
229        _directive: &'a Directive<'a>,
230        _info: &VisitInfo,
231    ) -> VisitFlow {
232        VisitFlow::Next
233    }
234
235    /// Called when an [Argument] node is visited and before its child nodes are visited.
236    fn enter_argument(
237        &mut self,
238        _ctx: &mut Context,
239        _argument: &'a Argument<'a>,
240        _info: &VisitInfo,
241    ) -> VisitFlow {
242        VisitFlow::Next
243    }
244    /// Called after an [Argument] node and its child nodes were visited.
245    fn leave_argument(
246        &mut self,
247        _ctx: &mut Context,
248        _argument: &'a Argument<'a>,
249        _info: &VisitInfo,
250    ) -> VisitFlow {
251        VisitFlow::Next
252    }
253}
254
255/// Trait for visiting AST Nodes of a GraphQL language document in depth-first order using a
256/// custom visitor.
257///
258/// The visitor must implement the [Visitor] trait which may also define a custom context structure
259/// that can be passed to the `visit` method.
260pub trait VisitNode<'a>: Sized + private::VisitNodeWithInfo<'a> {
261    /// Visit a GraphQL AST node tree recursively in depth-first order with a given visitor.
262    ///
263    /// The visitor must implement the [Visitor] trait which may also define a custom context structure
264    /// that can be passed to the `visit` method. By default the context is an empty unit `()`.
265    fn visit<'b, C, V: Visitor<'a, C>>(&'a self, ctx: &'b mut C, visitor: &'b mut V) -> VisitFlow {
266        let mut info = VisitInfo::default();
267        self.visit_with_info(ctx, visitor, &mut info)
268    }
269}
270
271impl<'a, T: private::VisitNodeWithInfo<'a>> VisitNode<'a> for T {}
272
273impl<'a> private::VisitNodeWithInfo<'a> for Argument<'a> {
274    #[inline]
275    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
276        &'a self,
277        ctx: &'b mut C,
278        visitor: &'b mut V,
279        info: &mut VisitInfo,
280    ) -> VisitFlow {
281        let flow = visitor.enter_argument(ctx, self, info);
282        if let VisitFlow::Next = flow {
283            visitor.leave_argument(ctx, self, info)
284        } else {
285            flow
286        }
287    }
288}
289
290impl<'a> private::VisitNodeWithInfo<'a> for Arguments<'a> {
291    #[inline]
292    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
293        &'a self,
294        ctx: &'b mut C,
295        visitor: &'b mut V,
296        info: &mut VisitInfo,
297    ) -> VisitFlow {
298        for (index, argument) in self.children.iter().enumerate() {
299            info.path.push(PathSegment::Index(index));
300            if let VisitFlow::Break = argument.visit_with_info(ctx, visitor, info) {
301                return VisitFlow::Break;
302            }
303            info.path.pop();
304        }
305        VisitFlow::Next
306    }
307}
308
309impl<'a> private::VisitNodeWithInfo<'a> for Directive<'a> {
310    #[inline]
311    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
312        &'a self,
313        ctx: &'b mut C,
314        visitor: &'b mut V,
315        info: &mut VisitInfo,
316    ) -> VisitFlow {
317        let flow = visitor.enter_directive(ctx, self, info);
318        if let VisitFlow::Next = flow {
319            info.path.push(PathSegment::Arguments);
320            if self.arguments.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
321                return VisitFlow::Break;
322            }
323            info.path.pop();
324
325            visitor.leave_directive(ctx, self, info)
326        } else {
327            flow
328        }
329    }
330}
331
332impl<'a> private::VisitNodeWithInfo<'a> for Directives<'a> {
333    #[inline]
334    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
335        &'a self,
336        ctx: &'b mut C,
337        visitor: &'b mut V,
338        info: &mut VisitInfo,
339    ) -> VisitFlow {
340        for (index, directive) in self.children.iter().enumerate() {
341            info.path.push(PathSegment::Index(index));
342            if directive.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
343                return VisitFlow::Break;
344            }
345            info.path.pop();
346        }
347        VisitFlow::Next
348    }
349}
350
351impl<'a> private::VisitNodeWithInfo<'a> for VariableDefinition<'a> {
352    #[inline]
353    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
354        &'a self,
355        ctx: &'b mut C,
356        visitor: &'b mut V,
357        info: &mut VisitInfo,
358    ) -> VisitFlow {
359        let flow = visitor.enter_variable_definition(ctx, self, info);
360        if let VisitFlow::Next = flow {
361            info.path.push(PathSegment::Directives);
362            if self.directives.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
363                return VisitFlow::Break;
364            }
365            info.path.pop();
366
367            visitor.leave_variable_definition(ctx, self, info)
368        } else {
369            flow
370        }
371    }
372}
373
374impl<'a> private::VisitNodeWithInfo<'a> for VariableDefinitions<'a> {
375    #[inline]
376    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
377        &'a self,
378        ctx: &'b mut C,
379        visitor: &'b mut V,
380        info: &mut VisitInfo,
381    ) -> VisitFlow {
382        for (index, var_def) in self.children.iter().enumerate() {
383            info.path.push(PathSegment::Index(index));
384            if var_def.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
385                return VisitFlow::Break;
386            }
387            info.path.pop();
388        }
389        VisitFlow::Next
390    }
391}
392
393impl<'a> private::VisitNodeWithInfo<'a> for Field<'a> {
394    #[inline]
395    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
396        &'a self,
397        ctx: &'b mut C,
398        visitor: &'b mut V,
399        info: &mut VisitInfo,
400    ) -> VisitFlow {
401        let flow = visitor.enter_field(ctx, self, info);
402        if let VisitFlow::Next = flow {
403            info.path.push(PathSegment::Arguments);
404            if self.arguments.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
405                return VisitFlow::Break;
406            }
407            info.path.pop();
408
409            info.path.push(PathSegment::Directives);
410            if self.directives.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
411                return VisitFlow::Break;
412            }
413            info.path.pop();
414
415            info.path.push(PathSegment::SelectionSet);
416            if self.selection_set.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
417                return VisitFlow::Break;
418            }
419            info.path.pop();
420
421            visitor.leave_field(ctx, self, info)
422        } else {
423            flow
424        }
425    }
426}
427
428impl<'a> private::VisitNodeWithInfo<'a> for FragmentSpread<'a> {
429    #[inline]
430    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
431        &'a self,
432        ctx: &'b mut C,
433        visitor: &'b mut V,
434        info: &mut VisitInfo,
435    ) -> VisitFlow {
436        let flow = visitor.enter_fragment_spread(ctx, self, info);
437        if let VisitFlow::Next = flow {
438            info.path.push(PathSegment::Directives);
439            if self.directives.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
440                return VisitFlow::Break;
441            }
442            info.path.pop();
443
444            visitor.leave_fragment_spread(ctx, self, info)
445        } else {
446            flow
447        }
448    }
449}
450
451impl<'a> private::VisitNodeWithInfo<'a> for InlineFragment<'a> {
452    #[inline]
453    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
454        &'a self,
455        ctx: &'b mut C,
456        visitor: &'b mut V,
457        info: &mut VisitInfo,
458    ) -> VisitFlow {
459        let flow = visitor.enter_inline_fragment(ctx, self, info);
460        if let VisitFlow::Next = flow {
461            info.path.push(PathSegment::Directives);
462            if self.directives.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
463                return VisitFlow::Break;
464            }
465            info.path.pop();
466
467            info.path.push(PathSegment::SelectionSet);
468            if self.selection_set.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
469                return VisitFlow::Break;
470            }
471            info.path.pop();
472
473            visitor.leave_inline_fragment(ctx, self, info)
474        } else {
475            flow
476        }
477    }
478}
479
480impl<'a> private::VisitNodeWithInfo<'a> for SelectionSet<'a> {
481    #[inline]
482    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
483        &'a self,
484        ctx: &'b mut C,
485        visitor: &'b mut V,
486        info: &mut VisitInfo,
487    ) -> VisitFlow {
488        let flow = visitor.enter_selection_set(ctx, self, info);
489        if let VisitFlow::Next = flow {
490            for (index, selection) in self.selections.iter().enumerate() {
491                info.path.push(PathSegment::Index(index));
492                let flow = match selection {
493                    Selection::Field(field) => field.visit_with_info(ctx, visitor, info),
494                    Selection::FragmentSpread(spread) => spread.visit_with_info(ctx, visitor, info),
495                    Selection::InlineFragment(fragment) => {
496                        fragment.visit_with_info(ctx, visitor, info)
497                    }
498                };
499                if flow == VisitFlow::Break {
500                    return VisitFlow::Break;
501                }
502                info.path.pop();
503            }
504            visitor.leave_selection_set(ctx, self, info)
505        } else {
506            flow
507        }
508    }
509}
510
511impl<'a> private::VisitNodeWithInfo<'a> for FragmentDefinition<'a> {
512    #[inline]
513    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
514        &'a self,
515        ctx: &'b mut C,
516        visitor: &'b mut V,
517        info: &mut VisitInfo,
518    ) -> VisitFlow {
519        let flow = visitor.enter_fragment(ctx, self, info);
520        if let VisitFlow::Next = flow {
521            info.path.push(PathSegment::Directives);
522            if self.directives.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
523                return VisitFlow::Break;
524            }
525            info.path.pop();
526
527            info.path.push(PathSegment::SelectionSet);
528            if self.selection_set.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
529                return VisitFlow::Break;
530            }
531            info.path.pop();
532
533            visitor.leave_fragment(ctx, self, info)
534        } else {
535            flow
536        }
537    }
538}
539
540impl<'a> private::VisitNodeWithInfo<'a> for OperationDefinition<'a> {
541    #[inline]
542    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
543        &'a self,
544        ctx: &'b mut C,
545        visitor: &'b mut V,
546        info: &mut VisitInfo,
547    ) -> VisitFlow {
548        let flow = visitor.enter_operation(ctx, self, info);
549        if let VisitFlow::Next = flow {
550            info.path.push(PathSegment::VariableDefinitions);
551            if self
552                .variable_definitions
553                .visit_with_info(ctx, visitor, info)
554                == VisitFlow::Break
555            {
556                return VisitFlow::Break;
557            }
558            info.path.pop();
559
560            info.path.push(PathSegment::Directives);
561            if self.directives.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
562                return VisitFlow::Break;
563            }
564            info.path.pop();
565
566            info.path.push(PathSegment::SelectionSet);
567            if self.selection_set.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
568                return VisitFlow::Break;
569            }
570            info.path.pop();
571
572            visitor.leave_operation(ctx, self, info)
573        } else {
574            flow
575        }
576    }
577}
578
579impl<'a> private::VisitNodeWithInfo<'a> for Document<'a> {
580    #[inline]
581    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
582        &'a self,
583        ctx: &'b mut C,
584        visitor: &'b mut V,
585        info: &mut VisitInfo,
586    ) -> VisitFlow {
587        let flow = visitor.enter_document(ctx, self, info);
588        if let VisitFlow::Next = flow {
589            for (index, selection) in self.definitions.iter().enumerate() {
590                info.path.push(PathSegment::Index(index));
591                let flow = match selection {
592                    Definition::Operation(operation) => {
593                        operation.visit_with_info(ctx, visitor, info)
594                    }
595                    Definition::Fragment(fragment) => fragment.visit_with_info(ctx, visitor, info),
596                };
597                if flow == VisitFlow::Break {
598                    return VisitFlow::Break;
599                }
600                info.path.pop();
601            }
602            visitor.leave_document(ctx, self, info)
603        } else {
604            flow
605        }
606    }
607}
608
609#[cfg(test)]
610pub(crate) mod tests {
611    use super::*;
612
613    #[derive(Debug, PartialEq, Default)]
614    pub(crate) struct CountVisitor {
615        pub(crate) in_document: usize,
616        pub(crate) out_document: usize,
617        pub(crate) in_operation: usize,
618        pub(crate) out_operation: usize,
619        pub(crate) in_fragment: usize,
620        pub(crate) out_fragment: usize,
621        pub(crate) in_variable_definition: usize,
622        pub(crate) out_variable_definition: usize,
623        pub(crate) in_selection_set: usize,
624        pub(crate) out_selection_set: usize,
625        pub(crate) in_fragment_spread: usize,
626        pub(crate) out_fragment_spread: usize,
627        pub(crate) in_inline_fragment: usize,
628        pub(crate) out_inline_fragment: usize,
629        pub(crate) in_field: usize,
630        pub(crate) out_field: usize,
631        pub(crate) in_directive: usize,
632        pub(crate) out_directive: usize,
633        pub(crate) in_argument: usize,
634        pub(crate) out_argument: usize,
635    }
636
637    impl<'a> Visitor<'a, ()> for CountVisitor {
638        fn enter_document(
639            &mut self,
640            _: &mut (),
641            _document: &'a Document<'a>,
642            _info: &VisitInfo,
643        ) -> VisitFlow {
644            self.in_document += 1;
645            VisitFlow::Next
646        }
647        fn leave_document(
648            &mut self,
649            _: &mut (),
650            _document: &Document,
651            _info: &VisitInfo,
652        ) -> VisitFlow {
653            self.out_document += 1;
654            VisitFlow::Next
655        }
656
657        fn enter_operation(
658            &mut self,
659            _: &mut (),
660            _operation: &OperationDefinition,
661            _info: &VisitInfo,
662        ) -> VisitFlow {
663            self.in_operation += 1;
664            VisitFlow::Next
665        }
666        fn leave_operation(
667            &mut self,
668            _: &mut (),
669            _operation: &OperationDefinition,
670            _info: &VisitInfo,
671        ) -> VisitFlow {
672            self.out_operation += 1;
673            VisitFlow::Next
674        }
675
676        fn enter_fragment(
677            &mut self,
678            _: &mut (),
679            _fragment: &FragmentDefinition,
680            _info: &VisitInfo,
681        ) -> VisitFlow {
682            self.in_fragment += 1;
683            VisitFlow::Next
684        }
685        fn leave_fragment(
686            &mut self,
687            _ctx: &mut (),
688            _fragment: &FragmentDefinition,
689            _info: &VisitInfo,
690        ) -> VisitFlow {
691            self.out_fragment += 1;
692            VisitFlow::Next
693        }
694
695        fn enter_variable_definition(
696            &mut self,
697            _: &mut (),
698            _var_def: &VariableDefinition,
699            _info: &VisitInfo,
700        ) -> VisitFlow {
701            self.in_variable_definition += 1;
702            VisitFlow::Next
703        }
704        fn leave_variable_definition(
705            &mut self,
706            _: &mut (),
707            _var_def: &VariableDefinition,
708            _info: &VisitInfo,
709        ) -> VisitFlow {
710            self.out_variable_definition += 1;
711            VisitFlow::Next
712        }
713
714        fn enter_selection_set(
715            &mut self,
716            _: &mut (),
717            _selection_set: &SelectionSet,
718            _info: &VisitInfo,
719        ) -> VisitFlow {
720            self.in_selection_set += 1;
721            VisitFlow::Next
722        }
723        fn leave_selection_set(
724            &mut self,
725            _: &mut (),
726            _selection_set: &SelectionSet,
727            _info: &VisitInfo,
728        ) -> VisitFlow {
729            self.out_selection_set += 1;
730            VisitFlow::Next
731        }
732
733        fn enter_fragment_spread(
734            &mut self,
735            _: &mut (),
736            _fragment_spread: &FragmentSpread,
737            _info: &VisitInfo,
738        ) -> VisitFlow {
739            self.in_fragment_spread += 1;
740            VisitFlow::Next
741        }
742        fn leave_fragment_spread(
743            &mut self,
744            _: &mut (),
745            _fragment_spread: &FragmentSpread,
746            _info: &VisitInfo,
747        ) -> VisitFlow {
748            self.out_fragment_spread += 1;
749            VisitFlow::Next
750        }
751
752        fn enter_inline_fragment(
753            &mut self,
754            _: &mut (),
755            _inline_fragment: &InlineFragment,
756            _info: &VisitInfo,
757        ) -> VisitFlow {
758            self.in_inline_fragment += 1;
759            VisitFlow::Next
760        }
761        fn leave_inline_fragment(
762            &mut self,
763            _: &mut (),
764            _inline_fragment: &InlineFragment,
765            _info: &VisitInfo,
766        ) -> VisitFlow {
767            self.out_inline_fragment += 1;
768            VisitFlow::Next
769        }
770
771        fn enter_field(&mut self, _: &mut (), _field: &Field, _info: &VisitInfo) -> VisitFlow {
772            self.in_field += 1;
773            VisitFlow::Next
774        }
775        fn leave_field(&mut self, _: &mut (), _field: &Field, _info: &VisitInfo) -> VisitFlow {
776            self.out_field += 1;
777            VisitFlow::Next
778        }
779
780        fn enter_directive(
781            &mut self,
782            _: &mut (),
783            _directive: &Directive,
784            _info: &VisitInfo,
785        ) -> VisitFlow {
786            self.in_directive += 1;
787            VisitFlow::Next
788        }
789        fn leave_directive(
790            &mut self,
791            _: &mut (),
792            _directive: &Directive,
793            _info: &VisitInfo,
794        ) -> VisitFlow {
795            self.out_directive += 1;
796            VisitFlow::Next
797        }
798
799        fn enter_argument(
800            &mut self,
801            _: &mut (),
802            _argument: &Argument,
803            _info: &VisitInfo,
804        ) -> VisitFlow {
805            self.in_argument += 1;
806            VisitFlow::Next
807        }
808        fn leave_argument(
809            &mut self,
810            _: &mut (),
811            _argument: &Argument,
812            _info: &VisitInfo,
813        ) -> VisitFlow {
814            self.out_argument += 1;
815            VisitFlow::Next
816        }
817    }
818
819    #[test]
820    fn kitchen_sink() {
821        let ctx = ASTContext::new();
822        let query = include_str!("../../fixture/kitchen_sink.graphql");
823        let ast = Document::parse(&ctx, query).unwrap();
824
825        let mut visitor = CountVisitor::default();
826        ast.visit(&mut (), &mut visitor);
827
828        assert_eq!(
829            visitor,
830            CountVisitor {
831                in_document: 1,
832                out_document: 1,
833                in_operation: 5,
834                out_operation: 5,
835                in_fragment: 1,
836                out_fragment: 1,
837                in_variable_definition: 3,
838                out_variable_definition: 3,
839                in_selection_set: 30,
840                out_selection_set: 30,
841                in_fragment_spread: 1,
842                out_fragment_spread: 1,
843                in_inline_fragment: 3,
844                out_inline_fragment: 3,
845                in_field: 21,
846                out_field: 21,
847                in_directive: 10,
848                out_directive: 10,
849                in_argument: 13,
850                out_argument: 13,
851            }
852        )
853    }
854
855    struct InfoVisitor {}
856
857    impl<'a> Visitor<'a> for InfoVisitor {
858        fn enter_fragment_spread(
859            &mut self,
860            _ctx: &mut (),
861            _fragment_spread: &'a FragmentSpread<'a>,
862            info: &VisitInfo,
863        ) -> VisitFlow {
864            // We run this visitor on the kitchen sink query which contains
865            // exactly one fragment spread at the following location
866            assert_eq!(
867                info.path,
868                Path::try_from(
869                    "0.selectionSet.0.selectionSet.1.selectionSet.0.selectionSet.1.selectionSet.1"
870                )
871                .unwrap()
872            );
873            VisitFlow::Next
874        }
875    }
876
877    #[test]
878    fn visit_info_path() {
879        let ctx = ASTContext::new();
880        let query = include_str!("../../fixture/kitchen_sink.graphql");
881        let ast = Document::parse(&ctx, query).unwrap();
882
883        let mut visitor = InfoVisitor {};
884        ast.visit(&mut (), &mut visitor);
885    }
886}