1use std::{
2 collections::HashMap,
3 fmt::{self, Display, Formatter},
4};
5
6use async_graphql_value::Value;
7
8use crate::{
9 InputType, Name, Pos, Positioned, ServerError, ServerResult, Variables,
10 parser::types::{
11 Directive, ExecutableDocument, Field, FragmentDefinition, FragmentSpread, InlineFragment,
12 OperationDefinition, OperationType, Selection, SelectionSet, TypeCondition,
13 VariableDefinition,
14 },
15 registry::{self, MetaType, MetaTypeName},
16};
17
18#[doc(hidden)]
19pub struct VisitorContext<'a> {
20 pub(crate) registry: &'a registry::Registry,
21 pub(crate) variables: Option<&'a Variables>,
22 pub(crate) operation_name: Option<&'a str>,
23 pub(crate) errors: Vec<RuleError>,
24 type_stack: Vec<Option<&'a registry::MetaType>>,
25 input_type: Vec<Option<MetaTypeName<'a>>>,
26 fragments: &'a HashMap<Name, Positioned<FragmentDefinition>>,
27}
28
29impl<'a> VisitorContext<'a> {
30 pub(crate) fn new(
31 registry: &'a registry::Registry,
32 doc: &'a ExecutableDocument,
33 variables: Option<&'a Variables>,
34 operation_name: Option<&'a str>,
35 ) -> Self {
36 Self {
37 registry,
38 variables,
39 operation_name,
40 errors: Default::default(),
41 type_stack: Default::default(),
42 input_type: Default::default(),
43 fragments: &doc.fragments,
44 }
45 }
46
47 pub(crate) fn report_error<T: Into<String>>(&mut self, locations: Vec<Pos>, msg: T) {
48 self.errors.push(RuleError::new(locations, msg));
49 }
50
51 pub(crate) fn append_errors(&mut self, errors: Vec<RuleError>) {
52 self.errors.extend(errors);
53 }
54
55 pub(crate) fn with_type<F: FnMut(&mut VisitorContext<'a>)>(
56 &mut self,
57 ty: Option<&'a registry::MetaType>,
58 mut f: F,
59 ) {
60 self.type_stack.push(ty);
61 f(self);
62 self.type_stack.pop();
63 }
64
65 pub(crate) fn with_input_type<F: FnMut(&mut VisitorContext<'a>)>(
66 &mut self,
67 ty: Option<MetaTypeName<'a>>,
68 mut f: F,
69 ) {
70 self.input_type.push(ty);
71 f(self);
72 self.input_type.pop();
73 }
74
75 pub(crate) fn parent_type(&self) -> Option<&'a registry::MetaType> {
76 if self.type_stack.len() >= 2 {
77 self.type_stack
78 .get(self.type_stack.len() - 2)
79 .copied()
80 .flatten()
81 } else {
82 None
83 }
84 }
85
86 pub(crate) fn current_type(&self) -> Option<&'a registry::MetaType> {
87 self.type_stack.last().copied().flatten()
88 }
89
90 pub(crate) fn is_known_fragment(&self, name: &str) -> bool {
91 self.fragments.contains_key(name)
92 }
93
94 pub(crate) fn fragment(&self, name: &str) -> Option<&'a Positioned<FragmentDefinition>> {
95 self.fragments.get(name)
96 }
97
98 #[doc(hidden)]
99 pub fn param_value<T: InputType>(
100 &self,
101 variable_definitions: &[Positioned<VariableDefinition>],
102 field: &Field,
103 name: &str,
104 default: Option<fn() -> T>,
105 ) -> ServerResult<T> {
106 let value = field.get_argument(name).cloned();
107
108 if value.is_none()
109 && let Some(default) = default
110 {
111 return Ok(default());
112 }
113
114 let (pos, value) = match value {
115 Some(value) => {
116 let pos = value.pos;
117 (
118 pos,
119 Some(value.node.into_const_with(|name| {
120 variable_definitions
121 .iter()
122 .find(|def| def.node.name.node == name)
123 .and_then(|def| {
124 if let Some(variables) = self.variables {
125 variables
126 .get(&def.node.name.node)
127 .or_else(|| def.node.default_value())
128 } else {
129 None
130 }
131 })
132 .cloned()
133 .ok_or_else(|| {
134 ServerError::new(
135 format!("Variable {} is not defined.", name),
136 Some(pos),
137 )
138 })
139 })?),
140 )
141 }
142 None => (Pos::default(), None),
143 };
144
145 T::parse(value).map_err(|e| e.into_server_error(pos))
146 }
147}
148
149#[derive(Copy, Clone, Eq, PartialEq)]
150pub(crate) enum VisitMode {
151 Normal,
152 Inline,
153}
154
155pub(crate) trait Visitor<'a> {
156 fn mode(&self) -> VisitMode {
157 VisitMode::Normal
158 }
159
160 fn enter_document(&mut self, _ctx: &mut VisitorContext<'a>, _doc: &'a ExecutableDocument) {}
161 fn exit_document(&mut self, _ctx: &mut VisitorContext<'a>, _doc: &'a ExecutableDocument) {}
162
163 fn enter_operation_definition(
164 &mut self,
165 _ctx: &mut VisitorContext<'a>,
166 _name: Option<&'a Name>,
167 _operation_definition: &'a Positioned<OperationDefinition>,
168 ) {
169 }
170 fn exit_operation_definition(
171 &mut self,
172 _ctx: &mut VisitorContext<'a>,
173 _name: Option<&'a Name>,
174 _operation_definition: &'a Positioned<OperationDefinition>,
175 ) {
176 }
177
178 fn enter_fragment_definition(
179 &mut self,
180 _ctx: &mut VisitorContext<'a>,
181 _name: &'a Name,
182 _fragment_definition: &'a Positioned<FragmentDefinition>,
183 ) {
184 }
185 fn exit_fragment_definition(
186 &mut self,
187 _ctx: &mut VisitorContext<'a>,
188 _name: &'a Name,
189 _fragment_definition: &'a Positioned<FragmentDefinition>,
190 ) {
191 }
192
193 fn enter_variable_definition(
194 &mut self,
195 _ctx: &mut VisitorContext<'a>,
196 _variable_definition: &'a Positioned<VariableDefinition>,
197 ) {
198 }
199 fn exit_variable_definition(
200 &mut self,
201 _ctx: &mut VisitorContext<'a>,
202 _variable_definition: &'a Positioned<VariableDefinition>,
203 ) {
204 }
205
206 fn enter_directive(
207 &mut self,
208 _ctx: &mut VisitorContext<'a>,
209 _directive: &'a Positioned<Directive>,
210 ) {
211 }
212 fn exit_directive(
213 &mut self,
214 _ctx: &mut VisitorContext<'a>,
215 _directive: &'a Positioned<Directive>,
216 ) {
217 }
218
219 fn enter_argument(
220 &mut self,
221 _ctx: &mut VisitorContext<'a>,
222 _name: &'a Positioned<Name>,
223 _value: &'a Positioned<Value>,
224 ) {
225 }
226 fn exit_argument(
227 &mut self,
228 _ctx: &mut VisitorContext<'a>,
229 _name: &'a Positioned<Name>,
230 _value: &'a Positioned<Value>,
231 ) {
232 }
233
234 fn enter_selection_set(
235 &mut self,
236 _ctx: &mut VisitorContext<'a>,
237 _selection_set: &'a Positioned<SelectionSet>,
238 ) {
239 }
240 fn exit_selection_set(
241 &mut self,
242 _ctx: &mut VisitorContext<'a>,
243 _selection_set: &'a Positioned<SelectionSet>,
244 ) {
245 }
246
247 fn enter_selection(
248 &mut self,
249 _ctx: &mut VisitorContext<'a>,
250 _selection: &'a Positioned<Selection>,
251 ) {
252 }
253 fn exit_selection(
254 &mut self,
255 _ctx: &mut VisitorContext<'a>,
256 _selection: &'a Positioned<Selection>,
257 ) {
258 }
259
260 fn enter_field(&mut self, _ctx: &mut VisitorContext<'a>, _field: &'a Positioned<Field>) {}
261 fn exit_field(&mut self, _ctx: &mut VisitorContext<'a>, _field: &'a Positioned<Field>) {}
262
263 fn enter_fragment_spread(
264 &mut self,
265 _ctx: &mut VisitorContext<'a>,
266 _fragment_spread: &'a Positioned<FragmentSpread>,
267 ) {
268 }
269 fn exit_fragment_spread(
270 &mut self,
271 _ctx: &mut VisitorContext<'a>,
272 _fragment_spread: &'a Positioned<FragmentSpread>,
273 ) {
274 }
275
276 fn enter_inline_fragment(
277 &mut self,
278 _ctx: &mut VisitorContext<'a>,
279 _inline_fragment: &'a Positioned<InlineFragment>,
280 ) {
281 }
282 fn exit_inline_fragment(
283 &mut self,
284 _ctx: &mut VisitorContext<'a>,
285 _inline_fragment: &'a Positioned<InlineFragment>,
286 ) {
287 }
288
289 fn enter_input_value(
290 &mut self,
291 _ctx: &mut VisitorContext<'a>,
292 _pos: Pos,
293 _expected_type: &Option<MetaTypeName<'a>>,
294 _value: &'a Value,
295 ) {
296 }
297 fn exit_input_value(
298 &mut self,
299 _ctx: &mut VisitorContext<'a>,
300 _pos: Pos,
301 _expected_type: &Option<MetaTypeName<'a>>,
302 _value: &Value,
303 ) {
304 }
305}
306
307pub(crate) struct VisitorNil;
308
309impl VisitorNil {
310 pub(crate) fn with<V>(self, visitor: V) -> VisitorCons<V, Self> {
311 VisitorCons(visitor, self)
312 }
313}
314
315pub(crate) struct VisitorCons<A, B>(A, B);
316
317impl<A, B> VisitorCons<A, B> {
318 pub(crate) const fn with<V>(self, visitor: V) -> VisitorCons<V, Self> {
319 VisitorCons(visitor, self)
320 }
321}
322
323impl Visitor<'_> for VisitorNil {}
324
325impl<'a, A, B> Visitor<'a> for VisitorCons<A, B>
326where
327 A: Visitor<'a> + 'a,
328 B: Visitor<'a> + 'a,
329{
330 fn mode(&self) -> VisitMode {
331 self.0.mode()
332 }
333
334 fn enter_document(&mut self, ctx: &mut VisitorContext<'a>, doc: &'a ExecutableDocument) {
335 self.0.enter_document(ctx, doc);
336 self.1.enter_document(ctx, doc);
337 }
338
339 fn exit_document(&mut self, ctx: &mut VisitorContext<'a>, doc: &'a ExecutableDocument) {
340 self.0.exit_document(ctx, doc);
341 self.1.exit_document(ctx, doc);
342 }
343
344 fn enter_operation_definition(
345 &mut self,
346 ctx: &mut VisitorContext<'a>,
347 name: Option<&'a Name>,
348 operation_definition: &'a Positioned<OperationDefinition>,
349 ) {
350 self.0
351 .enter_operation_definition(ctx, name, operation_definition);
352 self.1
353 .enter_operation_definition(ctx, name, operation_definition);
354 }
355
356 fn exit_operation_definition(
357 &mut self,
358 ctx: &mut VisitorContext<'a>,
359 name: Option<&'a Name>,
360 operation_definition: &'a Positioned<OperationDefinition>,
361 ) {
362 self.0
363 .exit_operation_definition(ctx, name, operation_definition);
364 self.1
365 .exit_operation_definition(ctx, name, operation_definition);
366 }
367
368 fn enter_fragment_definition(
369 &mut self,
370 ctx: &mut VisitorContext<'a>,
371 name: &'a Name,
372 fragment_definition: &'a Positioned<FragmentDefinition>,
373 ) {
374 self.0
375 .enter_fragment_definition(ctx, name, fragment_definition);
376 self.1
377 .enter_fragment_definition(ctx, name, fragment_definition);
378 }
379
380 fn exit_fragment_definition(
381 &mut self,
382 ctx: &mut VisitorContext<'a>,
383 name: &'a Name,
384 fragment_definition: &'a Positioned<FragmentDefinition>,
385 ) {
386 self.0
387 .exit_fragment_definition(ctx, name, fragment_definition);
388 self.1
389 .exit_fragment_definition(ctx, name, fragment_definition);
390 }
391
392 fn enter_variable_definition(
393 &mut self,
394 ctx: &mut VisitorContext<'a>,
395 variable_definition: &'a Positioned<VariableDefinition>,
396 ) {
397 self.0.enter_variable_definition(ctx, variable_definition);
398 self.1.enter_variable_definition(ctx, variable_definition);
399 }
400
401 fn exit_variable_definition(
402 &mut self,
403 ctx: &mut VisitorContext<'a>,
404 variable_definition: &'a Positioned<VariableDefinition>,
405 ) {
406 self.0.exit_variable_definition(ctx, variable_definition);
407 self.1.exit_variable_definition(ctx, variable_definition);
408 }
409
410 fn enter_directive(
411 &mut self,
412 ctx: &mut VisitorContext<'a>,
413 directive: &'a Positioned<Directive>,
414 ) {
415 self.0.enter_directive(ctx, directive);
416 self.1.enter_directive(ctx, directive);
417 }
418
419 fn exit_directive(
420 &mut self,
421 ctx: &mut VisitorContext<'a>,
422 directive: &'a Positioned<Directive>,
423 ) {
424 self.0.exit_directive(ctx, directive);
425 self.1.exit_directive(ctx, directive);
426 }
427
428 fn enter_argument(
429 &mut self,
430 ctx: &mut VisitorContext<'a>,
431 name: &'a Positioned<Name>,
432 value: &'a Positioned<Value>,
433 ) {
434 self.0.enter_argument(ctx, name, value);
435 self.1.enter_argument(ctx, name, value);
436 }
437
438 fn exit_argument(
439 &mut self,
440 ctx: &mut VisitorContext<'a>,
441 name: &'a Positioned<Name>,
442 value: &'a Positioned<Value>,
443 ) {
444 self.0.exit_argument(ctx, name, value);
445 self.1.exit_argument(ctx, name, value);
446 }
447
448 fn enter_selection_set(
449 &mut self,
450 ctx: &mut VisitorContext<'a>,
451 selection_set: &'a Positioned<SelectionSet>,
452 ) {
453 self.0.enter_selection_set(ctx, selection_set);
454 self.1.enter_selection_set(ctx, selection_set);
455 }
456
457 fn exit_selection_set(
458 &mut self,
459 ctx: &mut VisitorContext<'a>,
460 selection_set: &'a Positioned<SelectionSet>,
461 ) {
462 self.0.exit_selection_set(ctx, selection_set);
463 self.1.exit_selection_set(ctx, selection_set);
464 }
465
466 fn enter_selection(
467 &mut self,
468 ctx: &mut VisitorContext<'a>,
469 selection: &'a Positioned<Selection>,
470 ) {
471 self.0.enter_selection(ctx, selection);
472 self.1.enter_selection(ctx, selection);
473 }
474
475 fn exit_selection(
476 &mut self,
477 ctx: &mut VisitorContext<'a>,
478 selection: &'a Positioned<Selection>,
479 ) {
480 self.0.exit_selection(ctx, selection);
481 self.1.exit_selection(ctx, selection);
482 }
483
484 fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Positioned<Field>) {
485 self.0.enter_field(ctx, field);
486 self.1.enter_field(ctx, field);
487 }
488
489 fn exit_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Positioned<Field>) {
490 self.0.exit_field(ctx, field);
491 self.1.exit_field(ctx, field);
492 }
493
494 fn enter_fragment_spread(
495 &mut self,
496 ctx: &mut VisitorContext<'a>,
497 fragment_spread: &'a Positioned<FragmentSpread>,
498 ) {
499 self.0.enter_fragment_spread(ctx, fragment_spread);
500 self.1.enter_fragment_spread(ctx, fragment_spread);
501 }
502
503 fn exit_fragment_spread(
504 &mut self,
505 ctx: &mut VisitorContext<'a>,
506 fragment_spread: &'a Positioned<FragmentSpread>,
507 ) {
508 self.0.exit_fragment_spread(ctx, fragment_spread);
509 self.1.exit_fragment_spread(ctx, fragment_spread);
510 }
511
512 fn enter_inline_fragment(
513 &mut self,
514 ctx: &mut VisitorContext<'a>,
515 inline_fragment: &'a Positioned<InlineFragment>,
516 ) {
517 self.0.enter_inline_fragment(ctx, inline_fragment);
518 self.1.enter_inline_fragment(ctx, inline_fragment);
519 }
520
521 fn exit_inline_fragment(
522 &mut self,
523 ctx: &mut VisitorContext<'a>,
524 inline_fragment: &'a Positioned<InlineFragment>,
525 ) {
526 self.0.exit_inline_fragment(ctx, inline_fragment);
527 self.1.exit_inline_fragment(ctx, inline_fragment);
528 }
529}
530
531pub(crate) fn visit<'a, V: Visitor<'a>>(
532 v: &mut V,
533 ctx: &mut VisitorContext<'a>,
534 doc: &'a ExecutableDocument,
535) {
536 v.enter_document(ctx, doc);
537
538 for (name, fragment) in &doc.fragments {
539 ctx.with_type(
540 ctx.registry
541 .types
542 .get(fragment.node.type_condition.node.on.node.as_str()),
543 |ctx| visit_fragment_definition(v, ctx, name, fragment),
544 )
545 }
546
547 for (name, operation) in doc.operations.iter() {
548 visit_operation_definition(v, ctx, name, operation);
549 }
550
551 v.exit_document(ctx, doc);
552}
553
554fn visit_operation_definition<'a, V: Visitor<'a>>(
555 v: &mut V,
556 ctx: &mut VisitorContext<'a>,
557 name: Option<&'a Name>,
558 operation: &'a Positioned<OperationDefinition>,
559) {
560 v.enter_operation_definition(ctx, name, operation);
561 let root_name = match &operation.node.ty {
562 OperationType::Query => Some(&*ctx.registry.query_type),
563 OperationType::Mutation => ctx.registry.mutation_type.as_deref(),
564 OperationType::Subscription => ctx.registry.subscription_type.as_deref(),
565 };
566 if let Some(root_name) = root_name {
567 ctx.with_type(Some(&ctx.registry.types[root_name]), |ctx| {
568 visit_variable_definitions(v, ctx, &operation.node.variable_definitions);
569 visit_directives(v, ctx, &operation.node.directives);
570 visit_selection_set(v, ctx, &operation.node.selection_set);
571 });
572 } else {
573 ctx.report_error(
574 vec![operation.pos],
575 format!("Schema is not configured for {}s.", operation.node.ty),
577 );
578 }
579 v.exit_operation_definition(ctx, name, operation);
580}
581
582fn visit_selection_set<'a, V: Visitor<'a>>(
583 v: &mut V,
584 ctx: &mut VisitorContext<'a>,
585 selection_set: &'a Positioned<SelectionSet>,
586) {
587 if !selection_set.node.items.is_empty() {
588 v.enter_selection_set(ctx, selection_set);
589 for selection in &selection_set.node.items {
590 visit_selection(v, ctx, selection);
591 }
592 v.exit_selection_set(ctx, selection_set);
593 }
594}
595
596fn visit_selection<'a, V: Visitor<'a>>(
597 v: &mut V,
598 ctx: &mut VisitorContext<'a>,
599 selection: &'a Positioned<Selection>,
600) {
601 v.enter_selection(ctx, selection);
602 match &selection.node {
603 Selection::Field(field) => {
604 if field.node.name.node != "__typename" {
605 ctx.with_type(
606 ctx.current_type()
607 .and_then(|ty| ty.field_by_name(&field.node.name.node))
608 .and_then(|schema_field| {
609 ctx.registry.concrete_type_by_name(&schema_field.ty)
610 }),
611 |ctx| {
612 visit_field(v, ctx, field);
613 },
614 );
615 } else if ctx.current_type().map(|ty| match ty {
616 MetaType::Object {
617 is_subscription, ..
618 } => *is_subscription,
619 _ => false,
620 }) == Some(true)
621 {
622 ctx.report_error(
623 vec![field.pos],
624 "Unknown field \"__typename\" on type \"Subscription\".",
625 );
626 }
627 }
628 Selection::FragmentSpread(fragment_spread) => {
629 visit_fragment_spread(v, ctx, fragment_spread)
630 }
631 Selection::InlineFragment(inline_fragment) => {
632 if let Some(TypeCondition { on: name }) = &inline_fragment
633 .node
634 .type_condition
635 .as_ref()
636 .map(|c| &c.node)
637 {
638 ctx.with_type(ctx.registry.types.get(name.node.as_str()), |ctx| {
639 visit_inline_fragment(v, ctx, inline_fragment)
640 });
641 } else {
642 visit_inline_fragment(v, ctx, inline_fragment)
643 }
644 }
645 }
646 v.exit_selection(ctx, selection);
647}
648
649fn visit_field<'a, V: Visitor<'a>>(
650 v: &mut V,
651 ctx: &mut VisitorContext<'a>,
652 field: &'a Positioned<Field>,
653) {
654 v.enter_field(ctx, field);
655
656 for (name, value) in &field.node.arguments {
657 v.enter_argument(ctx, name, value);
658 let expected_ty = ctx
659 .parent_type()
660 .and_then(|ty| ty.field_by_name(&field.node.name.node))
661 .and_then(|schema_field| schema_field.args.get(&*name.node))
662 .map(|input_ty| MetaTypeName::create(&input_ty.ty));
663 ctx.with_input_type(expected_ty, |ctx| {
664 visit_input_value(v, ctx, field.pos, expected_ty, &value.node)
665 });
666 v.exit_argument(ctx, name, value);
667 }
668
669 visit_directives(v, ctx, &field.node.directives);
670 visit_selection_set(v, ctx, &field.node.selection_set);
671 v.exit_field(ctx, field);
672}
673
674fn visit_input_value<'a, V: Visitor<'a>>(
675 v: &mut V,
676 ctx: &mut VisitorContext<'a>,
677 pos: Pos,
678 expected_ty: Option<MetaTypeName<'a>>,
679 value: &'a Value,
680) {
681 v.enter_input_value(ctx, pos, &expected_ty, value);
682
683 match value {
684 Value::List(values) => {
685 if let Some(expected_ty) = expected_ty {
686 let elem_ty = expected_ty.unwrap_non_null();
687 if let MetaTypeName::List(expected_ty) = elem_ty {
688 values.iter().for_each(|value| {
689 visit_input_value(
690 v,
691 ctx,
692 pos,
693 Some(MetaTypeName::create(expected_ty)),
694 value,
695 )
696 });
697 }
698 }
699 }
700 Value::Object(values) => {
701 if let Some(expected_ty) = expected_ty {
702 let expected_ty = expected_ty.unwrap_non_null();
703 if let MetaTypeName::Named(expected_ty) = expected_ty
704 && let Some(MetaType::InputObject { input_fields, .. }) = ctx
705 .registry
706 .types
707 .get(MetaTypeName::concrete_typename(expected_ty))
708 {
709 for (item_key, item_value) in values {
710 if let Some(input_value) = input_fields.get(item_key.as_str()) {
711 visit_input_value(
712 v,
713 ctx,
714 pos,
715 Some(MetaTypeName::create(&input_value.ty)),
716 item_value,
717 );
718 }
719 }
720 }
721 }
722 }
723 _ => {}
724 }
725
726 v.exit_input_value(ctx, pos, &expected_ty, value);
727}
728
729fn visit_variable_definitions<'a, V: Visitor<'a>>(
730 v: &mut V,
731 ctx: &mut VisitorContext<'a>,
732 variable_definitions: &'a [Positioned<VariableDefinition>],
733) {
734 for d in variable_definitions {
735 v.enter_variable_definition(ctx, d);
736 v.exit_variable_definition(ctx, d);
737 }
738}
739
740fn visit_directives<'a, V: Visitor<'a>>(
741 v: &mut V,
742 ctx: &mut VisitorContext<'a>,
743 directives: &'a [Positioned<Directive>],
744) {
745 for d in directives {
746 v.enter_directive(ctx, d);
747
748 let schema_directive = ctx.registry.directives.get(d.node.name.node.as_str());
749
750 for (name, value) in &d.node.arguments {
751 v.enter_argument(ctx, name, value);
752 let expected_ty = schema_directive
753 .and_then(|schema_directive| schema_directive.args.get(&*name.node))
754 .map(|input_ty| MetaTypeName::create(&input_ty.ty));
755 ctx.with_input_type(expected_ty, |ctx| {
756 visit_input_value(v, ctx, d.pos, expected_ty, &value.node)
757 });
758 v.exit_argument(ctx, name, value);
759 }
760
761 v.exit_directive(ctx, d);
762 }
763}
764
765fn visit_fragment_definition<'a, V: Visitor<'a>>(
766 v: &mut V,
767 ctx: &mut VisitorContext<'a>,
768 name: &'a Name,
769 fragment: &'a Positioned<FragmentDefinition>,
770) {
771 if v.mode() == VisitMode::Normal {
772 v.enter_fragment_definition(ctx, name, fragment);
773 visit_directives(v, ctx, &fragment.node.directives);
774 visit_selection_set(v, ctx, &fragment.node.selection_set);
775 v.exit_fragment_definition(ctx, name, fragment);
776 }
777}
778
779fn visit_fragment_spread<'a, V: Visitor<'a>>(
780 v: &mut V,
781 ctx: &mut VisitorContext<'a>,
782 fragment_spread: &'a Positioned<FragmentSpread>,
783) {
784 v.enter_fragment_spread(ctx, fragment_spread);
785 visit_directives(v, ctx, &fragment_spread.node.directives);
786 if v.mode() == VisitMode::Inline
787 && let Some(fragment) = ctx
788 .fragments
789 .get(fragment_spread.node.fragment_name.node.as_str())
790 {
791 visit_selection_set(v, ctx, &fragment.node.selection_set);
792 }
793 v.exit_fragment_spread(ctx, fragment_spread);
794}
795
796fn visit_inline_fragment<'a, V: Visitor<'a>>(
797 v: &mut V,
798 ctx: &mut VisitorContext<'a>,
799 inline_fragment: &'a Positioned<InlineFragment>,
800) {
801 v.enter_inline_fragment(ctx, inline_fragment);
802 visit_directives(v, ctx, &inline_fragment.node.directives);
803 visit_selection_set(v, ctx, &inline_fragment.node.selection_set);
804 v.exit_inline_fragment(ctx, inline_fragment);
805}
806
807#[derive(Debug, PartialEq)]
808pub(crate) struct RuleError {
809 pub(crate) locations: Vec<Pos>,
810 pub(crate) message: String,
811}
812
813impl RuleError {
814 pub(crate) fn new(locations: Vec<Pos>, msg: impl Into<String>) -> Self {
815 Self {
816 locations,
817 message: msg.into(),
818 }
819 }
820}
821
822impl Display for RuleError {
823 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
824 for (idx, loc) in self.locations.iter().enumerate() {
825 if idx == 0 {
826 write!(f, "[")?;
827 } else {
828 write!(f, ", ")?;
829 }
830
831 write!(f, "{}:{}", loc.line, loc.column)?;
832
833 if idx == self.locations.len() - 1 {
834 write!(f, "] ")?;
835 }
836 }
837
838 write!(f, "{}", self.message)?;
839 Ok(())
840 }
841}
842
843impl From<RuleError> for ServerError {
844 fn from(e: RuleError) -> Self {
845 Self {
846 message: e.message,
847 source: None,
848 locations: e.locations,
849 path: Vec::new(),
850 extensions: None,
851 }
852 }
853}