bluejay_validator/executable/document/rules/
required_arguments.rs1use 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}