1use std::collections::HashMap;
2
3use crate::parser::query::TypeCondition;
4
5use crate::static_graphql::{
6 query::{self, *},
7 schema::{self},
8};
9
10pub struct OperationVisitorContext<'a> {
12 pub schema: &'a schema::Document,
13 pub operation: &'a query::Document,
14 pub known_fragments: HashMap<&'a str, &'a FragmentDefinition>,
15 pub directives: HashMap<String, schema::DirectiveDefinition>,
16
17 type_stack: Vec<Option<&'a schema::TypeDefinition>>,
18 parent_type_stack: Vec<Option<&'a schema::TypeDefinition>>,
19 input_type_stack: Vec<Option<&'a schema::TypeDefinition>>,
20 type_literal_stack: Vec<Option<Type>>,
21 input_type_literal_stack: Vec<Option<&'a Type>>,
22 input_type_default_stack: Vec<bool>,
23 field_stack: Vec<Option<&'a schema::Field>>,
24}
25
26impl<'a> OperationVisitorContext<'a> {
27 pub fn new(operation: &'a Document, schema: &'a schema::Document) -> Self {
28 OperationVisitorContext {
29 schema,
30 operation,
31 type_stack: vec![],
32 parent_type_stack: vec![],
33 input_type_stack: vec![],
34 type_literal_stack: vec![],
35 input_type_literal_stack: vec![],
36 input_type_default_stack: vec![],
37 field_stack: vec![],
38 known_fragments: HashMap::from_iter(operation.definitions.iter().filter_map(|def| {
39 match def {
40 Definition::Fragment(fragment) => Some((fragment.name.as_str(), fragment)),
41 _ => None,
42 }
43 })),
44 directives: HashMap::<String, schema::DirectiveDefinition>::from_iter(
45 schema.definitions.iter().filter_map(|def| match def {
46 schema::Definition::DirectiveDefinition(directive_def) => {
47 Some((directive_def.name.clone(), directive_def.clone()))
48 }
49 _ => None,
50 }),
51 ),
52 }
53 }
54
55 pub fn with_type<Func>(&mut self, t: Option<&Type>, func: Func)
56 where
57 Func: FnOnce(&mut OperationVisitorContext<'a>),
58 {
59 if let Some(t) = t {
60 self.type_stack
61 .push(self.schema.type_by_name(t.inner_type()));
62 } else {
63 self.type_stack.push(None);
64 }
65
66 self.type_literal_stack.push(t.cloned());
67 func(self);
68 self.type_literal_stack.pop();
69 self.type_stack.pop();
70 }
71
72 pub fn with_parent_type<Func>(&mut self, func: Func)
73 where
74 Func: FnOnce(&mut OperationVisitorContext<'a>),
75 {
76 self.parent_type_stack
77 .push(*self.type_stack.last().unwrap_or(&None));
78 func(self);
79 self.parent_type_stack.pop();
80 }
81
82 pub fn with_field<'f, Func>(&mut self, f: Option<&'f schema::Field>, func: Func)
83 where
84 Func: FnOnce(&mut OperationVisitorContext<'a>),
85 'f: 'a,
86 {
87 if let Some(f) = f {
88 self.field_stack.push(Some(f));
89 } else {
90 self.field_stack.push(None);
91 }
92
93 func(self);
94 self.field_stack.pop();
95 }
96
97 pub fn with_input_type<Func>(&mut self, t: Option<&'a Type>, func: Func)
98 where
99 Func: FnOnce(&mut OperationVisitorContext<'a>),
100 {
101 self.with_input_type_and_default(t, false, func);
102 }
103
104 pub fn with_input_type_and_default<Func>(
105 &mut self,
106 t: Option<&'a Type>,
107 has_default: bool,
108 func: Func,
109 ) where
110 Func: FnOnce(&mut OperationVisitorContext<'a>),
111 {
112 if let Some(t) = t {
113 self.input_type_stack
114 .push(self.schema.type_by_name(t.inner_type()));
115 } else {
116 self.input_type_stack.push(None);
117 }
118
119 self.input_type_literal_stack.push(t);
120 self.input_type_default_stack.push(has_default);
121 func(self);
122 self.input_type_default_stack.pop();
123 self.input_type_literal_stack.pop();
124 self.input_type_stack.pop();
125 }
126
127 pub fn current_input_type_has_default(&self) -> bool {
128 *self.input_type_default_stack.last().unwrap_or(&false)
129 }
130
131 pub fn current_type(&self) -> Option<&schema::TypeDefinition> {
132 self.type_stack.last().unwrap_or(&None).as_deref()
133 }
134
135 pub fn current_input_type(&self) -> Option<&schema::TypeDefinition> {
136 self.input_type_stack.last().unwrap_or(&None).as_deref()
137 }
138
139 pub fn current_parent_type(&self) -> Option<&'a schema::TypeDefinition> {
140 *self.parent_type_stack.last().unwrap_or(&None)
141 }
142
143 pub fn current_type_literal(&self) -> Option<&Type> {
144 self.type_literal_stack.last().unwrap_or(&None).as_ref()
145 }
146
147 pub fn current_input_type_literal(&self) -> Option<&'a Type> {
148 *self.input_type_literal_stack.last().unwrap_or(&None)
149 }
150
151 pub fn current_field(&self) -> Option<&schema::Field> {
152 self.field_stack.last().unwrap_or(&None).as_deref()
153 }
154}
155
156pub fn visit_document<'a, Visitor, UserContext>(
157 visitor: &mut Visitor,
158 document: &'a Document,
159 context: &mut OperationVisitorContext<'a>,
160 user_context: &mut UserContext,
161) where
162 Visitor: OperationVisitor<'a, UserContext>,
163{
164 visitor.enter_document(context, user_context, document);
165 visit_definitions(visitor, &document.definitions, context, user_context);
166 visitor.leave_document(context, user_context, document);
167}
168
169fn visit_definitions<'a, Visitor, UserContext>(
170 visitor: &mut Visitor,
171 definitions: &'a Vec<Definition>,
172 context: &mut OperationVisitorContext<'a>,
173 user_context: &mut UserContext,
174) where
175 Visitor: OperationVisitor<'a, UserContext>,
176{
177 for definition in definitions {
178 let schema_type_name = match definition {
179 Definition::Fragment(fragment) => {
180 let TypeCondition::On(name) = &fragment.type_condition;
181 Some(name)
182 }
183 Definition::Operation(operation) => match operation {
184 OperationDefinition::Query(_) => Some(&context.schema.query_type().name),
185 OperationDefinition::SelectionSet(_) => Some(&context.schema.query_type().name),
186 OperationDefinition::Mutation(_) => context.schema.mutation_type().map(|t| &t.name),
187 OperationDefinition::Subscription(_) => {
188 context.schema.subscription_type().map(|t| &t.name)
189 }
190 },
191 };
192
193 let schema_type = schema_type_name.map(|v| Type::NamedType(v.clone()));
194 context.with_type(schema_type.as_ref(), |context| match definition {
195 Definition::Fragment(fragment) => {
196 visit_fragment_definition(visitor, fragment, context, user_context)
197 }
198 Definition::Operation(operation) => {
199 visit_operation_definition(visitor, operation, context, user_context)
200 }
201 });
202 }
203}
204
205fn visit_directives<'a, Visitor, UserContext>(
206 visitor: &mut Visitor,
207 directives: &'a [Directive],
208 context: &mut OperationVisitorContext<'a>,
209 user_context: &mut UserContext,
210) where
211 Visitor: OperationVisitor<'a, UserContext>,
212{
213 for directive in directives {
214 let directive_def_args = context
215 .schema
216 .directive_by_name(&directive.name)
217 .map(|def| &def.arguments);
218
219 visitor.enter_directive(context, user_context, directive);
220 visit_arguments(
221 visitor,
222 directive_def_args,
223 &directive.arguments,
224 context,
225 user_context,
226 );
227 visitor.leave_directive(context, user_context, directive);
228 }
229}
230
231fn visit_arguments<'a, Visitor, UserContext>(
232 visitor: &mut Visitor,
233 arguments_definition: Option<&'a Vec<schema::InputValue>>,
234 arguments: &'a Vec<(String, Value)>,
235 context: &mut OperationVisitorContext<'a>,
236 user_context: &mut UserContext,
237) where
238 Visitor: OperationVisitor<'a, UserContext>,
239{
240 for argument in arguments {
241 let arg_def = arguments_definition
242 .and_then(|argument_defs| argument_defs.iter().find(|a| a.name.eq(&argument.0)));
243
244 let arg_type = arg_def.map(|a| &a.value_type);
245 let has_default = arg_def.and_then(|a| a.default_value.as_ref()).is_some();
246
247 context.with_input_type_and_default(arg_type, has_default, |context| {
248 visitor.enter_argument(context, user_context, argument);
249 visit_input_value(visitor, &argument.1, context, user_context);
250 visitor.leave_argument(context, user_context, argument);
251 })
252 }
253}
254
255fn visit_input_value<'a, Visitor, UserContext>(
256 visitor: &mut Visitor,
257 input_value: &'a Value,
258 context: &mut OperationVisitorContext<'a>,
259 user_context: &mut UserContext,
260) where
261 Visitor: OperationVisitor<'a, UserContext>,
262{
263 match input_value {
264 Value::Boolean(_) | Value::Float(_) | Value::Int(_) | Value::String(_) => {
265 visitor.enter_scalar_value(context, user_context, input_value);
266 visitor.leave_scalar_value(context, user_context, input_value);
267 }
268 Value::Null => {
269 visitor.enter_null_value(context, user_context, ());
270 visitor.leave_null_value(context, user_context, ());
271 }
272 Value::Enum(v) => {
273 visitor.enter_enum_value(context, user_context, v);
274 visitor.leave_enum_value(context, user_context, v);
275 }
276 Value::List(v) => {
277 visitor.enter_list_value(context, user_context, v);
278
279 let input_type = context.current_input_type_literal().and_then(|t| match t {
280 Type::ListType(inner_type) => Some(inner_type.as_ref()),
281 _ => None,
282 });
283
284 context.with_input_type(input_type, |context| {
285 for item in v {
286 visit_input_value(visitor, item, context, user_context)
287 }
288 });
289
290 visitor.leave_list_value(context, user_context, v);
291 }
292 Value::Object(v) => {
293 visitor.enter_object_value(context, user_context, v);
294
295 for pair in v {
296 let input_field = context
297 .current_input_type_literal()
298 .and_then(|v| context.schema.type_by_name(v.inner_type()))
299 .and_then(|v| v.input_field_by_name(&pair.0));
300 let input_type = input_field.map(|field| &field.value_type);
301 let has_default = input_field
302 .and_then(|field| field.default_value.as_ref())
303 .is_some();
304
305 context.with_input_type_and_default(input_type, has_default, |context| {
306 visitor.enter_object_field(context, user_context, pair);
307 visit_input_value(visitor, &pair.1, context, user_context);
308 visitor.leave_object_field(context, user_context, pair);
309 });
310 }
311
312 visitor.leave_object_value(context, user_context, v);
313 }
314 Value::Variable(v) => {
315 visitor.enter_variable_value(context, user_context, v);
316 visitor.leave_variable_value(context, user_context, v);
317 }
318 }
319}
320
321fn visit_variable_definitions<'a, Visitor, UserContext>(
322 visitor: &mut Visitor,
323 variables: &'a [VariableDefinition],
324 context: &mut OperationVisitorContext<'a>,
325 user_context: &mut UserContext,
326) where
327 Visitor: OperationVisitor<'a, UserContext>,
328{
329 for variable in variables {
330 context.with_input_type(Some(&variable.var_type), |context| {
331 visitor.enter_variable_definition(context, user_context, variable);
332
333 if let Some(default_value) = &variable.default_value {
334 visit_input_value(visitor, default_value, context, user_context);
335 }
336
337 visitor.leave_variable_definition(context, user_context, variable);
340 })
341 }
342}
343
344fn visit_selection<'a, Visitor, UserContext>(
345 visitor: &mut Visitor,
346 selection: &'a Selection,
347 context: &mut OperationVisitorContext<'a>,
348 user_context: &mut UserContext,
349) where
350 Visitor: OperationVisitor<'a, UserContext>,
351{
352 match selection {
353 Selection::Field(field) => {
354 let parent_type_def = context
355 .current_parent_type()
356 .and_then(|t| t.field_by_name(&field.name));
357
358 let field_type = parent_type_def.map(|f| &f.field_type);
359 let field_args = parent_type_def.map(|f| &f.arguments);
360
361 context.with_type(field_type, |context| {
362 visitor.enter_field(context, user_context, field);
363 context.with_field(
364 context
365 .current_parent_type()
366 .and_then(|t| t.field_by_name(&field.name)),
367 |context| {
368 visit_arguments(
369 visitor,
370 field_args,
371 &field.arguments,
372 context,
373 user_context,
374 );
375 visit_directives(visitor, &field.directives, context, user_context);
376 visit_selection_set(visitor, &field.selection_set, context, user_context);
377 },
378 );
379 visitor.leave_field(context, user_context, field);
380 });
381 }
382 Selection::FragmentSpread(fragment_spread) => {
383 visitor.enter_fragment_spread(context, user_context, fragment_spread);
384 visit_directives(visitor, &fragment_spread.directives, context, user_context);
385 visitor.leave_fragment_spread(context, user_context, fragment_spread);
386 }
387 Selection::InlineFragment(inline_fragment) => {
388 if let Some(TypeCondition::On(fragment_condition)) = &inline_fragment.type_condition {
389 context.with_type(
390 Some(&Type::NamedType(fragment_condition.clone())),
391 |context| {
392 visitor.enter_inline_fragment(context, user_context, inline_fragment);
393 visit_directives(
394 visitor,
395 &inline_fragment.directives,
396 context,
397 user_context,
398 );
399 visit_selection_set(
400 visitor,
401 &inline_fragment.selection_set,
402 context,
403 user_context,
404 );
405 visitor.leave_inline_fragment(context, user_context, inline_fragment);
406 },
407 );
408 } else {
409 visitor.enter_inline_fragment(context, user_context, inline_fragment);
410 visit_directives(visitor, &inline_fragment.directives, context, user_context);
411 visit_selection_set(
412 visitor,
413 &inline_fragment.selection_set,
414 context,
415 user_context,
416 );
417 visitor.leave_inline_fragment(context, user_context, inline_fragment);
418 }
419 }
420 }
421}
422
423fn visit_selection_set<'a, Visitor, UserContext>(
424 visitor: &mut Visitor,
425 selection_set: &'a SelectionSet,
426 context: &mut OperationVisitorContext<'a>,
427 user_context: &mut UserContext,
428) where
429 Visitor: OperationVisitor<'a, UserContext>,
430{
431 context.with_parent_type(|context| {
432 visitor.enter_selection_set(context, user_context, selection_set);
433
434 for selection in &selection_set.items {
435 visit_selection(visitor, selection, context, user_context);
436 }
437
438 visitor.leave_selection_set(context, user_context, selection_set);
439 });
440}
441
442fn visit_fragment_definition<'a, Visitor, UserContext>(
443 visitor: &mut Visitor,
444 fragment: &'a FragmentDefinition,
445 context: &mut OperationVisitorContext<'a>,
446 user_context: &mut UserContext,
447) where
448 Visitor: OperationVisitor<'a, UserContext>,
449{
450 visitor.enter_fragment_definition(context, user_context, fragment);
451 visit_directives(visitor, &fragment.directives, context, user_context);
452 visit_selection_set(visitor, &fragment.selection_set, context, user_context);
453 visitor.leave_fragment_definition(context, user_context, fragment);
454}
455
456fn visit_operation_definition<'a, Visitor, UserContext>(
457 visitor: &mut Visitor,
458 operation: &'a OperationDefinition,
459 context: &mut OperationVisitorContext<'a>,
460 user_context: &mut UserContext,
461) where
462 Visitor: OperationVisitor<'a, UserContext>,
463{
464 visitor.enter_operation_definition(context, user_context, operation);
465 visit_directives(visitor, operation.directives(), context, user_context);
466 visit_variable_definitions(
467 visitor,
468 operation.variable_definitions(),
469 context,
470 user_context,
471 );
472 visit_selection_set(visitor, operation.selection_set(), context, user_context);
473 visitor.leave_operation_definition(context, user_context, operation);
474}
475
476pub trait OperationVisitor<'a, UserContext = ()> {
478 fn enter_document(
479 &mut self,
480 _: &mut OperationVisitorContext<'a>,
481 _: &mut UserContext,
482 _: &'a Document,
483 ) {
484 }
485 fn leave_document(
486 &mut self,
487 _: &mut OperationVisitorContext<'a>,
488 _: &mut UserContext,
489 _: &Document,
490 ) {
491 }
492
493 fn enter_operation_definition(
494 &mut self,
495 _: &mut OperationVisitorContext<'a>,
496 _: &mut UserContext,
497 _: &'a OperationDefinition,
498 ) {
499 }
500 fn leave_operation_definition(
501 &mut self,
502 _: &mut OperationVisitorContext<'a>,
503 _: &mut UserContext,
504 _: &OperationDefinition,
505 ) {
506 }
507
508 fn enter_fragment_definition(
509 &mut self,
510 _: &mut OperationVisitorContext<'a>,
511 _: &mut UserContext,
512 _: &'a FragmentDefinition,
513 ) {
514 }
515 fn leave_fragment_definition(
516 &mut self,
517 _: &mut OperationVisitorContext<'a>,
518 _: &mut UserContext,
519 _: &FragmentDefinition,
520 ) {
521 }
522
523 fn enter_variable_definition(
524 &mut self,
525 _: &mut OperationVisitorContext<'a>,
526 _: &mut UserContext,
527 _: &'a VariableDefinition,
528 ) {
529 }
530 fn leave_variable_definition(
531 &mut self,
532 _: &mut OperationVisitorContext<'a>,
533 _: &mut UserContext,
534 _: &VariableDefinition,
535 ) {
536 }
537
538 fn enter_directive(
539 &mut self,
540 _: &mut OperationVisitorContext<'a>,
541 _: &mut UserContext,
542 _: &Directive,
543 ) {
544 }
545 fn leave_directive(
546 &mut self,
547 _: &mut OperationVisitorContext<'a>,
548 _: &mut UserContext,
549 _: &Directive,
550 ) {
551 }
552
553 fn enter_argument(
554 &mut self,
555 _: &mut OperationVisitorContext<'a>,
556 _: &mut UserContext,
557 _: &'a (String, Value),
558 ) {
559 }
560 fn leave_argument(
561 &mut self,
562 _: &mut OperationVisitorContext<'a>,
563 _: &mut UserContext,
564 _: &(String, Value),
565 ) {
566 }
567
568 fn enter_selection_set(
569 &mut self,
570 _: &mut OperationVisitorContext<'a>,
571 _: &mut UserContext,
572 _: &'a SelectionSet,
573 ) {
574 }
575 fn leave_selection_set(
576 &mut self,
577 _: &mut OperationVisitorContext<'a>,
578 _: &mut UserContext,
579 _: &SelectionSet,
580 ) {
581 }
582
583 fn enter_field(&mut self, _: &mut OperationVisitorContext<'a>, _: &mut UserContext, _: &Field) {
584 }
585 fn leave_field(&mut self, _: &mut OperationVisitorContext<'a>, _: &mut UserContext, _: &Field) {
586 }
587
588 fn enter_fragment_spread(
589 &mut self,
590 _: &mut OperationVisitorContext<'a>,
591 _: &mut UserContext,
592 _: &'a FragmentSpread,
593 ) {
594 }
595 fn leave_fragment_spread(
596 &mut self,
597 _: &mut OperationVisitorContext<'a>,
598 _: &mut UserContext,
599 _: &FragmentSpread,
600 ) {
601 }
602
603 fn enter_inline_fragment(
604 &mut self,
605 _: &mut OperationVisitorContext<'a>,
606 _: &mut UserContext,
607 _: &InlineFragment,
608 ) {
609 }
610 fn leave_inline_fragment(
611 &mut self,
612 _: &mut OperationVisitorContext<'a>,
613 _: &mut UserContext,
614 _: &InlineFragment,
615 ) {
616 }
617
618 fn enter_null_value(
619 &mut self,
620 _: &mut OperationVisitorContext<'a>,
621 _: &mut UserContext,
622 _: (),
623 ) {
624 }
625 fn leave_null_value(
626 &mut self,
627 _: &mut OperationVisitorContext<'a>,
628 _: &mut UserContext,
629 _: (),
630 ) {
631 }
632
633 fn enter_scalar_value(
634 &mut self,
635 _: &mut OperationVisitorContext<'a>,
636 _: &mut UserContext,
637 _: &Value,
638 ) {
639 }
640 fn leave_scalar_value(
641 &mut self,
642 _: &mut OperationVisitorContext<'a>,
643 _: &mut UserContext,
644 _: &Value,
645 ) {
646 }
647
648 fn enter_enum_value(
649 &mut self,
650 _: &mut OperationVisitorContext<'a>,
651 _: &mut UserContext,
652 _: &String,
653 ) {
654 }
655 fn leave_enum_value(
656 &mut self,
657 _: &mut OperationVisitorContext<'a>,
658 _: &mut UserContext,
659 _: &String,
660 ) {
661 }
662
663 fn enter_variable_value(
664 &mut self,
665 _: &mut OperationVisitorContext<'a>,
666 _: &mut UserContext,
667 _: &'a str,
668 ) {
669 }
670 fn leave_variable_value(
671 &mut self,
672 _: &mut OperationVisitorContext<'a>,
673 _: &mut UserContext,
674 _: &String,
675 ) {
676 }
677
678 fn enter_list_value(
679 &mut self,
680 _: &mut OperationVisitorContext<'a>,
681 _: &mut UserContext,
682 _: &Vec<Value>,
683 ) {
684 }
685 fn leave_list_value(
686 &mut self,
687 _: &mut OperationVisitorContext<'a>,
688 _: &mut UserContext,
689 _: &Vec<Value>,
690 ) {
691 }
692
693 fn enter_object_value(
694 &mut self,
695 _: &mut OperationVisitorContext<'a>,
696 _: &mut UserContext,
697 _: &[(String, Value)],
698 ) {
699 }
700 fn leave_object_value(
701 &mut self,
702 _: &mut OperationVisitorContext<'a>,
703 _: &mut UserContext,
704 _: &[(String, Value)],
705 ) {
706 }
707
708 fn enter_object_field(
709 &mut self,
710 _: &mut OperationVisitorContext<'a>,
711 _: &mut UserContext,
712 _: &(String, Value),
713 ) {
714 }
715 fn leave_object_field(
716 &mut self,
717 _: &mut OperationVisitorContext<'a>,
718 _: &mut UserContext,
719 _: &(String, Value),
720 ) {
721 }
722}
723
724#[cfg(test)]
725mod tests {
726 use super::*;
727
728 struct RecordingVisitor {
729 seen_types: Vec<Option<String>>,
730 }
731
732 impl<'a> OperationVisitor<'a, ()> for RecordingVisitor {
733 fn enter_operation_definition(
734 &mut self,
735 context: &mut OperationVisitorContext<'a>,
736 _user_context: &mut (),
737 _operation: &'a OperationDefinition,
738 ) {
739 self.seen_types.push(
740 context
741 .current_type()
742 .map(|type_def| type_def.name().to_string()),
743 );
744 }
745 }
746
747 fn assert_seen_type(schema_sdl: &str, operation: &str, expected: &str) {
748 let schema = crate::parser::parse_schema::<String>(schema_sdl)
749 .expect("valid schema")
750 .into_static();
751 let operation_doc = crate::parser::parse_query::<String>(operation)
752 .expect("valid operation")
753 .into_static();
754
755 let mut context = OperationVisitorContext::new(&operation_doc, &schema);
756 let mut visitor = RecordingVisitor {
757 seen_types: Vec::new(),
758 };
759
760 visit_document(&mut visitor, &operation_doc, &mut context, &mut ());
761
762 assert_eq!(visitor.seen_types.len(), 1);
763 assert_eq!(visitor.seen_types[0], Some(expected.to_string()));
764 }
765
766 #[test]
767 fn default_root_type_names() {
768 let schema = "
769 type Query { hello: String }
770 type Mutation { doThing: String }
771 type Subscription { onThing: String }
772 ";
773
774 assert_seen_type(schema, "mutation { doThing }", "Mutation");
775 assert_seen_type(schema, "subscription { onThing }", "Subscription");
776 }
777
778 #[test]
779 fn renamed_root_types() {
780 let schema = "
781 schema {
782 query: RootQuery
783 mutation: RootMutation
784 subscription: RootSubscription
785 }
786
787 type RootQuery { hello: String }
788 type RootMutation { doThing: String }
789 type RootSubscription { onThing: String }
790 ";
791
792 assert_seen_type(schema, "mutation { doThing }", "RootMutation");
793 assert_seen_type(schema, "subscription { onThing }", "RootSubscription");
794 }
795}