bluejay_validator/executable/document/rules/
argument_names.rs

1use crate::executable::{
2    document::{ArgumentError, Error, Path, Rule, Visitor},
3    Cache,
4};
5use bluejay_core::definition::{
6    DirectiveDefinition, FieldDefinition, InputValueDefinition, SchemaDefinition,
7};
8use bluejay_core::executable::{ExecutableDocument, Field};
9use bluejay_core::{Argument, AsIter, Directive};
10
11pub struct ArgumentNames<'a, E: ExecutableDocument, S: SchemaDefinition> {
12    schema_definition: &'a S,
13    errors: Vec<Error<'a, E, S>>,
14}
15
16impl<'a, E: ExecutableDocument + 'a, S: SchemaDefinition + 'a> ArgumentNames<'a, E, S> {
17    fn visit_directive<
18        const CONST: bool,
19        F: Fn(ArgumentError<'a, CONST, E, S>) -> Error<'a, E, S>,
20    >(
21        &mut self,
22        directive: &'a <E as ExecutableDocument>::Directive<CONST>,
23        build_error: F,
24    ) {
25        if let Some((arguments, directive_definition)) = directive.arguments().zip(
26            self.schema_definition
27                .get_directive_definition(directive.name()),
28        ) {
29            self.visit_arguments(
30                Some(arguments),
31                directive_definition.arguments_definition(),
32                |argument| {
33                    build_error(ArgumentError::ArgumentDoesNotExistOnDirective {
34                        argument,
35                        directive_definition,
36                    })
37                },
38            )
39        }
40    }
41
42    fn visit_arguments<const CONST: bool, F: Fn(&'a E::Argument<CONST>) -> Error<'a, E, S>>(
43        &mut self,
44        arguments: Option<&'a E::Arguments<CONST>>,
45        arguments_definition: Option<&'a S::ArgumentsDefinition>,
46        build_error: F,
47    ) {
48        if let Some(arguments) = arguments {
49            self.errors.extend(arguments.iter().filter_map(|argument| {
50                let argument_definition = arguments_definition.and_then(|arguments_definition| {
51                    arguments_definition
52                        .iter()
53                        .find(|ivd| ivd.name() == argument.name())
54                });
55                argument_definition.is_none().then(|| build_error(argument))
56            }))
57        }
58    }
59}
60
61impl<'a, E: ExecutableDocument + 'a, S: SchemaDefinition + 'a> Visitor<'a, E, S>
62    for ArgumentNames<'a, E, S>
63{
64    fn new(_: &'a E, schema_definition: &'a S, _: &'a Cache<'a, E, S>) -> Self {
65        Self {
66            schema_definition,
67            errors: Vec::new(),
68        }
69    }
70
71    fn visit_field(
72        &mut self,
73        field: &'a <E as ExecutableDocument>::Field,
74        field_definition: &'a S::FieldDefinition,
75        _: &Path<'a, E>,
76    ) {
77        self.visit_arguments(
78            field.arguments(),
79            field_definition.arguments_definition(),
80            |argument| {
81                Error::InvalidVariableArgument(ArgumentError::ArgumentDoesNotExistOnField {
82                    argument,
83                    field_definition,
84                })
85            },
86        )
87    }
88
89    fn visit_variable_directive(
90        &mut self,
91        directive: &'a <E as ExecutableDocument>::Directive<false>,
92        _: bluejay_core::definition::DirectiveLocation,
93    ) {
94        self.visit_directive(directive, Error::InvalidVariableArgument)
95    }
96
97    fn visit_const_directive(
98        &mut self,
99        directive: &'a <E as ExecutableDocument>::Directive<true>,
100        _: bluejay_core::definition::DirectiveLocation,
101    ) {
102        self.visit_directive(directive, Error::InvalidConstArgument)
103    }
104}
105
106impl<'a, E: ExecutableDocument + 'a, S: SchemaDefinition + 'a> Rule<'a, E, S>
107    for ArgumentNames<'a, E, S>
108{
109    type Error = Error<'a, E, S>;
110    type Errors = std::vec::IntoIter<Error<'a, E, S>>;
111
112    fn into_errors(self) -> Self::Errors {
113        self.errors.into_iter()
114    }
115}