1use crate::ast::*;
2use bumpalo::collections::Vec;
3
4pub use super::{folder_simple::SimpleFolder, Path, PathSegment, VisitInfo};
5pub use crate::error::{Error, Result};
6
7pub(crate) mod private {
8 use hashbrown::{hash_map::DefaultHashBuilder, HashMap};
9 use std::cell::RefCell;
10
11 use super::{ASTContext, Definition, Document, Folder, Result, Vec};
12 use crate::{ast::FragmentDefinitionWithIndex, visit::VisitInfo};
13
14 pub struct FolderContext<'a> {
18 pub(crate) ctx: &'a ASTContext,
19 pub(crate) definitions: RefCell<Vec<'a, Definition<'a>>>,
20 pub(crate) fragments: RefCell<
21 HashMap<
22 &'a str,
23 FragmentDefinitionWithIndex<'a>,
24 DefaultHashBuilder,
25 &'a bumpalo::Bump,
26 >,
27 >,
28 pub(crate) fragment_names:
29 RefCell<HashMap<&'a str, &'a str, DefaultHashBuilder, &'a bumpalo::Bump>>,
30 pub(crate) recurse: bool,
31 }
32
33 impl<'a> FolderContext<'a> {
34 pub(crate) fn empty(ctx: &'a ASTContext) -> Self {
35 FolderContext {
36 ctx,
37 fragments: RefCell::new(HashMap::new_in(&ctx.arena)),
38 fragment_names: RefCell::new(HashMap::new_in(&ctx.arena)),
39 definitions: RefCell::new(Vec::new_in(&ctx.arena)),
40 recurse: false,
41 }
42 }
43
44 pub(crate) fn new_vec<T>(&self) -> Vec<'_, T> {
45 Vec::new_in(&self.ctx.arena)
46 }
47
48 pub(crate) fn with_document(ctx: &'a ASTContext, document: &'a Document<'a>) -> Self {
49 FolderContext {
50 ctx,
51 fragments: RefCell::new(document.fragments_with_index(ctx)),
52 fragment_names: RefCell::new(HashMap::new_in(&ctx.arena)),
53 definitions: RefCell::new(Vec::new_in(&ctx.arena)),
54 recurse: true,
55 }
56 }
57 }
58
59 pub trait FoldNode<'a>: Sized {
60 fn fold_with_ctx<'b, F: Folder<'a>>(
61 &self,
62 info: &mut VisitInfo,
63 ctx: &'a FolderContext<'a>,
64 folder: &'b mut F,
65 ) -> Result<Self>;
66 }
67}
68
69pub trait Folder<'a> {
84 #[inline]
87 fn enter_operation(
88 &mut self,
89 _ctx: &'a ASTContext,
90 operation: OperationDefinition<'a>,
91 _info: &VisitInfo,
92 ) -> Result<OperationDefinition<'a>> {
93 Ok(operation)
94 }
95
96 #[inline]
99 fn leave_operation(
100 &mut self,
101 _ctx: &'a ASTContext,
102 operation: OperationDefinition<'a>,
103 _info: &VisitInfo,
104 ) -> Result<OperationDefinition<'a>> {
105 Ok(operation)
106 }
107
108 #[inline]
111 fn enter_fragment(
112 &mut self,
113 _ctx: &'a ASTContext,
114 fragment: FragmentDefinition<'a>,
115 _info: &VisitInfo,
116 ) -> Result<FragmentDefinition<'a>> {
117 Ok(fragment)
118 }
119
120 #[inline]
123 fn leave_fragment(
124 &mut self,
125 _ctx: &'a ASTContext,
126 fragment: FragmentDefinition<'a>,
127 _info: &VisitInfo,
128 ) -> Result<FragmentDefinition<'a>> {
129 Ok(fragment)
130 }
131
132 #[inline]
134 fn variable_definitions(
135 &mut self,
136 _ctx: &'a ASTContext,
137 var_defs: VariableDefinitions<'a>,
138 _info: &VisitInfo,
139 ) -> Result<VariableDefinitions<'a>> {
140 Ok(var_defs)
141 }
142
143 #[inline]
145 fn variable_definition(
146 &mut self,
147 _ctx: &'a ASTContext,
148 var_def: VariableDefinition<'a>,
149 _info: &VisitInfo,
150 ) -> Result<VariableDefinition<'a>> {
151 Ok(var_def)
152 }
153
154 #[inline]
156 fn selection_set(
157 &mut self,
158 _ctx: &'a ASTContext,
159 selection_set: SelectionSet<'a>,
160 _info: &VisitInfo,
161 ) -> Result<SelectionSet<'a>> {
162 Ok(selection_set)
163 }
164
165 #[inline]
168 fn enter_fragment_spread(
169 &mut self,
170 _ctx: &'a ASTContext,
171 fragment_spread: FragmentSpread<'a>,
172 _info: &VisitInfo,
173 ) -> Result<FragmentSpread<'a>> {
174 Ok(fragment_spread)
175 }
176
177 #[inline]
180 fn leave_fragment_spread(
181 &mut self,
182 _ctx: &'a ASTContext,
183 fragment_spread: FragmentSpread<'a>,
184 _info: &VisitInfo,
185 ) -> Result<FragmentSpread<'a>> {
186 Ok(fragment_spread)
187 }
188
189 #[inline]
192 fn enter_inline_fragment(
193 &mut self,
194 _ctx: &'a ASTContext,
195 inline_fragment: InlineFragment<'a>,
196 _info: &VisitInfo,
197 ) -> Result<InlineFragment<'a>> {
198 Ok(inline_fragment)
199 }
200
201 #[inline]
204 fn leave_inline_fragment(
205 &mut self,
206 _ctx: &'a ASTContext,
207 inline_fragment: InlineFragment<'a>,
208 _info: &VisitInfo,
209 ) -> Result<InlineFragment<'a>> {
210 Ok(inline_fragment)
211 }
212
213 #[inline]
216 fn enter_field(
217 &mut self,
218 _ctx: &'a ASTContext,
219 field: Field<'a>,
220 _info: &VisitInfo,
221 ) -> Result<Field<'a>> {
222 Ok(field)
223 }
224
225 #[inline]
228 fn leave_field(
229 &mut self,
230 _ctx: &'a ASTContext,
231 field: Field<'a>,
232 _info: &VisitInfo,
233 ) -> Result<Field<'a>> {
234 Ok(field)
235 }
236
237 #[inline]
239 fn directives(
240 &mut self,
241 _ctx: &'a ASTContext,
242 directives: Directives<'a>,
243 _info: &VisitInfo,
244 ) -> Result<Directives<'a>> {
245 Ok(directives)
246 }
247
248 #[inline]
251 fn enter_directive(
252 &mut self,
253 _ctx: &'a ASTContext,
254 directive: Directive<'a>,
255 _info: &VisitInfo,
256 ) -> Result<Directive<'a>> {
257 Ok(directive)
258 }
259
260 #[inline]
263 fn leave_directive(
264 &mut self,
265 _ctx: &'a ASTContext,
266 directive: Directive<'a>,
267 _info: &VisitInfo,
268 ) -> Result<Directive<'a>> {
269 Ok(directive)
270 }
271
272 #[inline]
274 fn arguments(
275 &mut self,
276 _ctx: &'a ASTContext,
277 arguments: Arguments<'a>,
278 _info: &VisitInfo,
279 ) -> Result<Arguments<'a>> {
280 Ok(arguments)
281 }
282
283 #[inline]
285 fn argument(
286 &mut self,
287 _ctx: &'a ASTContext,
288 argument: Argument<'a>,
289 _info: &VisitInfo,
290 ) -> Result<Argument<'a>> {
291 Ok(argument)
292 }
293
294 #[inline]
296 fn value(
297 &mut self,
298 _ctx: &'a ASTContext,
299 value: Value<'a>,
300 _info: &VisitInfo,
301 ) -> Result<Value<'a>> {
302 Ok(value)
303 }
304
305 #[inline]
307 fn of_type(
308 &mut self,
309 _ctx: &'a ASTContext,
310 of_type: Type<'a>,
311 _info: &VisitInfo,
312 ) -> Result<Type<'a>> {
313 Ok(of_type)
314 }
315
316 #[inline]
318 fn variable(
319 &mut self,
320 _ctx: &'a ASTContext,
321 var: Variable<'a>,
322 _info: &VisitInfo,
323 ) -> Result<Variable<'a>> {
324 Ok(var)
325 }
326
327 #[inline]
329 fn named_type(
330 &mut self,
331 _ctx: &'a ASTContext,
332 name: NamedType<'a>,
333 _info: &VisitInfo,
334 ) -> Result<NamedType<'a>> {
335 Ok(name)
336 }
337}
338
339pub trait FoldNode<'a>: private::FoldNode<'a> {
344 fn fold<'b, F: Folder<'a>>(
350 &'a self,
351 ctx: &'a ASTContext,
352 folder: &'b mut F,
353 ) -> Result<&'a Self> {
354 let mut info = VisitInfo::default();
355 let folder_ctx = ctx.alloc(private::FolderContext::empty(ctx));
356 Ok(ctx.alloc(self.fold_with_ctx(&mut info, folder_ctx, folder)?))
357 }
358}
359
360pub trait FoldDocument<'a>: private::FoldNode<'a> {
368 fn fold_operation<'b, F: Folder<'a>>(
373 &'a self,
374 ctx: &'a ASTContext,
375 operation: Option<&'a str>,
376 folder: &'b mut F,
377 ) -> Result<&'a Self>;
378}
379
380impl<'a, T: private::FoldNode<'a>> FoldNode<'a> for T {}
381
382impl<'a> private::FoldNode<'a> for Type<'a> {
383 #[inline]
384 fn fold_with_ctx<'b, F: Folder<'a>>(
385 &self,
386 info: &mut VisitInfo,
387 ctx: &'b private::FolderContext<'a>,
388 folder: &'b mut F,
389 ) -> Result<Self> {
390 folder.of_type(ctx.ctx, *self, info)
391 }
392}
393
394impl<'a> private::FoldNode<'a> for Value<'a> {
395 #[inline]
396 fn fold_with_ctx<'b, F: Folder<'a>>(
397 &self,
398 info: &mut VisitInfo,
399 ctx: &'a private::FolderContext<'a>,
400 folder: &'b mut F,
401 ) -> Result<Self> {
402 folder.value(ctx.ctx, self.clone(), info)
403 }
404}
405
406impl<'a> private::FoldNode<'a> for NamedType<'a> {
407 #[inline]
408 fn fold_with_ctx<'b, F: Folder<'a>>(
409 &self,
410 info: &mut VisitInfo,
411 ctx: &'a private::FolderContext<'a>,
412 folder: &'b mut F,
413 ) -> Result<Self> {
414 folder.named_type(ctx.ctx, *self, info)
415 }
416}
417
418impl<'a> private::FoldNode<'a> for Variable<'a> {
419 #[inline]
420 fn fold_with_ctx<'b, F: Folder<'a>>(
421 &self,
422 info: &mut VisitInfo,
423 ctx: &'a private::FolderContext<'a>,
424 folder: &'b mut F,
425 ) -> Result<Self> {
426 folder.variable(ctx.ctx, *self, info)
427 }
428}
429
430impl<'a> private::FoldNode<'a> for Argument<'a> {
431 #[inline]
432 fn fold_with_ctx<'b, F: Folder<'a>>(
433 &self,
434 info: &mut VisitInfo,
435 ctx: &'a private::FolderContext<'a>,
436 folder: &'b mut F,
437 ) -> Result<Self> {
438 let argument = folder.argument(ctx.ctx, self.clone(), info)?;
439
440 info.path.push(PathSegment::Value);
441 let value = argument.value.fold_with_ctx(info, ctx, folder)?;
442 info.path.pop();
443
444 Ok(Argument {
445 name: argument.name,
446 value,
447 })
448 }
449}
450
451impl<'a> private::FoldNode<'a> for Arguments<'a> {
452 #[inline]
453 fn fold_with_ctx<'b, F: Folder<'a>>(
454 &self,
455 info: &mut VisitInfo,
456 ctx: &'a private::FolderContext<'a>,
457 folder: &'b mut F,
458 ) -> Result<Self> {
459 let new_arguments_iter = {
460 folder
461 .arguments(ctx.ctx, self.clone(), info)?
462 .into_iter()
463 .enumerate()
464 .map(|(index, argument)| {
465 info.path.push(PathSegment::Index(index));
466 let folded = argument.fold_with_ctx(info, ctx, folder);
467 info.path.pop();
468 folded
469 })
470 };
471
472 let new_arguments = {
473 let mut new_arguments = ctx.new_vec();
474 for item in new_arguments_iter {
475 new_arguments.push(item?);
476 }
477
478 new_arguments
479 };
480
481 Ok(Arguments {
482 children: new_arguments,
483 })
484 }
485}
486
487impl<'a> private::FoldNode<'a> for Directive<'a> {
488 #[inline]
489 fn fold_with_ctx<'b, F: Folder<'a>>(
490 &self,
491 info: &mut VisitInfo,
492 ctx: &'a private::FolderContext<'a>,
493 folder: &'b mut F,
494 ) -> Result<Self> {
495 let directive = &folder.enter_directive(ctx.ctx, self.clone(), info)?;
496
497 info.path.push(PathSegment::Arguments);
498 let arguments = directive.arguments.fold_with_ctx(info, ctx, folder)?;
499 info.path.pop();
500
501 let directive = Directive {
502 name: directive.name,
503 arguments,
504 };
505 folder.leave_directive(ctx.ctx, directive, info)
506 }
507}
508
509impl<'a> private::FoldNode<'a> for Directives<'a> {
510 #[inline]
511 fn fold_with_ctx<'b, F: Folder<'a>>(
512 &self,
513 info: &mut VisitInfo,
514 ctx: &'a private::FolderContext<'a>,
515 folder: &'b mut F,
516 ) -> Result<Self> {
517 let new_directives_iter = folder
519 .directives(ctx.ctx, self.clone(), info)?
520 .into_iter()
521 .enumerate()
522 .map(|(index, directive)| {
523 info.path.push(PathSegment::Index(index));
524 let folded = directive.fold_with_ctx(info, ctx, folder);
525 info.path.pop();
526 folded
527 });
528
529 let mut new_directives = Vec::new_in(&ctx.ctx.arena);
530 for item in new_directives_iter {
531 new_directives.push(item?);
532 }
533 Ok(Directives {
534 children: new_directives,
535 })
536 }
537}
538
539impl<'a> private::FoldNode<'a> for VariableDefinition<'a> {
540 #[inline]
541 fn fold_with_ctx<'b, F: Folder<'a>>(
542 &self,
543 info: &mut VisitInfo,
544 ctx: &'a private::FolderContext<'a>,
545 folder: &'b mut F,
546 ) -> Result<Self> {
547 let var_def = folder.variable_definition(ctx.ctx, self.clone(), info)?;
548
549 info.path.push(PathSegment::Variable);
550 let variable = var_def.variable.fold_with_ctx(info, ctx, folder)?;
551 info.path.pop();
552
553 info.path.push(PathSegment::Type);
554 let of_type = var_def.of_type.fold_with_ctx(info, ctx, folder)?;
555 info.path.pop();
556
557 info.path.push(PathSegment::Value);
558 let default_value = var_def.default_value.fold_with_ctx(info, ctx, folder)?;
559 info.path.pop();
560
561 info.path.push(PathSegment::Directives);
562 let directives = var_def.directives.fold_with_ctx(info, ctx, folder)?;
563 info.path.pop();
564
565 Ok(VariableDefinition {
566 variable,
567 of_type,
568 default_value,
569 directives,
570 })
571 }
572}
573
574impl<'a> private::FoldNode<'a> for VariableDefinitions<'a> {
575 #[inline]
576 fn fold_with_ctx<'b, F: Folder<'a>>(
577 &self,
578 info: &mut VisitInfo,
579 ctx: &'a private::FolderContext<'a>,
580 folder: &'b mut F,
581 ) -> Result<Self> {
582 let new_variables_iter = folder
583 .variable_definitions(ctx.ctx, self.clone(), info)?
584 .into_iter()
585 .enumerate()
586 .map(|(index, var_def)| {
587 info.path.push(PathSegment::Index(index));
588 let folded = var_def.fold_with_ctx(info, ctx, folder);
589 info.path.pop();
590 folded
591 });
592
593 let mut new_variables = Vec::new_in(&ctx.ctx.arena);
594 for item in new_variables_iter {
595 new_variables.push(item?);
596 }
597 Ok(VariableDefinitions {
598 children: new_variables,
599 })
600 }
601}
602
603impl<'a> private::FoldNode<'a> for Field<'a> {
604 #[inline]
605 fn fold_with_ctx<'b, F: Folder<'a>>(
606 &self,
607 info: &mut VisitInfo,
608 ctx: &'a private::FolderContext<'a>,
609 folder: &'b mut F,
610 ) -> Result<Self> {
611 let field = &folder.enter_field(ctx.ctx, self.clone(), info)?;
612
613 info.path.push(PathSegment::Arguments);
614 let arguments = field.arguments.fold_with_ctx(info, ctx, folder)?;
615 info.path.pop();
616
617 info.path.push(PathSegment::Directives);
618 let directives = field.directives.fold_with_ctx(info, ctx, folder)?;
619 info.path.pop();
620
621 info.path.push(PathSegment::SelectionSet);
622 let selection_set = field.selection_set.fold_with_ctx(info, ctx, folder)?;
623 info.path.pop();
624
625 let field = Field {
626 alias: field.alias,
627 name: field.name,
628 arguments,
629 directives,
630 selection_set,
631 };
632 folder.leave_field(ctx.ctx, field, info)
633 }
634}
635
636impl<'a> private::FoldNode<'a> for FragmentSpread<'a> {
637 #[inline]
638 fn fold_with_ctx<'b, F: Folder<'a>>(
639 &self,
640 info: &mut VisitInfo,
641 ctx: &'a private::FolderContext<'a>,
642 folder: &'b mut F,
643 ) -> Result<Self> {
644 let spread = &folder.enter_fragment_spread(ctx.ctx, self.clone(), info)?;
645 let spread = if ctx.recurse {
646 let fragment_name = spread.name.name;
647
648 let already_visited = {
650 let borrowed = ctx.fragment_names.borrow();
651 borrowed.get(fragment_name).copied()
652 };
653
654 let fragment_name = if let Some(fragment_name) = already_visited {
655 fragment_name
656 } else {
657 let fragment_and_index = ctx
659 .fragments
660 .borrow()
661 .get(spread.name.name)
662 .map(|f| (f.fragment.clone(), f.index));
663
664 if let Some((fragment, index)) = fragment_and_index {
665 let path = info.path.clone();
666 info.path = Path::default();
667 info.path.push(PathSegment::Index(index.to_owned()));
668 let fragment = fragment.fold_with_ctx(info, ctx, folder)?;
669 info.path = path;
670
671 let fragment_name = fragment.name.name;
672 ctx.definitions
674 .borrow_mut()
675 .push(Definition::Fragment(fragment));
676 ctx.fragment_names
677 .borrow_mut()
678 .insert(fragment_name, fragment_name);
679 fragment_name
680 } else {
681 return Err(Error::new(
682 format!("The fragment '{}' does not exist", fragment_name),
683 None,
684 ));
685 }
686 };
687
688 info.path.push(PathSegment::Directives);
689 let directives = spread.directives.fold_with_ctx(info, ctx, folder)?;
690 info.path.pop();
691
692 FragmentSpread {
693 name: fragment_name.into(),
694 directives,
695 }
696 } else {
697 info.path.push(PathSegment::Name);
698 let name = spread.name.fold_with_ctx(info, ctx, folder)?;
699 info.path.pop();
700
701 info.path.push(PathSegment::Directives);
702 let directives = spread.directives.fold_with_ctx(info, ctx, folder)?;
703 info.path.pop();
704
705 FragmentSpread { name, directives }
706 };
707 folder.leave_fragment_spread(ctx.ctx, spread, info)
708 }
709}
710
711impl<'a> private::FoldNode<'a> for InlineFragment<'a> {
712 #[inline]
713 fn fold_with_ctx<'b, F: Folder<'a>>(
714 &self,
715 info: &mut VisitInfo,
716 ctx: &'a private::FolderContext<'a>,
717 folder: &'b mut F,
718 ) -> Result<Self> {
719 let fragment = folder.enter_inline_fragment(ctx.ctx, self.clone(), info)?;
720
721 let type_condition = match fragment.type_condition {
722 Some(condition) => {
723 info.path.push(PathSegment::Name);
724 let folded = condition.fold_with_ctx(info, ctx, folder)?;
725 info.path.pop();
726 Some(folded)
727 }
728 None => None,
729 };
730
731 info.path.push(PathSegment::Directives);
732 let directives = fragment.directives.fold_with_ctx(info, ctx, folder)?;
733 info.path.pop();
734
735 info.path.push(PathSegment::SelectionSet);
736 let selection_set = fragment.selection_set.fold_with_ctx(info, ctx, folder)?;
737 info.path.pop();
738
739 let fragment = InlineFragment {
740 type_condition,
741 directives,
742 selection_set,
743 };
744 folder.leave_inline_fragment(ctx.ctx, fragment, info)
745 }
746}
747
748impl<'a> private::FoldNode<'a> for SelectionSet<'a> {
749 #[inline]
750 fn fold_with_ctx<'b, F: Folder<'a>>(
751 &self,
752 info: &mut VisitInfo,
753 ctx: &'a private::FolderContext<'a>,
754 folder: &'b mut F,
755 ) -> Result<Self> {
756 let new_selections_iter = folder
757 .selection_set(ctx.ctx, self.clone(), info)?
758 .into_iter()
759 .enumerate()
760 .map(|(index, selection)| -> Result<Selection> {
761 info.path.push(PathSegment::Index(index));
762 let folded = match selection {
763 Selection::Field(field) => Ok(field.fold_with_ctx(info, ctx, folder)?.into()),
764 Selection::FragmentSpread(spread) => {
765 Ok(spread.fold_with_ctx(info, ctx, folder)?.into())
766 }
767 Selection::InlineFragment(fragment) => {
768 Ok(fragment.fold_with_ctx(info, ctx, folder)?.into())
769 }
770 };
771 info.path.pop();
772 folded
773 });
774
775 let mut new_selections = Vec::new_in(&ctx.ctx.arena);
776 for item in new_selections_iter {
777 new_selections.push(item?);
778 }
779 Ok(SelectionSet {
780 selections: new_selections,
781 })
782 }
783}
784
785impl<'a> private::FoldNode<'a> for FragmentDefinition<'a> {
786 #[inline]
787 fn fold_with_ctx<'b, F: Folder<'a>>(
788 &self,
789 info: &mut VisitInfo,
790 ctx: &'a private::FolderContext<'a>,
791 folder: &'b mut F,
792 ) -> Result<Self> {
793 let fragment = &folder.enter_fragment(ctx.ctx, self.clone(), info)?;
794
795 info.path.push(PathSegment::Name);
796 let name = fragment.name.fold_with_ctx(info, ctx, folder)?;
797 info.path.pop();
798
799 info.path.push(PathSegment::Type);
800 let type_condition = fragment.type_condition.fold_with_ctx(info, ctx, folder)?;
801 info.path.pop();
802
803 info.path.push(PathSegment::Directives);
804 let directives = fragment.directives.fold_with_ctx(info, ctx, folder)?;
805 info.path.pop();
806
807 info.path.push(PathSegment::SelectionSet);
808 let selection_set = fragment.selection_set.fold_with_ctx(info, ctx, folder)?;
809 info.path.pop();
810
811 let fragment = FragmentDefinition {
812 name,
813 type_condition,
814 directives,
815 selection_set,
816 };
817 folder.leave_fragment(ctx.ctx, fragment, info)
818 }
819}
820
821impl<'a> private::FoldNode<'a> for OperationDefinition<'a> {
822 #[inline]
823 fn fold_with_ctx<'b, F: Folder<'a>>(
824 &self,
825 info: &mut VisitInfo,
826 ctx: &'a private::FolderContext<'a>,
827 folder: &'b mut F,
828 ) -> Result<Self> {
829 let operation = &folder.enter_operation(ctx.ctx, self.clone(), info)?;
830
831 info.path.push(PathSegment::VariableDefinitions);
832 let variable_definitions = operation
833 .variable_definitions
834 .fold_with_ctx(info, ctx, folder)?;
835 info.path.pop();
836
837 info.path.push(PathSegment::Directives);
838 let directives = operation.directives.fold_with_ctx(info, ctx, folder)?;
839 info.path.pop();
840
841 info.path.push(PathSegment::SelectionSet);
842 let selection_set = operation.selection_set.fold_with_ctx(info, ctx, folder)?;
843 info.path.pop();
844
845 let operation = OperationDefinition {
846 operation: operation.operation,
847 name: operation.name,
848 variable_definitions,
849 directives,
850 selection_set,
851 };
852 folder.leave_operation(ctx.ctx, operation, info)
853 }
854}
855
856impl<'a> private::FoldNode<'a> for Document<'a> {
857 #[inline]
858 fn fold_with_ctx<'b, F: Folder<'a>>(
859 &self,
860 info: &mut VisitInfo,
861 ctx: &'a private::FolderContext<'a>,
862 folder: &'b mut F,
863 ) -> Result<Self> {
864 let new_definitions_iter =
865 self.definitions
866 .iter()
867 .enumerate()
868 .map(|(index, selection)| -> Result<Definition> {
869 info.path.push(PathSegment::Index(index));
870 let folded = match selection {
871 Definition::Operation(operation) => {
872 Ok(operation.fold_with_ctx(info, ctx, folder)?.into())
873 }
874 Definition::Fragment(fragment) => {
875 Ok(fragment.fold_with_ctx(info, ctx, folder)?.into())
876 }
877 };
878 info.path.pop();
879 folded
880 });
881 let mut new_definitions = Vec::new_in(&ctx.ctx.arena);
882 for item in new_definitions_iter.rev() {
883 new_definitions.push(item?);
884 }
885 Ok(Document {
886 definitions: new_definitions,
887 size_hint: self.size_hint,
888 })
889 }
890}
891
892impl<'a> FoldDocument<'a> for Document<'a> {
893 fn fold_operation<'b, F: Folder<'a>>(
898 &'a self,
899 ctx: &'a ASTContext,
900 operation_name: Option<&'a str>,
901 folder: &'b mut F,
902 ) -> Result<&'a Self> {
903 let (operation, index) = self.operation_with_index(operation_name)?;
904 let folder_ctx = ctx.alloc(private::FolderContext::with_document(ctx, self));
905 let mut info = VisitInfo::default();
906 info.path.push(PathSegment::Index(index));
907 let operation = private::FoldNode::fold_with_ctx(operation, &mut info, folder_ctx, folder)?;
908
909 let mut borrowed_definitions = folder_ctx.definitions.clone().into_inner();
910
911 borrowed_definitions.push(Definition::Operation(operation));
912 Ok(ctx.alloc(Document {
913 definitions: Vec::from_iter_in(borrowed_definitions.into_iter().rev(), &ctx.arena),
914 size_hint: self.size_hint,
915 }))
916 }
917}
918
919#[cfg(test)]
920mod tests {
921 use bumpalo::collections::CollectIn;
922
923 use super::{super::*, FoldNode, Folder, Result};
924 use crate::{
925 ast::*,
926 visit::{folder::FoldDocument, folder_simple::SimpleFolder},
927 };
928
929 #[test]
930 fn kitchen_sink() {
931 #[derive(Default)]
932 struct FoldNoop {}
933 impl<'a> SimpleFolder<'a> for FoldNoop {}
934
935 let ctx = ASTContext::new();
936 let query = include_str!("../../fixture/kitchen_sink.graphql");
937 let ast = Document::parse(&ctx, query).unwrap();
938
939 let output = ast
940 .fold_operation(&ctx, Some("queryName"), &mut FoldNoop::default())
941 .unwrap();
942
943 let actual = output.print();
944 let expected = indoc::indoc! {r#"
945 query queryName($foo: ComplexType, $site: Site = MOBILE) @onQuery {
946 whoever123is: node(id: [123, 456]) {
947 id
948 ... on User @onInlineFragment {
949 field2 {
950 id
951 alias: field1(first: 10, after: $foo) @include(if: $foo) {
952 id
953 ...frag @onFragmentSpread
954 }
955 }
956 }
957 ... @skip(unless: $foo) {
958 id
959 }
960 ... {
961 id
962 }
963 }
964 }
965
966 fragment frag on Friend @onFragmentDefinition {
967 foo(size: $site, bar: 12, obj: {key: "value", block: """
968 block string uses \"""
969 """})
970 }"#};
971
972 assert_eq!(actual, expected);
973 }
974
975 struct InfoFolder {}
976
977 impl<'a> Folder<'a> for InfoFolder {
978 fn enter_fragment_spread(
979 &mut self,
980 _ctx: &'a ASTContext,
981 fragment_spread: FragmentSpread<'a>,
982 info: &VisitInfo,
983 ) -> Result<FragmentSpread<'a>> {
984 let expected_path = Path::try_from(
987 "0.selectionSet.0.selectionSet.1.selectionSet.0.selectionSet.1.selectionSet.1",
988 )
989 .unwrap();
990 assert_eq!(info.path, expected_path);
991 Ok(fragment_spread)
992 }
993
994 fn value(
995 &mut self,
996 _ctx: &'a ASTContext,
997 value: Value<'a>,
998 info: &VisitInfo,
999 ) -> Result<Value<'a>> {
1000 if let Value::Object(_) = value {
1001 let expected_path = Path::try_from("3.selectionSet.0.arguments.2.value").unwrap();
1004 assert_eq!(info.path, expected_path);
1005 }
1006
1007 Ok(value)
1008 }
1009 }
1010
1011 #[test]
1012 fn fold_info_path() {
1013 let ctx = ASTContext::new();
1014 let query = include_str!("../../fixture/kitchen_sink.graphql");
1015 let ast = Document::parse(&ctx, query).unwrap();
1016
1017 let mut folder = InfoFolder {};
1018 let _ = ast.fold(&ctx, &mut folder).unwrap();
1019 }
1020
1021 #[test]
1022 fn fold_operation_info_path() {
1023 let ctx = ASTContext::new();
1024 let query = include_str!("../../fixture/kitchen_sink.graphql");
1025 let ast = Document::parse(&ctx, query).unwrap();
1026
1027 let mut folder = InfoFolder {};
1028 let _ = ast
1029 .fold_operation(&ctx, Some("queryName"), &mut folder)
1030 .unwrap();
1031 }
1032
1033 struct AddTypenames {}
1034
1035 use std::iter;
1036
1037 impl<'arena> Folder<'arena> for AddTypenames {
1038 fn selection_set(
1039 &mut self,
1040 ctx: &'arena ASTContext,
1041 selection_set: SelectionSet<'arena>,
1042 _info: &VisitInfo,
1043 ) -> Result<SelectionSet<'arena>> {
1044 if selection_set.is_empty() {
1045 return Ok(selection_set);
1046 }
1047
1048 let has_typename = selection_set.selections.iter().any(|selection| {
1049 selection
1050 .field()
1051 .map(|field| field.name == "__typename" && field.alias.is_none())
1052 .unwrap_or(false)
1053 });
1054
1055 if !has_typename {
1056 let typename_field = Selection::Field(Field {
1057 alias: None,
1058 name: "__typename",
1059 arguments: Arguments::default_in(&ctx.arena),
1060 directives: Directives::default_in(&ctx.arena),
1061 selection_set: SelectionSet::default_in(&ctx.arena),
1062 });
1063
1064 let new_selections =
1065 selection_set
1066 .into_iter()
1067 .chain(iter::once(typename_field))
1068 .collect_in::<bumpalo::collections::Vec<Selection>>(&ctx.arena);
1069
1070 Ok(SelectionSet {
1071 selections: new_selections,
1072 })
1073 } else {
1074 Ok(selection_set)
1075 }
1076 }
1077 }
1078
1079 #[test]
1080 fn fold_typename() {
1081 let ctx = ASTContext::new();
1082 let query = "query { todo { id author { id } } }";
1083 let ast = Document::parse(&ctx, query).unwrap();
1084
1085 let mut folder = AddTypenames {};
1086 let new_ast = ast.fold_operation(&ctx, None, &mut folder);
1087 assert_eq!(
1088 new_ast.unwrap().print(),
1089 "{\n todo {\n id\n author {\n id\n __typename\n }\n __typename\n }\n __typename\n}"
1090 );
1091 }
1092}