1use super::visitor::*;
2use crate::ast::*;
3
4pub struct ComposedVisitor<'a, Context, A: Visitor<'a, Context>, B: Visitor<'a, Context>> {
15 _marker: std::marker::PhantomData<&'a Context>,
16 depth: usize,
17 skip_a: usize,
18 skip_b: usize,
19 pub a: A,
20 pub b: B,
21}
22
23impl<'a, C, A: Visitor<'a, C>, B: Visitor<'a, C>> ComposedVisitor<'a, C, A, B> {
24 #[inline]
26 pub fn new(a: A, b: B) -> ComposedVisitor<'a, C, A, B> {
27 ComposedVisitor {
28 _marker: std::marker::PhantomData,
29 depth: 1,
30 skip_a: usize::MAX,
31 skip_b: usize::MAX,
32 a,
33 b,
34 }
35 }
36
37 #[inline(always)]
38 fn compose_flow_enter<'b, Node: 'a>(
39 &mut self,
40 fn_a: fn(_self: &mut A, ctx: &mut C, node: &'a Node, info: &VisitInfo) -> VisitFlow,
41 fn_b: fn(_self: &mut B, ctx: &mut C, node: &'a Node, info: &VisitInfo) -> VisitFlow,
42 node: &'a Node,
43 info: &VisitInfo,
44 ctx: &mut C,
45 ) -> VisitFlow {
46 let mut all_skip = true;
47 let mut all_break = true;
48
49 if self.skip_a == usize::MAX || self.skip_a == self.depth {
50 self.skip_a = usize::MAX;
51 let flow = fn_a(&mut self.a, ctx, node, info);
52 if flow == VisitFlow::Break {
53 self.skip_a = 0;
54 all_skip = false;
55 } else if flow == VisitFlow::Skip {
56 self.skip_a = self.depth;
57 all_break = false;
58 } else {
59 all_break = false;
60 all_skip = false;
61 }
62 } else if self.skip_b == 0 {
63 all_skip = false;
64 } else {
65 all_break = false;
66 }
67
68 if self.skip_b == usize::MAX || self.skip_b == self.depth {
69 self.skip_b = usize::MAX;
70 let flow = fn_b(&mut self.b, ctx, node, info);
71 if flow == VisitFlow::Break {
72 self.skip_b = 0;
73 all_skip = false;
74 } else if flow == VisitFlow::Skip {
75 self.skip_b = self.depth;
76 all_break = false;
77 } else {
78 all_break = false;
79 all_skip = false;
80 }
81 } else if self.skip_b == 0 {
82 all_skip = false;
83 } else {
84 all_break = false;
85 };
86
87 if all_break {
88 VisitFlow::Break
89 } else if all_skip {
90 if self.skip_a == self.depth {
91 self.skip_a = usize::MAX;
92 }
93 if self.skip_b == self.depth {
94 self.skip_b = usize::MAX;
95 }
96 VisitFlow::Skip
97 } else {
98 self.depth += 1;
99 VisitFlow::Next
100 }
101 }
102
103 #[inline(always)]
104 fn compose_flow_leave<'b, Node: 'a>(
105 &mut self,
106 fn_a: fn(_self: &mut A, ctx: &mut C, node: &'a Node, info: &VisitInfo) -> VisitFlow,
107 fn_b: fn(_self: &mut B, ctx: &mut C, node: &'a Node, info: &VisitInfo) -> VisitFlow,
108 node: &'a Node,
109 info: &VisitInfo,
110 ctx: &'b mut C,
111 ) -> VisitFlow {
112 self.depth -= 1;
113 let mut all_break = true;
114
115 if self.skip_a == usize::MAX {
116 let flow = fn_a(&mut self.a, ctx, node, info);
117 if flow == VisitFlow::Break {
118 self.skip_a = 0;
119 } else {
120 all_break = false;
121 }
122 } else if self.skip_a == self.depth {
123 self.skip_a = usize::MAX;
124 all_break = false;
125 } else if self.skip_a != 0 {
126 all_break = false;
127 }
128
129 if self.skip_b == usize::MAX {
130 let flow = fn_b(&mut self.b, ctx, node, info);
131 if flow == VisitFlow::Break {
132 self.skip_b = 0;
133 } else {
134 all_break = false;
135 }
136 } else if self.skip_b == self.depth {
137 self.skip_b = usize::MAX;
138 all_break = false;
139 } else if self.skip_b != 0 {
140 all_break = false;
141 }
142
143 if all_break {
144 VisitFlow::Break
145 } else {
146 VisitFlow::Next
147 }
148 }
149}
150
151impl<'a, C, A: Visitor<'a, C>, B: Visitor<'a, C>> Visitor<'a, C> for ComposedVisitor<'a, C, A, B> {
152 #[inline]
153 fn enter_document(
154 &mut self,
155 ctx: &mut C,
156 document: &'a Document<'a>,
157 info: &VisitInfo,
158 ) -> VisitFlow {
159 self.compose_flow_enter(A::enter_document, B::enter_document, document, info, ctx)
160 }
161
162 #[inline]
163 fn leave_document(
164 &mut self,
165 ctx: &mut C,
166 document: &'a Document<'a>,
167 info: &VisitInfo,
168 ) -> VisitFlow {
169 self.compose_flow_leave(A::leave_document, B::leave_document, document, info, ctx)
170 }
171
172 #[inline]
173 fn enter_operation(
174 &mut self,
175 ctx: &mut C,
176 operation: &'a OperationDefinition<'a>,
177 info: &VisitInfo,
178 ) -> VisitFlow {
179 self.compose_flow_enter(A::enter_operation, B::enter_operation, operation, info, ctx)
180 }
181
182 #[inline]
183 fn leave_operation(
184 &mut self,
185 ctx: &mut C,
186 operation: &'a OperationDefinition<'a>,
187 info: &VisitInfo,
188 ) -> VisitFlow {
189 self.compose_flow_leave(A::leave_operation, B::leave_operation, operation, info, ctx)
190 }
191
192 #[inline]
193 fn enter_fragment(
194 &mut self,
195 ctx: &mut C,
196 fragment: &'a FragmentDefinition<'a>,
197 info: &VisitInfo,
198 ) -> VisitFlow {
199 self.compose_flow_enter(A::enter_fragment, B::enter_fragment, fragment, info, ctx)
200 }
201
202 #[inline]
203 fn leave_fragment(
204 &mut self,
205 ctx: &mut C,
206 fragment: &'a FragmentDefinition<'a>,
207 info: &VisitInfo,
208 ) -> VisitFlow {
209 self.compose_flow_leave(A::leave_fragment, B::leave_fragment, fragment, info, ctx)
210 }
211
212 #[inline]
213 fn enter_variable_definition(
214 &mut self,
215 ctx: &mut C,
216 var_def: &'a VariableDefinition<'a>,
217 info: &VisitInfo,
218 ) -> VisitFlow {
219 self.compose_flow_enter(
220 A::enter_variable_definition,
221 B::enter_variable_definition,
222 var_def,
223 info,
224 ctx,
225 )
226 }
227
228 #[inline]
229 fn leave_variable_definition(
230 &mut self,
231 ctx: &mut C,
232 var_def: &'a VariableDefinition<'a>,
233 info: &VisitInfo,
234 ) -> VisitFlow {
235 self.compose_flow_leave(
236 A::leave_variable_definition,
237 B::leave_variable_definition,
238 var_def,
239 info,
240 ctx,
241 )
242 }
243
244 #[inline]
245 fn enter_selection_set(
246 &mut self,
247 ctx: &mut C,
248 selection_set: &'a SelectionSet<'a>,
249 info: &VisitInfo,
250 ) -> VisitFlow {
251 self.compose_flow_enter(
252 A::enter_selection_set,
253 B::enter_selection_set,
254 selection_set,
255 info,
256 ctx,
257 )
258 }
259
260 #[inline]
261 fn leave_selection_set(
262 &mut self,
263 ctx: &mut C,
264 selection_set: &'a SelectionSet<'a>,
265 info: &VisitInfo,
266 ) -> VisitFlow {
267 self.compose_flow_leave(
268 A::leave_selection_set,
269 B::leave_selection_set,
270 selection_set,
271 info,
272 ctx,
273 )
274 }
275
276 #[inline]
277 fn enter_fragment_spread(
278 &mut self,
279 ctx: &mut C,
280 fragment_spread: &'a FragmentSpread<'a>,
281 info: &VisitInfo,
282 ) -> VisitFlow {
283 self.compose_flow_enter(
284 A::enter_fragment_spread,
285 B::enter_fragment_spread,
286 fragment_spread,
287 info,
288 ctx,
289 )
290 }
291
292 #[inline]
293 fn leave_fragment_spread(
294 &mut self,
295 ctx: &mut C,
296 fragment_spread: &'a FragmentSpread<'a>,
297 info: &VisitInfo,
298 ) -> VisitFlow {
299 self.compose_flow_leave(
300 A::leave_fragment_spread,
301 B::leave_fragment_spread,
302 fragment_spread,
303 info,
304 ctx,
305 )
306 }
307
308 #[inline]
309 fn enter_inline_fragment(
310 &mut self,
311 ctx: &mut C,
312 inline_fragment: &'a InlineFragment<'a>,
313 info: &VisitInfo,
314 ) -> VisitFlow {
315 self.compose_flow_enter(
316 A::enter_inline_fragment,
317 B::enter_inline_fragment,
318 inline_fragment,
319 info,
320 ctx,
321 )
322 }
323
324 #[inline]
325 fn leave_inline_fragment(
326 &mut self,
327 ctx: &mut C,
328 inline_fragment: &'a InlineFragment<'a>,
329 info: &VisitInfo,
330 ) -> VisitFlow {
331 self.compose_flow_leave(
332 A::leave_inline_fragment,
333 B::leave_inline_fragment,
334 inline_fragment,
335 info,
336 ctx,
337 )
338 }
339
340 #[inline]
341 fn enter_field(&mut self, ctx: &mut C, field: &'a Field<'a>, info: &VisitInfo) -> VisitFlow {
342 self.compose_flow_enter(A::enter_field, B::enter_field, field, info, ctx)
343 }
344
345 #[inline]
346 fn leave_field(&mut self, ctx: &mut C, field: &'a Field<'a>, info: &VisitInfo) -> VisitFlow {
347 self.compose_flow_leave(A::leave_field, B::leave_field, field, info, ctx)
348 }
349
350 #[inline]
351 fn enter_directive(
352 &mut self,
353 ctx: &mut C,
354 directive: &'a Directive<'a>,
355 info: &VisitInfo,
356 ) -> VisitFlow {
357 self.compose_flow_enter(A::enter_directive, B::enter_directive, directive, info, ctx)
358 }
359
360 #[inline]
361 fn leave_directive(
362 &mut self,
363 ctx: &mut C,
364 directive: &'a Directive<'a>,
365 info: &VisitInfo,
366 ) -> VisitFlow {
367 self.compose_flow_leave(A::leave_directive, B::leave_directive, directive, info, ctx)
368 }
369
370 #[inline]
371 fn enter_argument(
372 &mut self,
373 ctx: &mut C,
374 argument: &'a Argument<'a>,
375 info: &VisitInfo,
376 ) -> VisitFlow {
377 self.compose_flow_enter(A::enter_argument, B::enter_argument, argument, info, ctx)
378 }
379
380 #[inline]
381 fn leave_argument(
382 &mut self,
383 ctx: &mut C,
384 argument: &'a Argument<'a>,
385 info: &VisitInfo,
386 ) -> VisitFlow {
387 self.compose_flow_leave(A::leave_argument, B::leave_argument, argument, info, ctx)
388 }
389}
390
391#[cfg(test)]
392mod tests {
393 use super::super::visitor::tests::*;
394 use super::*;
395
396 #[derive(Debug, PartialEq, Default)]
397 struct SkipVisitor {
398 pub(crate) enter: usize,
399 pub(crate) leave: usize,
400 pub(crate) enter_field: usize,
401 pub(crate) leave_field: usize,
402 pub(crate) enter_directive: usize,
403 pub(crate) leave_directive: usize,
404 }
405
406 impl<'a> Visitor<'a, ()> for SkipVisitor {
407 fn enter_selection_set(
408 &mut self,
409 _: &mut (),
410 _selection_set: &SelectionSet,
411 _info: &VisitInfo,
412 ) -> VisitFlow {
413 self.enter += 1;
414 VisitFlow::Next
415 }
416 fn leave_selection_set(
417 &mut self,
418 _: &mut (),
419 _selection_set: &SelectionSet,
420 _info: &VisitInfo,
421 ) -> VisitFlow {
422 self.leave += 1;
423 VisitFlow::Next
424 }
425
426 fn enter_field(&mut self, _: &mut (), _field: &Field, _info: &VisitInfo) -> VisitFlow {
427 self.enter_field += 1;
428 VisitFlow::Skip
429 }
430 fn leave_field(&mut self, _: &mut (), _field: &Field, _info: &VisitInfo) -> VisitFlow {
431 self.leave_field += 1;
432 VisitFlow::Skip
433 }
434
435 fn enter_directive(
436 &mut self,
437 _: &mut (),
438 _field: &Directive,
439 _info: &VisitInfo,
440 ) -> VisitFlow {
441 self.enter_directive += 1;
442 VisitFlow::Skip
443 }
444 fn leave_directive(
445 &mut self,
446 _: &mut (),
447 _field: &Directive,
448 _info: &VisitInfo,
449 ) -> VisitFlow {
450 self.leave_directive += 1;
451 VisitFlow::Skip
452 }
453 }
454
455 #[test]
456 fn kitchen_sink() {
457 let ctx = ASTContext::new();
458 let query = include_str!("../../fixture/kitchen_sink.graphql");
459 let ast = Document::parse(&ctx, query).unwrap();
460 let mut visitor = ComposedVisitor::new(CountVisitor::default(), SkipVisitor::default());
461 ast.visit(&mut (), &mut visitor);
462
463 assert_eq!(
464 visitor.a,
465 CountVisitor {
466 in_document: 1,
467 out_document: 1,
468 in_operation: 5,
469 out_operation: 5,
470 in_fragment: 1,
471 out_fragment: 1,
472 in_variable_definition: 3,
473 out_variable_definition: 3,
474 in_selection_set: 30,
475 out_selection_set: 30,
476 in_fragment_spread: 1,
477 out_fragment_spread: 1,
478 in_inline_fragment: 3,
479 out_inline_fragment: 3,
480 in_field: 21,
481 out_field: 21,
482 in_directive: 10,
483 out_directive: 10,
484 in_argument: 13,
485 out_argument: 13,
486 }
487 );
488
489 assert_eq!(
490 visitor.b,
491 SkipVisitor {
492 enter: 6,
493 leave: 6,
494 enter_field: 7,
495 leave_field: 0,
496 enter_directive: 4,
497 leave_directive: 0,
498 }
499 );
500 }
501}