1use crate::executable::{
2 document::{Analyzer, BuiltinRules, Path, PathRoot, Rule, Visitor},
3 Cache,
4};
5use bluejay_core::definition::{
6 ArgumentsDefinition, DirectiveDefinition, DirectiveLocation, FieldDefinition, FieldsDefinition,
7 ObjectTypeDefinition, OutputType, SchemaDefinition, TypeDefinitionReference,
8};
9use bluejay_core::executable::{
10 ExecutableDocument, Field, FragmentDefinition, FragmentSpread, InlineFragment,
11 OperationDefinition, Selection, SelectionReference, VariableDefinition,
12};
13use bluejay_core::{Argument, AsIter, Directive, OperationType};
14
15pub struct Orchestrator<'a, E: ExecutableDocument, S: SchemaDefinition, V: Visitor<'a, E, S>> {
16 schema_definition: &'a S,
17 executable_document: &'a E,
18 visitor: V,
19}
20
21pub type BuiltinRulesValidator<'a, E, S> = Orchestrator<'a, E, S, BuiltinRules<'a, E, S>>;
22
23impl<'a, E: ExecutableDocument, S: SchemaDefinition, V: Visitor<'a, E, S>>
24 Orchestrator<'a, E, S, V>
25{
26 fn new(
27 executable_document: &'a E,
28 schema_definition: &'a S,
29 cache: &'a Cache<'a, E, S>,
30 ) -> Self {
31 Self {
32 schema_definition,
33 executable_document,
34 visitor: Visitor::new(executable_document, schema_definition, cache),
35 }
36 }
37
38 fn visit(&mut self) {
39 self.executable_document
40 .operation_definitions()
41 .for_each(|operation_definition| {
42 self.visit_operation_definition(operation_definition);
43 });
44 self.executable_document
45 .fragment_definitions()
46 .for_each(|fragment_definition| {
47 self.visit_fragment_definition(fragment_definition);
48 });
49 }
50
51 fn visit_operation_definition(&mut self, operation_definition: &'a E::OperationDefinition) {
52 let path = Path::new(PathRoot::Operation(operation_definition));
53 self.visitor
54 .visit_operation_definition(operation_definition);
55 let core_operation_definition = operation_definition.as_ref();
56 if let Some(directives) = core_operation_definition.directives() {
57 self.visit_variable_directives(
58 directives,
59 core_operation_definition
60 .operation_type()
61 .associated_directive_location(),
62 &path,
63 );
64 }
65
66 if let Some(variable_definitions) = core_operation_definition.variable_definitions() {
67 self.visit_variable_definitions(variable_definitions);
68 }
69
70 let root_operation_type_definition_name = match core_operation_definition.operation_type() {
71 OperationType::Query => Some(self.schema_definition.query().name()),
72 OperationType::Mutation => self
73 .schema_definition
74 .mutation()
75 .map(ObjectTypeDefinition::name),
76 OperationType::Subscription => self
77 .schema_definition
78 .subscription()
79 .map(ObjectTypeDefinition::name),
80 };
81
82 if let Some(root_operation_type_definition_name) = root_operation_type_definition_name {
83 self.visit_selection_set(
84 core_operation_definition.selection_set(),
85 self.schema_definition
86 .get_type_definition(root_operation_type_definition_name)
87 .unwrap_or_else(|| {
88 panic!(
89 "Schema definition's `get_type` method returned `None` for {} root",
90 core_operation_definition.operation_type()
91 )
92 }),
93 &path,
94 );
95 }
96 }
97
98 fn visit_fragment_definition(&mut self, fragment_definition: &'a E::FragmentDefinition) {
99 let path = Path::new(PathRoot::Fragment(fragment_definition));
100 let type_condition = self
101 .schema_definition
102 .get_type_definition(fragment_definition.type_condition());
103 if let Some(type_condition) = type_condition {
104 self.visit_selection_set(fragment_definition.selection_set(), type_condition, &path);
105 }
106 if let Some(directives) = fragment_definition.directives() {
107 self.visit_variable_directives(
108 directives,
109 DirectiveLocation::FragmentDefinition,
110 &path,
111 );
112 }
113
114 self.visitor.visit_fragment_definition(fragment_definition);
115 }
116
117 fn visit_selection_set(
118 &mut self,
119 selection_set: &'a E::SelectionSet,
120 scoped_type: TypeDefinitionReference<'a, S::TypeDefinition>,
121 path: &Path<'a, E>,
122 ) {
123 self.visitor.visit_selection_set(selection_set, scoped_type);
124
125 selection_set.iter().for_each(|selection| {
126 let nested_path = path.with_selection(selection);
127 match selection.as_ref() {
128 SelectionReference::Field(f) => {
129 let field_definition = scoped_type
130 .fields_definition()
131 .and_then(|fields_definition| fields_definition.get(f.name()));
132
133 if let Some(field_definition) = field_definition {
134 self.visit_field(f, field_definition, &nested_path);
135 }
136 }
137 SelectionReference::InlineFragment(i) => {
138 self.visit_inline_fragment(i, scoped_type, &nested_path)
139 }
140 SelectionReference::FragmentSpread(fs) => {
141 self.visit_fragment_spread(fs, scoped_type, path)
142 }
143 }
144 })
145 }
146
147 fn visit_field(
148 &mut self,
149 field: &'a E::Field,
150 field_definition: &'a S::FieldDefinition,
151 path: &Path<'a, E>,
152 ) {
153 self.visitor.visit_field(field, field_definition, path);
154 if let Some(directives) = field.directives() {
155 self.visit_variable_directives(directives, DirectiveLocation::Field, path);
156 }
157
158 if let Some((arguments, arguments_definition)) = field
159 .arguments()
160 .zip(field_definition.arguments_definition())
161 {
162 self.visit_variable_arguments(arguments, arguments_definition, path);
163 }
164
165 if let Some(selection_set) = field.selection_set() {
166 if let Some(nested_type) = self
167 .schema_definition
168 .get_type_definition(field_definition.r#type().base_name())
169 {
170 self.visit_selection_set(selection_set, nested_type, path);
171 }
172 }
173 }
174
175 fn visit_variable_directives(
176 &mut self,
177 directives: &'a E::Directives<false>,
178 location: DirectiveLocation,
179 path: &Path<'a, E>,
180 ) {
181 self.visitor.visit_variable_directives(directives, location);
182 directives
183 .iter()
184 .for_each(|directive| self.visit_variable_directive(directive, location, path));
185 }
186
187 fn visit_const_directives(
188 &mut self,
189 directives: &'a E::Directives<true>,
190 location: DirectiveLocation,
191 ) {
192 self.visitor.visit_const_directives(directives, location);
193 directives
194 .iter()
195 .for_each(|directive| self.visit_const_directive(directive, location));
196 }
197
198 fn visit_variable_directive(
199 &mut self,
200 directive: &'a E::Directive<false>,
201 location: DirectiveLocation,
202 path: &Path<'a, E>,
203 ) {
204 self.visitor.visit_variable_directive(directive, location);
205 if let Some(arguments) = directive.arguments() {
206 if let Some(arguments_definition) = self
207 .schema_definition
208 .get_directive_definition(directive.name())
209 .and_then(DirectiveDefinition::arguments_definition)
210 {
211 self.visit_variable_arguments(arguments, arguments_definition, path);
212 }
213 }
214 }
215
216 fn visit_const_directive(
217 &mut self,
218 directive: &'a E::Directive<true>,
219 location: DirectiveLocation,
220 ) {
221 self.visitor.visit_const_directive(directive, location);
222 if let Some(arguments) = directive.arguments() {
223 if let Some(arguments_definition) = self
224 .schema_definition
225 .get_directive_definition(directive.name())
226 .and_then(DirectiveDefinition::arguments_definition)
227 {
228 self.visit_const_arguments(arguments, arguments_definition);
229 }
230 }
231 }
232
233 fn visit_inline_fragment(
234 &mut self,
235 inline_fragment: &'a E::InlineFragment,
236 scoped_type: TypeDefinitionReference<'a, S::TypeDefinition>,
237 path: &Path<'a, E>,
238 ) {
239 if let Some(directives) = inline_fragment.directives() {
240 self.visit_variable_directives(directives, DirectiveLocation::InlineFragment, path);
241 }
242
243 let fragment_type = if let Some(type_condition) = inline_fragment.type_condition() {
244 self.schema_definition.get_type_definition(type_condition)
245 } else {
246 Some(scoped_type)
247 };
248
249 if let Some(fragment_type) = fragment_type {
250 self.visit_selection_set(inline_fragment.selection_set(), fragment_type, path);
251 }
252
253 self.visitor
254 .visit_inline_fragment(inline_fragment, scoped_type);
255 }
256
257 fn visit_fragment_spread(
258 &mut self,
259 fragment_spread: &'a E::FragmentSpread,
260 scoped_type: TypeDefinitionReference<'a, S::TypeDefinition>,
261 path: &Path<'a, E>,
262 ) {
263 if let Some(directives) = fragment_spread.directives() {
264 self.visit_variable_directives(directives, DirectiveLocation::FragmentSpread, path);
265 }
266
267 self.visitor
268 .visit_fragment_spread(fragment_spread, scoped_type, path);
269 }
271
272 fn visit_variable_definitions(&mut self, variable_definitions: &'a E::VariableDefinitions) {
273 self.visitor
274 .visit_variable_definitions(variable_definitions);
275 variable_definitions.iter().for_each(|variable_definition| {
276 if let Some(directives) = variable_definition.directives() {
277 self.visit_const_directives(directives, DirectiveLocation::VariableDefinition);
278 }
279 self.visitor.visit_variable_definition(variable_definition);
280 });
281 }
282
283 fn visit_const_arguments(
284 &mut self,
285 arguments: &'a E::Arguments<true>,
286 arguments_definition: &'a S::ArgumentsDefinition,
287 ) {
288 arguments.iter().for_each(|argument| {
289 if let Some(ivd) = arguments_definition.get(argument.name()) {
290 self.visit_const_argument(argument, ivd);
291 }
292 });
293 }
294
295 fn visit_variable_arguments(
296 &mut self,
297 arguments: &'a E::Arguments<false>,
298 arguments_definition: &'a S::ArgumentsDefinition,
299 path: &Path<'a, E>,
300 ) {
301 arguments.iter().for_each(|argument| {
302 if let Some(ivd) = arguments_definition.get(argument.name()) {
303 self.visit_variable_argument(argument, ivd, path);
304 }
305 });
306 }
307
308 fn visit_const_argument(
309 &mut self,
310 argument: &'a E::Argument<true>,
311 input_value_definition: &'a S::InputValueDefinition,
312 ) {
313 self.visitor
314 .visit_const_argument(argument, input_value_definition);
315 }
316
317 fn visit_variable_argument(
318 &mut self,
319 argument: &'a E::Argument<false>,
320 input_value_definition: &'a S::InputValueDefinition,
321 path: &Path<'a, E>,
322 ) {
323 self.visitor
324 .visit_variable_argument(argument, input_value_definition, path);
325 }
326
327 pub fn validate(
328 executable_document: &'a E,
329 schema_definition: &'a S,
330 cache: &'a Cache<'a, E, S>,
331 ) -> <V as Rule<'a, E, S>>::Errors
332 where
333 V: Rule<'a, E, S>,
334 {
335 let mut instance = Self::new(executable_document, schema_definition, cache);
336 instance.visit();
337 instance.visitor.into_errors()
338 }
339
340 pub fn analyze(
341 executable_document: &'a E,
342 schema_definition: &'a S,
343 cache: &'a Cache<'a, E, S>,
344 ) -> <V as Analyzer<'a, E, S>>::Output
345 where
346 V: Analyzer<'a, E, S>,
347 {
348 let mut instance = Self::new(executable_document, schema_definition, cache);
349 instance.visit();
350 instance.visitor.into_output()
351 }
352}
353
354impl<'a, E: ExecutableDocument, S: SchemaDefinition, R: Rule<'a, E, S>, A: Analyzer<'a, E, S>>
355 Orchestrator<'a, E, S, (R, A)>
356{
357 pub fn validate_and_analyze(
358 executable_document: &'a E,
359 schema_definition: &'a S,
360 cache: &'a Cache<'a, E, S>,
361 ) -> (
362 <R as Rule<'a, E, S>>::Errors,
363 <A as Analyzer<'a, E, S>>::Output,
364 ) {
365 let mut instance = Self::new(executable_document, schema_definition, cache);
366 instance.visit();
367 (
368 instance.visitor.0.into_errors(),
369 instance.visitor.1.into_output(),
370 )
371 }
372}