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
225 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 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 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
255pub trait VisitNode<'a>: Sized + private::VisitNodeWithInfo<'a> {
261 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 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}