Skip to main content

juniper/validation/
visitor.rs

1use crate::{
2    ast::{
3        Arguments, BorrowedType, Definition, Directive, Document, Field, Fragment, FragmentSpread,
4        InlineFragment, InputValue, Operation, OperationType, Selection, VariableDefinitions,
5    },
6    parser::Spanning,
7    schema::meta::Argument,
8    validation::{ValidatorContext, Visitor, multi_visitor::MultiVisitorCons},
9    value::ScalarValue,
10};
11
12#[doc(hidden)]
13pub fn visit<'a, A, B, S>(
14    v: &mut MultiVisitorCons<A, B>,
15    ctx: &mut ValidatorContext<'a, S>,
16    d: &'a Document<S>,
17) where
18    S: ScalarValue,
19    MultiVisitorCons<A, B>: Visitor<'a, S>,
20{
21    v.enter_document(ctx, d);
22    visit_definitions(v, ctx, d);
23    v.exit_document(ctx, d);
24}
25
26fn visit_definitions<'a, S, V>(v: &mut V, ctx: &mut ValidatorContext<'a, S>, d: &'a [Definition<S>])
27where
28    S: ScalarValue,
29    V: Visitor<'a, S>,
30{
31    for def in d {
32        let def_type = match def {
33            Definition::Fragment(Spanning {
34                item:
35                    Fragment {
36                        type_condition: Spanning { item: name, .. },
37                        ..
38                    },
39                ..
40            }) => Some(BorrowedType::non_null(name)),
41            Definition::Operation(Spanning {
42                item:
43                    Operation {
44                        operation_type: OperationType::Query,
45                        ..
46                    },
47                ..
48            }) => Some(BorrowedType::non_null(
49                ctx.schema.concrete_query_type().name().unwrap(),
50            )),
51            Definition::Operation(Spanning {
52                item:
53                    Operation {
54                        operation_type: OperationType::Mutation,
55                        ..
56                    },
57                ..
58            }) => ctx
59                .schema
60                .concrete_mutation_type()
61                .map(|t| BorrowedType::non_null(t.name().unwrap())),
62            Definition::Operation(Spanning {
63                item:
64                    Operation {
65                        operation_type: OperationType::Subscription,
66                        ..
67                    },
68                ..
69            }) => ctx
70                .schema
71                .concrete_subscription_type()
72                .map(|t| BorrowedType::non_null(t.name().unwrap())),
73        };
74
75        ctx.with_pushed_type(def_type, |ctx| {
76            enter_definition(v, ctx, def);
77            visit_definition(v, ctx, def);
78            exit_definition(v, ctx, def);
79        });
80    }
81}
82
83fn enter_definition<'a, S, V>(v: &mut V, ctx: &mut ValidatorContext<'a, S>, def: &'a Definition<S>)
84where
85    S: ScalarValue,
86    V: Visitor<'a, S>,
87{
88    match *def {
89        Definition::Operation(ref op) => v.enter_operation_definition(ctx, op),
90        Definition::Fragment(ref f) => v.enter_fragment_definition(ctx, f),
91    }
92}
93
94fn exit_definition<'a, S, V>(v: &mut V, ctx: &mut ValidatorContext<'a, S>, def: &'a Definition<S>)
95where
96    S: ScalarValue,
97    V: Visitor<'a, S>,
98{
99    match *def {
100        Definition::Operation(ref op) => v.exit_operation_definition(ctx, op),
101        Definition::Fragment(ref f) => v.exit_fragment_definition(ctx, f),
102    }
103}
104
105fn visit_definition<'a, S, V>(v: &mut V, ctx: &mut ValidatorContext<'a, S>, def: &'a Definition<S>)
106where
107    S: ScalarValue,
108    V: Visitor<'a, S>,
109{
110    match *def {
111        Definition::Operation(ref op) => {
112            visit_variable_definitions(v, ctx, &op.item.variable_definitions);
113            visit_directives(v, ctx, &op.item.directives);
114            visit_selection_set(v, ctx, &op.item.selection_set);
115        }
116        Definition::Fragment(ref f) => {
117            visit_directives(v, ctx, &f.item.directives);
118            visit_selection_set(v, ctx, &f.item.selection_set);
119        }
120    }
121}
122
123fn visit_variable_definitions<'a, S, V>(
124    v: &mut V,
125    ctx: &mut ValidatorContext<'a, S>,
126    defs: &'a Option<Spanning<VariableDefinitions<S>>>,
127) where
128    S: ScalarValue,
129    V: Visitor<'a, S>,
130{
131    if let Some(ref defs) = *defs {
132        for def in defs.item.iter() {
133            let var_type = &def.1.var_type.item;
134
135            ctx.with_pushed_input_type(Some(var_type), |ctx| {
136                v.enter_variable_definition(ctx, def);
137
138                if let Some(ref default_value) = def.1.default_value {
139                    visit_input_value(v, ctx, default_value);
140                }
141
142                if let Some(dirs) = &def.1.directives {
143                    for directive in dirs {
144                        let directive_arguments = ctx
145                            .schema
146                            .directive_by_name(directive.item.name.item)
147                            .map(|d| &d.arguments);
148
149                        v.enter_directive(ctx, directive);
150                        visit_arguments(v, ctx, directive_arguments, &directive.item.arguments);
151                        v.exit_directive(ctx, directive);
152                    }
153                }
154
155                v.exit_variable_definition(ctx, def);
156            })
157        }
158    }
159}
160
161fn visit_directives<'a, S, V>(
162    v: &mut V,
163    ctx: &mut ValidatorContext<'a, S>,
164    directives: &'a Option<Vec<Spanning<Directive<S>>>>,
165) where
166    S: ScalarValue,
167    V: Visitor<'a, S>,
168{
169    if let Some(ref directives) = *directives {
170        for directive in directives {
171            let directive_arguments = ctx
172                .schema
173                .directive_by_name(directive.item.name.item)
174                .map(|d| &d.arguments);
175
176            v.enter_directive(ctx, directive);
177            visit_arguments(v, ctx, directive_arguments, &directive.item.arguments);
178            v.exit_directive(ctx, directive);
179        }
180    }
181}
182
183fn visit_arguments<'a, S, V>(
184    v: &mut V,
185    ctx: &mut ValidatorContext<'a, S>,
186    meta_args: Option<&'a Vec<Argument<S>>>,
187    arguments: &'a Option<Spanning<Arguments<S>>>,
188) where
189    S: ScalarValue,
190    V: Visitor<'a, S>,
191{
192    if let Some(ref arguments) = *arguments {
193        for argument in arguments.item.iter() {
194            let arg_type = meta_args
195                .and_then(|args| args.iter().find(|a| a.name == argument.0.item))
196                .map(|a| &a.arg_type);
197
198            ctx.with_pushed_input_type(arg_type, |ctx| {
199                v.enter_argument(ctx, argument);
200
201                visit_input_value(v, ctx, &argument.1);
202
203                v.exit_argument(ctx, argument);
204            })
205        }
206    }
207}
208
209fn visit_selection_set<'a, S, V>(
210    v: &mut V,
211    ctx: &mut ValidatorContext<'a, S>,
212    selection_set: &'a [Selection<S>],
213) where
214    S: ScalarValue,
215    V: Visitor<'a, S>,
216{
217    ctx.with_pushed_parent_type(|ctx| {
218        v.enter_selection_set(ctx, selection_set);
219
220        for selection in selection_set.iter() {
221            visit_selection(v, ctx, selection);
222        }
223
224        v.exit_selection_set(ctx, selection_set);
225    });
226}
227
228fn visit_selection<'a, S, V>(
229    v: &mut V,
230    ctx: &mut ValidatorContext<'a, S>,
231    selection: &'a Selection<S>,
232) where
233    S: ScalarValue,
234    V: Visitor<'a, S>,
235{
236    match *selection {
237        Selection::Field(ref field) => visit_field(v, ctx, field),
238        Selection::FragmentSpread(ref spread) => visit_fragment_spread(v, ctx, spread),
239        Selection::InlineFragment(ref fragment) => visit_inline_fragment(v, ctx, fragment),
240    }
241}
242
243fn visit_field<'a, S, V>(
244    v: &mut V,
245    ctx: &mut ValidatorContext<'a, S>,
246    field: &'a Spanning<Field<S>>,
247) where
248    S: ScalarValue,
249    V: Visitor<'a, S>,
250{
251    let meta_field = ctx
252        .parent_type()
253        .and_then(|t| t.field_by_name(field.item.name.item));
254
255    let field_type = meta_field.map(|f| &f.field_type);
256    let field_args = meta_field.and_then(|f| f.arguments.as_ref());
257
258    ctx.with_pushed_type(field_type, |ctx| {
259        v.enter_field(ctx, field);
260
261        visit_arguments(v, ctx, field_args, &field.item.arguments);
262        visit_directives(v, ctx, &field.item.directives);
263
264        if let Some(ref selection_set) = field.item.selection_set {
265            visit_selection_set(v, ctx, selection_set);
266        }
267
268        v.exit_field(ctx, field);
269    });
270}
271
272fn visit_fragment_spread<'a, S, V>(
273    v: &mut V,
274    ctx: &mut ValidatorContext<'a, S>,
275    spread: &'a Spanning<FragmentSpread<S>>,
276) where
277    S: ScalarValue,
278    V: Visitor<'a, S>,
279{
280    v.enter_fragment_spread(ctx, spread);
281
282    visit_directives(v, ctx, &spread.item.directives);
283
284    v.exit_fragment_spread(ctx, spread);
285}
286
287fn visit_inline_fragment<'a, S, V>(
288    v: &mut V,
289    ctx: &mut ValidatorContext<'a, S>,
290    fragment: &'a Spanning<InlineFragment<'a, S>>,
291) where
292    S: ScalarValue,
293    V: Visitor<'a, S>,
294{
295    let mut visit_fn = move |ctx: &mut ValidatorContext<'a, S>| {
296        v.enter_inline_fragment(ctx, fragment);
297
298        visit_directives(v, ctx, &fragment.item.directives);
299        visit_selection_set(v, ctx, &fragment.item.selection_set);
300
301        v.exit_inline_fragment(ctx, fragment);
302    };
303
304    if let Some(Spanning {
305        item: type_name, ..
306    }) = fragment.item.type_condition
307    {
308        ctx.with_pushed_type(Some(BorrowedType::non_null(type_name)), visit_fn);
309    } else {
310        visit_fn(ctx);
311    }
312}
313
314fn visit_input_value<'a, S, V>(
315    v: &mut V,
316    ctx: &mut ValidatorContext<'a, S>,
317    input_value: &'a Spanning<InputValue<S>>,
318) where
319    S: ScalarValue,
320    V: Visitor<'a, S>,
321{
322    enter_input_value(v, ctx, input_value);
323
324    match &input_value.item {
325        InputValue::Object(fields) => {
326            for (key, value) in fields {
327                let inner_type = ctx
328                    .current_input_type_literal()
329                    .and_then(|t| t.name().and_then(|n| ctx.schema.concrete_type_by_name(n)))
330                    .and_then(|ct| ct.input_field_by_name(&key.item))
331                    .map(|f| &f.arg_type);
332
333                ctx.with_pushed_input_type(inner_type, |ctx| {
334                    v.enter_object_field(ctx, (key.as_ref(), value.as_ref()));
335                    visit_input_value(v, ctx, value);
336                    v.exit_object_field(ctx, (key.as_ref(), value.as_ref()));
337                })
338            }
339        }
340        InputValue::List(ls) => {
341            let inner_type = ctx
342                .current_input_type_literal()
343                .and_then(|t| t.borrow_list_inner());
344
345            ctx.with_pushed_input_type(inner_type, |ctx| {
346                for value in ls {
347                    visit_input_value(v, ctx, value);
348                }
349            })
350        }
351        _ => (),
352    }
353
354    exit_input_value(v, ctx, input_value);
355}
356
357fn enter_input_value<'a, S, V>(
358    v: &mut V,
359    ctx: &mut ValidatorContext<'a, S>,
360    input_value: &'a Spanning<InputValue<S>>,
361) where
362    S: ScalarValue,
363    V: Visitor<'a, S>,
364{
365    use crate::InputValue::*;
366
367    let span = &input_value.span;
368
369    match &input_value.item {
370        Null => v.enter_null_value(ctx, Spanning { span, item: &() }),
371        Scalar(item) => v.enter_scalar_value(ctx, Spanning { span, item }),
372        Enum(item) => v.enter_enum_value(ctx, Spanning { span, item }),
373        Variable(item) => v.enter_variable_value(ctx, Spanning { span, item }),
374        List(item) => v.enter_list_value(ctx, Spanning { span, item }),
375        Object(item) => v.enter_object_value(ctx, Spanning { span, item }),
376    }
377}
378
379fn exit_input_value<'a, S, V>(
380    v: &mut V,
381    ctx: &mut ValidatorContext<'a, S>,
382    input_value: &'a Spanning<InputValue<S>>,
383) where
384    S: ScalarValue,
385    V: Visitor<'a, S>,
386{
387    use crate::InputValue::*;
388
389    let span = &input_value.span;
390
391    match &input_value.item {
392        Null => v.exit_null_value(ctx, Spanning { span, item: &() }),
393        Scalar(item) => v.exit_scalar_value(ctx, Spanning { span, item }),
394        Enum(item) => v.exit_enum_value(ctx, Spanning { span, item }),
395        Variable(item) => v.exit_variable_value(ctx, Spanning { span, item }),
396        List(item) => v.exit_list_value(ctx, Spanning { span, item }),
397        Object(item) => v.exit_object_value(ctx, Spanning { span, item }),
398    }
399}