bluejay_validator/executable/document/rules/
required_arguments.rs

1use crate::executable::{
2    document::{ArgumentError, Error, Path, Rule, Visitor},
3    Cache,
4};
5use bluejay_core::definition::{
6    DirectiveDefinition, FieldDefinition, InputType, InputValueDefinition, SchemaDefinition,
7};
8use bluejay_core::executable::{ExecutableDocument, Field};
9use bluejay_core::{Argument, AsIter, Directive};
10use std::collections::HashMap;
11
12pub struct RequiredArguments<'a, E: ExecutableDocument, S: SchemaDefinition> {
13    schema_definition: &'a S,
14    errors: Vec<Error<'a, E, S>>,
15}
16
17impl<'a, E: ExecutableDocument + 'a, S: SchemaDefinition + 'a> RequiredArguments<'a, E, S> {
18    fn visit_directive<
19        const CONST: bool,
20        F: Fn(ArgumentError<'a, CONST, E, S>) -> Error<'a, E, S>,
21    >(
22        &mut self,
23        directive: &'a <E as ExecutableDocument>::Directive<CONST>,
24        build_error: F,
25    ) {
26        if let Some(directive_definition) = self
27            .schema_definition
28            .get_directive_definition(directive.name())
29        {
30            self.visit_arguments(
31                directive.arguments(),
32                directive_definition.arguments_definition(),
33                |missing_argument_definitions| {
34                    build_error(ArgumentError::DirectiveMissingRequiredArguments {
35                        directive,
36                        directive_definition,
37                        missing_argument_definitions,
38                    })
39                },
40            )
41        }
42    }
43
44    fn visit_arguments<
45        const CONST: bool,
46        F: Fn(Vec<&'a S::InputValueDefinition>) -> Error<'a, E, S>,
47    >(
48        &mut self,
49        arguments: Option<&'a E::Arguments<CONST>>,
50        arguments_definition: Option<&'a S::ArgumentsDefinition>,
51        build_error: F,
52    ) {
53        if let Some(arguments_definition) = arguments_definition {
54            let indexed_arguments = arguments
55                .map(|arguments| {
56                    arguments.iter().fold(
57                        HashMap::new(),
58                        |mut indexed_arguments: HashMap<&'a str, &'a E::Argument<CONST>>,
59                         argument| {
60                            indexed_arguments.insert(argument.name(), argument);
61                            indexed_arguments
62                        },
63                    )
64                })
65                .unwrap_or_default();
66            let missing_argument_definitions = arguments_definition
67                .iter()
68                .filter(|ivd| {
69                    ivd.r#type().is_required()
70                        && ivd.default_value().is_none()
71                        && !indexed_arguments.contains_key(ivd.name())
72                })
73                .collect::<Vec<_>>();
74            if !missing_argument_definitions.is_empty() {
75                self.errors.push(build_error(missing_argument_definitions));
76            }
77        }
78    }
79}
80
81impl<'a, E: ExecutableDocument + 'a, S: SchemaDefinition + 'a> Visitor<'a, E, S>
82    for RequiredArguments<'a, E, S>
83{
84    fn new(_: &'a E, schema_definition: &'a S, _: &'a Cache<'a, E, S>) -> Self {
85        Self {
86            schema_definition,
87            errors: Vec::new(),
88        }
89    }
90
91    fn visit_field(
92        &mut self,
93        field: &'a <E as ExecutableDocument>::Field,
94        field_definition: &'a S::FieldDefinition,
95        _: &Path<'a, E>,
96    ) {
97        self.visit_arguments(
98            field.arguments(),
99            field_definition.arguments_definition(),
100            |missing_argument_definitions| {
101                Error::InvalidVariableArgument(ArgumentError::FieldMissingRequiredArguments {
102                    field,
103                    field_definition,
104                    missing_argument_definitions,
105                })
106            },
107        )
108    }
109
110    fn visit_variable_directive(
111        &mut self,
112        directive: &'a <E as ExecutableDocument>::Directive<false>,
113        _: bluejay_core::definition::DirectiveLocation,
114    ) {
115        self.visit_directive(directive, Error::InvalidVariableArgument)
116    }
117
118    fn visit_const_directive(
119        &mut self,
120        directive: &'a <E as ExecutableDocument>::Directive<true>,
121        _: bluejay_core::definition::DirectiveLocation,
122    ) {
123        self.visit_directive(directive, Error::InvalidConstArgument)
124    }
125}
126
127impl<'a, E: ExecutableDocument + 'a, S: SchemaDefinition + 'a> Rule<'a, E, S>
128    for RequiredArguments<'a, E, S>
129{
130    type Error = Error<'a, E, S>;
131    type Errors = std::vec::IntoIter<Error<'a, E, S>>;
132
133    fn into_errors(self) -> Self::Errors {
134        self.errors.into_iter()
135    }
136}