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#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
23pub enum VisitFlow {
24 Next,
26 Break,
28 Skip,
31}
32
33#[derive(Debug, Default)]
34pub struct VisitInfo {
35 pub path: Path,
36}
37
38pub trait Visitor<'a, Context = ()>: Sized {
55 #[inline]
59 fn compose<V: Visitor<'a, Context>>(self, other: V) -> ComposedVisitor<'a, Context, Self, V> {
60 ComposedVisitor::new(self, other)
61 }
62
63 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
254pub trait VisitNode<'a>: Sized + private::VisitNodeWithInfo<'a> {
260 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 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}