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    /// Called after a [Directive] node and its child nodes were visited.
225    fn leave_directive(
226        &mut self,
227        _ctx: &mut Context,
228        _directive: &'a Directive<'a>,
229        _info: &VisitInfo,
230    ) -> VisitFlow {
231        VisitFlow::Next
232    }
233
234    /// Called when an [Argument] node is visited and before its child nodes are visited.
235    fn enter_argument(
236        &mut self,
237        _ctx: &mut Context,
238        _argument: &'a Argument<'a>,
239        _info: &VisitInfo,
240    ) -> VisitFlow {
241        VisitFlow::Next
242    }
243    /// Called after an [Argument] node and its child nodes were visited.
244    fn leave_argument(
245        &mut self,
246        _ctx: &mut Context,
247        _argument: &'a Argument<'a>,
248        _info: &VisitInfo,
249    ) -> VisitFlow {
250        VisitFlow::Next
251    }
252}
253
254/// Trait for visiting AST Nodes of a GraphQL language document in depth-first order using a
255/// custom visitor.
256///
257/// The visitor must implement the [Visitor] trait which may also define a custom context structure
258/// that can be passed to the `visit` method.
259pub trait VisitNode<'a>: Sized + private::VisitNodeWithInfo<'a> {
260    /// Visit a GraphQL AST node tree recursively in depth-first order with a given visitor.
261    ///
262    /// The visitor must implement the [Visitor] trait which may also define a custom context structure
263    /// that can be passed to the `visit` method. By default the context is an empty unit `()`.
264    fn visit<'b, C, V: Visitor<'a, C>>(&'a self, ctx: &'b mut C, visitor: &'b mut V) -> VisitFlow {
265        let mut info = VisitInfo::default();
266        self.visit_with_info(ctx, visitor, &mut info)
267    }
268}
269
270impl<'a, T: private::VisitNodeWithInfo<'a>> VisitNode<'a> for T {}
271
272impl<'a> private::VisitNodeWithInfo<'a> for Argument<'a> {
273    #[inline]
274    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
275        &'a self,
276        ctx: &'b mut C,
277        visitor: &'b mut V,
278        info: &mut VisitInfo,
279    ) -> VisitFlow {
280        let flow = visitor.enter_argument(ctx, self, info);
281        if let VisitFlow::Next = flow {
282            visitor.leave_argument(ctx, self, info)
283        } else {
284            flow
285        }
286    }
287}
288
289impl<'a> private::VisitNodeWithInfo<'a> for Arguments<'a> {
290    #[inline]
291    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
292        &'a self,
293        ctx: &'b mut C,
294        visitor: &'b mut V,
295        info: &mut VisitInfo,
296    ) -> VisitFlow {
297        for (index, argument) in self.children.iter().enumerate() {
298            info.path.push(PathSegment::Index(index));
299            if let VisitFlow::Break = argument.visit_with_info(ctx, visitor, info) {
300                return VisitFlow::Break;
301            }
302            info.path.pop();
303        }
304        VisitFlow::Next
305    }
306}
307
308impl<'a> private::VisitNodeWithInfo<'a> for Directive<'a> {
309    #[inline]
310    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
311        &'a self,
312        ctx: &'b mut C,
313        visitor: &'b mut V,
314        info: &mut VisitInfo,
315    ) -> VisitFlow {
316        let flow = visitor.enter_directive(ctx, self, info);
317        if let VisitFlow::Next = flow {
318            info.path.push(PathSegment::Arguments);
319            if self.arguments.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
320                return VisitFlow::Break;
321            }
322            info.path.pop();
323
324            visitor.leave_directive(ctx, self, info)
325        } else {
326            flow
327        }
328    }
329}
330
331impl<'a> private::VisitNodeWithInfo<'a> for Directives<'a> {
332    #[inline]
333    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
334        &'a self,
335        ctx: &'b mut C,
336        visitor: &'b mut V,
337        info: &mut VisitInfo,
338    ) -> VisitFlow {
339        for (index, directive) in self.children.iter().enumerate() {
340            info.path.push(PathSegment::Index(index));
341            if directive.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
342                return VisitFlow::Break;
343            }
344            info.path.pop();
345        }
346        VisitFlow::Next
347    }
348}
349
350impl<'a> private::VisitNodeWithInfo<'a> for VariableDefinition<'a> {
351    #[inline]
352    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
353        &'a self,
354        ctx: &'b mut C,
355        visitor: &'b mut V,
356        info: &mut VisitInfo,
357    ) -> VisitFlow {
358        let flow = visitor.enter_variable_definition(ctx, self, info);
359        if let VisitFlow::Next = flow {
360            info.path.push(PathSegment::Directives);
361            if self.directives.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
362                return VisitFlow::Break;
363            }
364            info.path.pop();
365
366            visitor.leave_variable_definition(ctx, self, info)
367        } else {
368            flow
369        }
370    }
371}
372
373impl<'a> private::VisitNodeWithInfo<'a> for VariableDefinitions<'a> {
374    #[inline]
375    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
376        &'a self,
377        ctx: &'b mut C,
378        visitor: &'b mut V,
379        info: &mut VisitInfo,
380    ) -> VisitFlow {
381        for (index, var_def) in self.children.iter().enumerate() {
382            info.path.push(PathSegment::Index(index));
383            if var_def.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
384                return VisitFlow::Break;
385            }
386            info.path.pop();
387        }
388        VisitFlow::Next
389    }
390}
391
392impl<'a> private::VisitNodeWithInfo<'a> for Field<'a> {
393    #[inline]
394    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
395        &'a self,
396        ctx: &'b mut C,
397        visitor: &'b mut V,
398        info: &mut VisitInfo,
399    ) -> VisitFlow {
400        let flow = visitor.enter_field(ctx, self, info);
401        if let VisitFlow::Next = flow {
402            info.path.push(PathSegment::Arguments);
403            if self.arguments.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
404                return VisitFlow::Break;
405            }
406            info.path.pop();
407
408            info.path.push(PathSegment::Directives);
409            if self.directives.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
410                return VisitFlow::Break;
411            }
412            info.path.pop();
413
414            info.path.push(PathSegment::SelectionSet);
415            if self.selection_set.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
416                return VisitFlow::Break;
417            }
418            info.path.pop();
419
420            visitor.leave_field(ctx, self, info)
421        } else {
422            flow
423        }
424    }
425}
426
427impl<'a> private::VisitNodeWithInfo<'a> for FragmentSpread<'a> {
428    #[inline]
429    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
430        &'a self,
431        ctx: &'b mut C,
432        visitor: &'b mut V,
433        info: &mut VisitInfo,
434    ) -> VisitFlow {
435        let flow = visitor.enter_fragment_spread(ctx, self, info);
436        if let VisitFlow::Next = flow {
437            info.path.push(PathSegment::Directives);
438            if self.directives.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
439                return VisitFlow::Break;
440            }
441            info.path.pop();
442
443            visitor.leave_fragment_spread(ctx, self, info)
444        } else {
445            flow
446        }
447    }
448}
449
450impl<'a> private::VisitNodeWithInfo<'a> for InlineFragment<'a> {
451    #[inline]
452    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
453        &'a self,
454        ctx: &'b mut C,
455        visitor: &'b mut V,
456        info: &mut VisitInfo,
457    ) -> VisitFlow {
458        let flow = visitor.enter_inline_fragment(ctx, self, info);
459        if let VisitFlow::Next = flow {
460            info.path.push(PathSegment::Directives);
461            if self.directives.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
462                return VisitFlow::Break;
463            }
464            info.path.pop();
465
466            info.path.push(PathSegment::SelectionSet);
467            if self.selection_set.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
468                return VisitFlow::Break;
469            }
470            info.path.pop();
471
472            visitor.leave_inline_fragment(ctx, self, info)
473        } else {
474            flow
475        }
476    }
477}
478
479impl<'a> private::VisitNodeWithInfo<'a> for SelectionSet<'a> {
480    #[inline]
481    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
482        &'a self,
483        ctx: &'b mut C,
484        visitor: &'b mut V,
485        info: &mut VisitInfo,
486    ) -> VisitFlow {
487        let flow = visitor.enter_selection_set(ctx, self, info);
488        if let VisitFlow::Next = flow {
489            for (index, selection) in self.selections.iter().enumerate() {
490                info.path.push(PathSegment::Index(index));
491                let flow = match selection {
492                    Selection::Field(field) => field.visit_with_info(ctx, visitor, info),
493                    Selection::FragmentSpread(spread) => spread.visit_with_info(ctx, visitor, info),
494                    Selection::InlineFragment(fragment) => {
495                        fragment.visit_with_info(ctx, visitor, info)
496                    }
497                };
498                if flow == VisitFlow::Break {
499                    return VisitFlow::Break;
500                }
501                info.path.pop();
502            }
503            visitor.leave_selection_set(ctx, self, info)
504        } else {
505            flow
506        }
507    }
508}
509
510impl<'a> private::VisitNodeWithInfo<'a> for FragmentDefinition<'a> {
511    #[inline]
512    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
513        &'a self,
514        ctx: &'b mut C,
515        visitor: &'b mut V,
516        info: &mut VisitInfo,
517    ) -> VisitFlow {
518        let flow = visitor.enter_fragment(ctx, self, info);
519        if let VisitFlow::Next = flow {
520            info.path.push(PathSegment::Directives);
521            if self.directives.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
522                return VisitFlow::Break;
523            }
524            info.path.pop();
525
526            info.path.push(PathSegment::SelectionSet);
527            if self.selection_set.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
528                return VisitFlow::Break;
529            }
530            info.path.pop();
531
532            visitor.leave_fragment(ctx, self, info)
533        } else {
534            flow
535        }
536    }
537}
538
539impl<'a> private::VisitNodeWithInfo<'a> for OperationDefinition<'a> {
540    #[inline]
541    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
542        &'a self,
543        ctx: &'b mut C,
544        visitor: &'b mut V,
545        info: &mut VisitInfo,
546    ) -> VisitFlow {
547        let flow = visitor.enter_operation(ctx, self, info);
548        if let VisitFlow::Next = flow {
549            info.path.push(PathSegment::VariableDefinitions);
550            if self
551                .variable_definitions
552                .visit_with_info(ctx, visitor, info)
553                == VisitFlow::Break
554            {
555                return VisitFlow::Break;
556            }
557            info.path.pop();
558
559            info.path.push(PathSegment::Directives);
560            if self.directives.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
561                return VisitFlow::Break;
562            }
563            info.path.pop();
564
565            info.path.push(PathSegment::SelectionSet);
566            if self.selection_set.visit_with_info(ctx, visitor, info) == VisitFlow::Break {
567                return VisitFlow::Break;
568            }
569            info.path.pop();
570
571            visitor.leave_operation(ctx, self, info)
572        } else {
573            flow
574        }
575    }
576}
577
578impl<'a> private::VisitNodeWithInfo<'a> for Document<'a> {
579    #[inline]
580    fn visit_with_info<'b, C, V: Visitor<'a, C>>(
581        &'a self,
582        ctx: &'b mut C,
583        visitor: &'b mut V,
584        info: &mut VisitInfo,
585    ) -> VisitFlow {
586        let flow = visitor.enter_document(ctx, self, info);
587        if let VisitFlow::Next = flow {
588            for (index, selection) in self.definitions.iter().enumerate() {
589                info.path.push(PathSegment::Index(index));
590                let flow = match selection {
591                    Definition::Operation(operation) => {
592                        operation.visit_with_info(ctx, visitor, info)
593                    }
594                    Definition::Fragment(fragment) => fragment.visit_with_info(ctx, visitor, info),
595                };
596                if flow == VisitFlow::Break {
597                    return VisitFlow::Break;
598                }
599                info.path.pop();
600            }
601            visitor.leave_document(ctx, self, info)
602        } else {
603            flow
604        }
605    }
606}
607
608#[cfg(test)]
609pub(crate) mod tests {
610    use super::*;
611
612    #[derive(Debug, PartialEq, Default)]
613    pub(crate) struct CountVisitor {
614        pub(crate) in_document: usize,
615        pub(crate) out_document: usize,
616        pub(crate) in_operation: usize,
617        pub(crate) out_operation: usize,
618        pub(crate) in_fragment: usize,
619        pub(crate) out_fragment: usize,
620        pub(crate) in_variable_definition: usize,
621        pub(crate) out_variable_definition: usize,
622        pub(crate) in_selection_set: usize,
623        pub(crate) out_selection_set: usize,
624        pub(crate) in_fragment_spread: usize,
625        pub(crate) out_fragment_spread: usize,
626        pub(crate) in_inline_fragment: usize,
627        pub(crate) out_inline_fragment: usize,
628        pub(crate) in_field: usize,
629        pub(crate) out_field: usize,
630        pub(crate) in_directive: usize,
631        pub(crate) out_directive: usize,
632        pub(crate) in_argument: usize,
633        pub(crate) out_argument: usize,
634    }
635
636    impl<'a> Visitor<'a, ()> for CountVisitor {
637        fn enter_document(
638            &mut self,
639            _: &mut (),
640            _document: &'a Document<'a>,
641            _info: &VisitInfo,
642        ) -> VisitFlow {
643            self.in_document += 1;
644            VisitFlow::Next
645        }
646        fn leave_document(
647            &mut self,
648            _: &mut (),
649            _document: &Document,
650            _info: &VisitInfo,
651        ) -> VisitFlow {
652            self.out_document += 1;
653            VisitFlow::Next
654        }
655
656        fn enter_operation(
657            &mut self,
658            _: &mut (),
659            _operation: &OperationDefinition,
660            _info: &VisitInfo,
661        ) -> VisitFlow {
662            self.in_operation += 1;
663            VisitFlow::Next
664        }
665        fn leave_operation(
666            &mut self,
667            _: &mut (),
668            _operation: &OperationDefinition,
669            _info: &VisitInfo,
670        ) -> VisitFlow {
671            self.out_operation += 1;
672            VisitFlow::Next
673        }
674
675        fn enter_fragment(
676            &mut self,
677            _: &mut (),
678            _fragment: &FragmentDefinition,
679            _info: &VisitInfo,
680        ) -> VisitFlow {
681            self.in_fragment += 1;
682            VisitFlow::Next
683        }
684        fn leave_fragment(
685            &mut self,
686            _ctx: &mut (),
687            _fragment: &FragmentDefinition,
688            _info: &VisitInfo,
689        ) -> VisitFlow {
690            self.out_fragment += 1;
691            VisitFlow::Next
692        }
693
694        fn enter_variable_definition(
695            &mut self,
696            _: &mut (),
697            _var_def: &VariableDefinition,
698            _info: &VisitInfo,
699        ) -> VisitFlow {
700            self.in_variable_definition += 1;
701            VisitFlow::Next
702        }
703        fn leave_variable_definition(
704            &mut self,
705            _: &mut (),
706            _var_def: &VariableDefinition,
707            _info: &VisitInfo,
708        ) -> VisitFlow {
709            self.out_variable_definition += 1;
710            VisitFlow::Next
711        }
712
713        fn enter_selection_set(
714            &mut self,
715            _: &mut (),
716            _selection_set: &SelectionSet,
717            _info: &VisitInfo,
718        ) -> VisitFlow {
719            self.in_selection_set += 1;
720            VisitFlow::Next
721        }
722        fn leave_selection_set(
723            &mut self,
724            _: &mut (),
725            _selection_set: &SelectionSet,
726            _info: &VisitInfo,
727        ) -> VisitFlow {
728            self.out_selection_set += 1;
729            VisitFlow::Next
730        }
731
732        fn enter_fragment_spread(
733            &mut self,
734            _: &mut (),
735            _fragment_spread: &FragmentSpread,
736            _info: &VisitInfo,
737        ) -> VisitFlow {
738            self.in_fragment_spread += 1;
739            VisitFlow::Next
740        }
741        fn leave_fragment_spread(
742            &mut self,
743            _: &mut (),
744            _fragment_spread: &FragmentSpread,
745            _info: &VisitInfo,
746        ) -> VisitFlow {
747            self.out_fragment_spread += 1;
748            VisitFlow::Next
749        }
750
751        fn enter_inline_fragment(
752            &mut self,
753            _: &mut (),
754            _inline_fragment: &InlineFragment,
755            _info: &VisitInfo,
756        ) -> VisitFlow {
757            self.in_inline_fragment += 1;
758            VisitFlow::Next
759        }
760        fn leave_inline_fragment(
761            &mut self,
762            _: &mut (),
763            _inline_fragment: &InlineFragment,
764            _info: &VisitInfo,
765        ) -> VisitFlow {
766            self.out_inline_fragment += 1;
767            VisitFlow::Next
768        }
769
770        fn enter_field(&mut self, _: &mut (), _field: &Field, _info: &VisitInfo) -> VisitFlow {
771            self.in_field += 1;
772            VisitFlow::Next
773        }
774        fn leave_field(&mut self, _: &mut (), _field: &Field, _info: &VisitInfo) -> VisitFlow {
775            self.out_field += 1;
776            VisitFlow::Next
777        }
778
779        fn enter_directive(
780            &mut self,
781            _: &mut (),
782            _directive: &Directive,
783            _info: &VisitInfo,
784        ) -> VisitFlow {
785            self.in_directive += 1;
786            VisitFlow::Next
787        }
788        fn leave_directive(
789            &mut self,
790            _: &mut (),
791            _directive: &Directive,
792            _info: &VisitInfo,
793        ) -> VisitFlow {
794            self.out_directive += 1;
795            VisitFlow::Next
796        }
797
798        fn enter_argument(
799            &mut self,
800            _: &mut (),
801            _argument: &Argument,
802            _info: &VisitInfo,
803        ) -> VisitFlow {
804            self.in_argument += 1;
805            VisitFlow::Next
806        }
807        fn leave_argument(
808            &mut self,
809            _: &mut (),
810            _argument: &Argument,
811            _info: &VisitInfo,
812        ) -> VisitFlow {
813            self.out_argument += 1;
814            VisitFlow::Next
815        }
816    }
817
818    #[test]
819    fn kitchen_sink() {
820        let ctx = ASTContext::new();
821        let query = include_str!("../../fixture/kitchen_sink.graphql");
822        let ast = Document::parse(&ctx, query).unwrap();
823
824        let mut visitor = CountVisitor::default();
825        ast.visit(&mut (), &mut visitor);
826
827        assert_eq!(
828            visitor,
829            CountVisitor {
830                in_document: 1,
831                out_document: 1,
832                in_operation: 5,
833                out_operation: 5,
834                in_fragment: 1,
835                out_fragment: 1,
836                in_variable_definition: 3,
837                out_variable_definition: 3,
838                in_selection_set: 30,
839                out_selection_set: 30,
840                in_fragment_spread: 1,
841                out_fragment_spread: 1,
842                in_inline_fragment: 3,
843                out_inline_fragment: 3,
844                in_field: 21,
845                out_field: 21,
846                in_directive: 10,
847                out_directive: 10,
848                in_argument: 13,
849                out_argument: 13,
850            }
851        )
852    }
853
854    struct InfoVisitor {}
855
856    impl<'a> Visitor<'a> for InfoVisitor {
857        fn enter_fragment_spread(
858            &mut self,
859            _ctx: &mut (),
860            _fragment_spread: &'a FragmentSpread<'a>,
861            info: &VisitInfo,
862        ) -> VisitFlow {
863            // We run this visitor on the kitchen sink query which contains
864            // exactly one fragment spread at the following location
865            assert_eq!(
866                info.path,
867                Path::try_from(
868                    "0.selectionSet.0.selectionSet.1.selectionSet.0.selectionSet.1.selectionSet.1"
869                )
870                .unwrap()
871            );
872            VisitFlow::Next
873        }
874    }
875
876    #[test]
877    fn visit_info_path() {
878        let ctx = ASTContext::new();
879        let query = include_str!("../../fixture/kitchen_sink.graphql");
880        let ast = Document::parse(&ctx, query).unwrap();
881
882        let mut visitor = InfoVisitor {};
883        ast.visit(&mut (), &mut visitor);
884    }
885}