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