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}