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};
10
11pub struct RequiredArguments<'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> RequiredArguments<'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(directive_definition) = self
26 .schema_definition
27 .get_directive_definition(directive.name())
28 {
29 self.visit_arguments(
30 directive.arguments(),
31 directive_definition.arguments_definition(),
32 |missing_argument_definitions| {
33 build_error(ArgumentError::DirectiveMissingRequiredArguments {
34 directive,
35 directive_definition,
36 missing_argument_definitions,
37 })
38 },
39 )
40 }
41 }
42
43 fn visit_arguments<
44 const CONST: bool,
45 F: Fn(Vec<&'a S::InputValueDefinition>) -> Error<'a, E, S>,
46 >(
47 &mut self,
48 arguments: Option<&'a E::Arguments<CONST>>,
49 arguments_definition: Option<&'a S::ArgumentsDefinition>,
50 build_error: F,
51 ) {
52 if let Some(arguments_definition) = arguments_definition {
53 let missing_argument_definitions: Vec<_> = arguments_definition
55 .iter()
56 .filter(|ivd| {
57 ivd.r#type().is_required()
58 && ivd.default_value().is_none()
59 && !arguments
60 .map(|args| args.iter().any(|arg| arg.name() == ivd.name()))
61 .unwrap_or(false)
62 })
63 .collect();
64 if !missing_argument_definitions.is_empty() {
65 self.errors.push(build_error(missing_argument_definitions));
66 }
67 }
68 }
69}
70
71impl<'a, E: ExecutableDocument + 'a, S: SchemaDefinition + 'a> Visitor<'a, E, S>
72 for RequiredArguments<'a, E, S>
73{
74 fn new(_: &'a E, schema_definition: &'a S, _: &'a Cache<'a, E, S>) -> Self {
75 Self {
76 schema_definition,
77 errors: Vec::new(),
78 }
79 }
80
81 fn visit_field(
82 &mut self,
83 field: &'a <E as ExecutableDocument>::Field,
84 field_definition: &'a S::FieldDefinition,
85 _: &Path<'a, E>,
86 ) {
87 self.visit_arguments(
88 field.arguments(),
89 field_definition.arguments_definition(),
90 |missing_argument_definitions| {
91 Error::InvalidVariableArgument(ArgumentError::FieldMissingRequiredArguments {
92 field,
93 field_definition,
94 missing_argument_definitions,
95 })
96 },
97 )
98 }
99
100 fn visit_variable_directive(
101 &mut self,
102 directive: &'a <E as ExecutableDocument>::Directive<false>,
103 _: bluejay_core::definition::DirectiveLocation,
104 ) {
105 self.visit_directive(directive, Error::InvalidVariableArgument)
106 }
107
108 fn visit_const_directive(
109 &mut self,
110 directive: &'a <E as ExecutableDocument>::Directive<true>,
111 _: bluejay_core::definition::DirectiveLocation,
112 ) {
113 self.visit_directive(directive, Error::InvalidConstArgument)
114 }
115}
116
117impl<'a, E: ExecutableDocument + 'a, S: SchemaDefinition + 'a> Rule<'a, E, S>
118 for RequiredArguments<'a, E, S>
119{
120 type Error = Error<'a, E, S>;
121 type Errors = std::vec::IntoIter<Error<'a, E, S>>;
122
123 fn into_errors(self) -> Self::Errors {
124 self.errors.into_iter()
125 }
126}