async_graphql/validation/
visitor.rs

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            // The only one with an irregular plural, "query", is always present
576            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}