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 let mut borrowed_fragment_names = ctx.fragment_names.borrow_mut();
648 let fragment_name =
649 if let Some(fragment_name) = borrowed_fragment_names.get(fragment_name) {
650 fragment_name
651 } else if let Some(FragmentDefinitionWithIndex { fragment, index }) =
652 ctx.fragments.borrow().get(spread.name.name)
653 {
654 let path = info.path.clone();
655 info.path = Path::default();
656 info.path.push(PathSegment::Index(index.to_owned()));
657 let fragment = fragment.fold_with_ctx(info, ctx, folder)?;
658 info.path = path;
659
660 let fragment_name = fragment.name.name;
661 ctx.definitions
663 .borrow_mut()
664 .push(Definition::Fragment(fragment));
665 borrowed_fragment_names.insert(fragment_name, fragment_name);
666 fragment_name
667 } else {
668 return Err(Error::new(
669 format!("The fragment '{}' does not exist", fragment_name),
670 None,
671 ));
672 };
673
674 info.path.push(PathSegment::Directives);
675 let directives = spread.directives.fold_with_ctx(info, ctx, folder)?;
676 info.path.pop();
677
678 FragmentSpread {
679 name: fragment_name.into(),
680 directives,
681 }
682 } else {
683 info.path.push(PathSegment::Name);
684 let name = spread.name.fold_with_ctx(info, ctx, folder)?;
685 info.path.pop();
686
687 info.path.push(PathSegment::Directives);
688 let directives = spread.directives.fold_with_ctx(info, ctx, folder)?;
689 info.path.pop();
690
691 FragmentSpread { name, directives }
692 };
693 folder.leave_fragment_spread(ctx.ctx, spread, info)
694 }
695}
696
697impl<'a> private::FoldNode<'a> for InlineFragment<'a> {
698 #[inline]
699 fn fold_with_ctx<'b, F: Folder<'a>>(
700 &self,
701 info: &mut VisitInfo,
702 ctx: &'a private::FolderContext<'a>,
703 folder: &'b mut F,
704 ) -> Result<Self> {
705 let fragment = folder.enter_inline_fragment(ctx.ctx, self.clone(), info)?;
706
707 let type_condition = match fragment.type_condition {
708 Some(condition) => {
709 info.path.push(PathSegment::Name);
710 let folded = condition.fold_with_ctx(info, ctx, folder)?;
711 info.path.pop();
712 Some(folded)
713 }
714 None => None,
715 };
716
717 info.path.push(PathSegment::Directives);
718 let directives = fragment.directives.fold_with_ctx(info, ctx, folder)?;
719 info.path.pop();
720
721 info.path.push(PathSegment::SelectionSet);
722 let selection_set = fragment.selection_set.fold_with_ctx(info, ctx, folder)?;
723 info.path.pop();
724
725 let fragment = InlineFragment {
726 type_condition,
727 directives,
728 selection_set,
729 };
730 folder.leave_inline_fragment(ctx.ctx, fragment, info)
731 }
732}
733
734impl<'a> private::FoldNode<'a> for SelectionSet<'a> {
735 #[inline]
736 fn fold_with_ctx<'b, F: Folder<'a>>(
737 &self,
738 info: &mut VisitInfo,
739 ctx: &'a private::FolderContext<'a>,
740 folder: &'b mut F,
741 ) -> Result<Self> {
742 let new_selections_iter = folder
743 .selection_set(ctx.ctx, self.clone(), info)?
744 .into_iter()
745 .enumerate()
746 .map(|(index, selection)| -> Result<Selection> {
747 info.path.push(PathSegment::Index(index));
748 let folded = match selection {
749 Selection::Field(field) => Ok(field.fold_with_ctx(info, ctx, folder)?.into()),
750 Selection::FragmentSpread(spread) => {
751 Ok(spread.fold_with_ctx(info, ctx, folder)?.into())
752 }
753 Selection::InlineFragment(fragment) => {
754 Ok(fragment.fold_with_ctx(info, ctx, folder)?.into())
755 }
756 };
757 info.path.pop();
758 folded
759 });
760
761 let mut new_selections = Vec::new_in(&ctx.ctx.arena);
762 for item in new_selections_iter {
763 new_selections.push(item?);
764 }
765 Ok(SelectionSet {
766 selections: new_selections,
767 })
768 }
769}
770
771impl<'a> private::FoldNode<'a> for FragmentDefinition<'a> {
772 #[inline]
773 fn fold_with_ctx<'b, F: Folder<'a>>(
774 &self,
775 info: &mut VisitInfo,
776 ctx: &'a private::FolderContext<'a>,
777 folder: &'b mut F,
778 ) -> Result<Self> {
779 let fragment = &folder.enter_fragment(ctx.ctx, self.clone(), info)?;
780
781 info.path.push(PathSegment::Name);
782 let name = fragment.name.fold_with_ctx(info, ctx, folder)?;
783 info.path.pop();
784
785 info.path.push(PathSegment::Type);
786 let type_condition = fragment.type_condition.fold_with_ctx(info, ctx, folder)?;
787 info.path.pop();
788
789 info.path.push(PathSegment::Directives);
790 let directives = fragment.directives.fold_with_ctx(info, ctx, folder)?;
791 info.path.pop();
792
793 info.path.push(PathSegment::SelectionSet);
794 let selection_set = fragment.selection_set.fold_with_ctx(info, ctx, folder)?;
795 info.path.pop();
796
797 let fragment = FragmentDefinition {
798 name,
799 type_condition,
800 directives,
801 selection_set,
802 };
803 folder.leave_fragment(ctx.ctx, fragment, info)
804 }
805}
806
807impl<'a> private::FoldNode<'a> for OperationDefinition<'a> {
808 #[inline]
809 fn fold_with_ctx<'b, F: Folder<'a>>(
810 &self,
811 info: &mut VisitInfo,
812 ctx: &'a private::FolderContext<'a>,
813 folder: &'b mut F,
814 ) -> Result<Self> {
815 let operation = &folder.enter_operation(ctx.ctx, self.clone(), info)?;
816
817 info.path.push(PathSegment::VariableDefinitions);
818 let variable_definitions = operation
819 .variable_definitions
820 .fold_with_ctx(info, ctx, folder)?;
821 info.path.pop();
822
823 info.path.push(PathSegment::Directives);
824 let directives = operation.directives.fold_with_ctx(info, ctx, folder)?;
825 info.path.pop();
826
827 info.path.push(PathSegment::SelectionSet);
828 let selection_set = operation.selection_set.fold_with_ctx(info, ctx, folder)?;
829 info.path.pop();
830
831 let operation = OperationDefinition {
832 operation: operation.operation,
833 name: operation.name,
834 variable_definitions,
835 directives,
836 selection_set,
837 };
838 folder.leave_operation(ctx.ctx, operation, info)
839 }
840}
841
842impl<'a> private::FoldNode<'a> for Document<'a> {
843 #[inline]
844 fn fold_with_ctx<'b, F: Folder<'a>>(
845 &self,
846 info: &mut VisitInfo,
847 ctx: &'a private::FolderContext<'a>,
848 folder: &'b mut F,
849 ) -> Result<Self> {
850 let new_definitions_iter =
851 self.definitions
852 .iter()
853 .enumerate()
854 .map(|(index, selection)| -> Result<Definition> {
855 info.path.push(PathSegment::Index(index));
856 let folded = match selection {
857 Definition::Operation(operation) => {
858 Ok(operation.fold_with_ctx(info, ctx, folder)?.into())
859 }
860 Definition::Fragment(fragment) => {
861 Ok(fragment.fold_with_ctx(info, ctx, folder)?.into())
862 }
863 };
864 info.path.pop();
865 folded
866 });
867 let mut new_definitions = Vec::new_in(&ctx.ctx.arena);
868 for item in new_definitions_iter.rev() {
869 new_definitions.push(item?);
870 }
871 Ok(Document {
872 definitions: new_definitions,
873 size_hint: self.size_hint,
874 })
875 }
876}
877
878impl<'a> FoldDocument<'a> for Document<'a> {
879 fn fold_operation<'b, F: Folder<'a>>(
884 &'a self,
885 ctx: &'a ASTContext,
886 operation_name: Option<&'a str>,
887 folder: &'b mut F,
888 ) -> Result<&'a Self> {
889 let (operation, index) = self.operation_with_index(operation_name)?;
890 let folder_ctx = ctx.alloc(private::FolderContext::with_document(ctx, self));
891 let mut info = VisitInfo::default();
892 info.path.push(PathSegment::Index(index));
893 let operation = private::FoldNode::fold_with_ctx(operation, &mut info, folder_ctx, folder)?;
894
895 let mut borrowed_definitions = folder_ctx.definitions.clone().into_inner();
896
897 borrowed_definitions.push(Definition::Operation(operation));
898 Ok(ctx.alloc(Document {
899 definitions: Vec::from_iter_in(borrowed_definitions.into_iter().rev(), &ctx.arena),
900 size_hint: self.size_hint,
901 }))
902 }
903}
904
905#[cfg(test)]
906mod tests {
907 use bumpalo::collections::CollectIn;
908
909 use super::{super::*, FoldNode, Folder, Result};
910 use crate::{
911 ast::*,
912 visit::{folder::FoldDocument, folder_simple::SimpleFolder},
913 };
914
915 #[test]
916 fn kitchen_sink() {
917 #[derive(Default)]
918 struct FoldNoop {}
919 impl<'a> SimpleFolder<'a> for FoldNoop {}
920
921 let ctx = ASTContext::new();
922 let query = include_str!("../../fixture/kitchen_sink.graphql");
923 let ast = Document::parse(&ctx, query).unwrap();
924
925 let output = ast
926 .fold_operation(&ctx, Some("queryName"), &mut FoldNoop::default())
927 .unwrap();
928
929 let actual = output.print();
930 let expected = indoc::indoc! {r#"
931 query queryName($foo: ComplexType, $site: Site = MOBILE) @onQuery {
932 whoever123is: node(id: [123, 456]) {
933 id
934 ... on User @onInlineFragment {
935 field2 {
936 id
937 alias: field1(first: 10, after: $foo) @include(if: $foo) {
938 id
939 ...frag @onFragmentSpread
940 }
941 }
942 }
943 ... @skip(unless: $foo) {
944 id
945 }
946 ... {
947 id
948 }
949 }
950 }
951
952 fragment frag on Friend @onFragmentDefinition {
953 foo(size: $site, bar: 12, obj: {key: "value", block: """
954 block string uses \"""
955 """})
956 }"#};
957
958 assert_eq!(actual, expected);
959 }
960
961 struct InfoFolder {}
962
963 impl<'a> Folder<'a> for InfoFolder {
964 fn enter_fragment_spread(
965 &mut self,
966 _ctx: &'a ASTContext,
967 fragment_spread: FragmentSpread<'a>,
968 info: &VisitInfo,
969 ) -> Result<FragmentSpread<'a>> {
970 let expected_path = Path::try_from(
973 "0.selectionSet.0.selectionSet.1.selectionSet.0.selectionSet.1.selectionSet.1",
974 )
975 .unwrap();
976 assert_eq!(info.path, expected_path);
977 Ok(fragment_spread)
978 }
979
980 fn value(
981 &mut self,
982 _ctx: &'a ASTContext,
983 value: Value<'a>,
984 info: &VisitInfo,
985 ) -> Result<Value<'a>> {
986 if let Value::Object(_) = value {
987 let expected_path = Path::try_from("3.selectionSet.0.arguments.2.value").unwrap();
990 assert_eq!(info.path, expected_path);
991 }
992
993 Ok(value)
994 }
995 }
996
997 #[test]
998 fn fold_info_path() {
999 let ctx = ASTContext::new();
1000 let query = include_str!("../../fixture/kitchen_sink.graphql");
1001 let ast = Document::parse(&ctx, query).unwrap();
1002
1003 let mut folder = InfoFolder {};
1004 let _ = ast.fold(&ctx, &mut folder).unwrap();
1005 }
1006
1007 #[test]
1008 fn fold_operation_info_path() {
1009 let ctx = ASTContext::new();
1010 let query = include_str!("../../fixture/kitchen_sink.graphql");
1011 let ast = Document::parse(&ctx, query).unwrap();
1012
1013 let mut folder = InfoFolder {};
1014 let _ = ast
1015 .fold_operation(&ctx, Some("queryName"), &mut folder)
1016 .unwrap();
1017 }
1018
1019 struct AddTypenames {}
1020
1021 use std::iter;
1022
1023 impl<'arena> Folder<'arena> for AddTypenames {
1024 fn selection_set(
1025 &mut self,
1026 ctx: &'arena ASTContext,
1027 selection_set: SelectionSet<'arena>,
1028 _info: &VisitInfo,
1029 ) -> Result<SelectionSet<'arena>> {
1030 if selection_set.is_empty() {
1031 return Ok(selection_set);
1032 }
1033
1034 let has_typename = selection_set.selections.iter().any(|selection| {
1035 selection
1036 .field()
1037 .map(|field| field.name == "__typename" && field.alias.is_none())
1038 .unwrap_or(false)
1039 });
1040
1041 if !has_typename {
1042 let typename_field = Selection::Field(Field {
1043 alias: None,
1044 name: "__typename",
1045 arguments: Arguments::default_in(&ctx.arena),
1046 directives: Directives::default_in(&ctx.arena),
1047 selection_set: SelectionSet::default_in(&ctx.arena),
1048 });
1049
1050 let new_selections = selection_set
1051 .into_iter()
1052 .chain(iter::once(typename_field))
1053 .collect_in::<bumpalo::collections::Vec<Selection>>(&ctx.arena);
1054
1055 Ok(SelectionSet { selections: new_selections })
1056 } else {
1057 Ok(selection_set)
1058 }
1059 }
1060 }
1061
1062 #[test]
1063 fn fold_typename() {
1064 let ctx = ASTContext::new();
1065 let query = "query { todo { id author { id } } }";
1066 let ast = Document::parse(&ctx, query).unwrap();
1067
1068 let mut folder = AddTypenames {};
1069 let new_ast = ast
1070 .fold_operation(&ctx, None, &mut folder);
1071 assert_eq!(
1072 new_ast.unwrap().print(),
1073 "{\n todo {\n id\n author {\n id\n __typename\n }\n __typename\n }\n __typename\n}"
1074 );
1075 }
1076}