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
126 .iter()
127 .for_each(|selection| 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, path);
135 }
136 }
137 SelectionReference::InlineFragment(i) => {
138 self.visit_inline_fragment(i, scoped_type, path)
139 }
140 SelectionReference::FragmentSpread(fs) => {
141 self.visit_fragment_spread(fs, scoped_type, path)
142 }
143 })
144 }
145
146 fn visit_field(
147 &mut self,
148 field: &'a E::Field,
149 field_definition: &'a S::FieldDefinition,
150 path: &Path<'a, E>,
151 ) {
152 self.visitor.visit_field(field, field_definition, path);
153 if let Some(directives) = field.directives() {
154 self.visit_variable_directives(directives, DirectiveLocation::Field, path);
155 }
156
157 if let Some((arguments, arguments_definition)) = field
158 .arguments()
159 .zip(field_definition.arguments_definition())
160 {
161 self.visit_variable_arguments(arguments, arguments_definition, path);
162 }
163
164 if let Some(selection_set) = field.selection_set() {
165 if let Some(nested_type) = self
166 .schema_definition
167 .get_type_definition(field_definition.r#type().base_name())
168 {
169 self.visit_selection_set(selection_set, nested_type, path);
170 }
171 }
172
173 self.visitor.leave_field(field, field_definition);
174 }
175
176 fn visit_variable_directives(
177 &mut self,
178 directives: &'a E::Directives<false>,
179 location: DirectiveLocation,
180 path: &Path<'a, E>,
181 ) {
182 self.visitor.visit_variable_directives(directives, location);
183 directives
184 .iter()
185 .for_each(|directive| self.visit_variable_directive(directive, location, path));
186 }
187
188 fn visit_const_directives(
189 &mut self,
190 directives: &'a E::Directives<true>,
191 location: DirectiveLocation,
192 ) {
193 self.visitor.visit_const_directives(directives, location);
194 directives
195 .iter()
196 .for_each(|directive| self.visit_const_directive(directive, location));
197 }
198
199 fn visit_variable_directive(
200 &mut self,
201 directive: &'a E::Directive<false>,
202 location: DirectiveLocation,
203 path: &Path<'a, E>,
204 ) {
205 self.visitor.visit_variable_directive(directive, location);
206 if let Some(arguments) = directive.arguments() {
207 if let Some(arguments_definition) = self
208 .schema_definition
209 .get_directive_definition(directive.name())
210 .and_then(DirectiveDefinition::arguments_definition)
211 {
212 self.visit_variable_arguments(arguments, arguments_definition, path);
213 }
214 }
215 }
216
217 fn visit_const_directive(
218 &mut self,
219 directive: &'a E::Directive<true>,
220 location: DirectiveLocation,
221 ) {
222 self.visitor.visit_const_directive(directive, location);
223 if let Some(arguments) = directive.arguments() {
224 if let Some(arguments_definition) = self
225 .schema_definition
226 .get_directive_definition(directive.name())
227 .and_then(DirectiveDefinition::arguments_definition)
228 {
229 self.visit_const_arguments(arguments, arguments_definition);
230 }
231 }
232 }
233
234 fn visit_inline_fragment(
235 &mut self,
236 inline_fragment: &'a E::InlineFragment,
237 scoped_type: TypeDefinitionReference<'a, S::TypeDefinition>,
238 path: &Path<'a, E>,
239 ) {
240 if let Some(directives) = inline_fragment.directives() {
241 self.visit_variable_directives(directives, DirectiveLocation::InlineFragment, path);
242 }
243
244 let fragment_type = if let Some(type_condition) = inline_fragment.type_condition() {
245 self.schema_definition.get_type_definition(type_condition)
246 } else {
247 Some(scoped_type)
248 };
249
250 if let Some(fragment_type) = fragment_type {
251 self.visit_selection_set(inline_fragment.selection_set(), fragment_type, path);
252 }
253
254 self.visitor
255 .visit_inline_fragment(inline_fragment, scoped_type);
256 }
257
258 fn visit_fragment_spread(
259 &mut self,
260 fragment_spread: &'a E::FragmentSpread,
261 scoped_type: TypeDefinitionReference<'a, S::TypeDefinition>,
262 path: &Path<'a, E>,
263 ) {
264 if let Some(directives) = fragment_spread.directives() {
265 self.visit_variable_directives(directives, DirectiveLocation::FragmentSpread, path);
266 }
267
268 self.visitor
269 .visit_fragment_spread(fragment_spread, scoped_type, path);
270 }
272
273 fn visit_variable_definitions(&mut self, variable_definitions: &'a E::VariableDefinitions) {
274 self.visitor
275 .visit_variable_definitions(variable_definitions);
276 variable_definitions.iter().for_each(|variable_definition| {
277 if let Some(directives) = variable_definition.directives() {
278 self.visit_const_directives(directives, DirectiveLocation::VariableDefinition);
279 }
280 self.visitor.visit_variable_definition(variable_definition);
281 });
282 }
283
284 fn visit_const_arguments(
285 &mut self,
286 arguments: &'a E::Arguments<true>,
287 arguments_definition: &'a S::ArgumentsDefinition,
288 ) {
289 arguments.iter().for_each(|argument| {
290 if let Some(ivd) = arguments_definition.get(argument.name()) {
291 self.visit_const_argument(argument, ivd);
292 }
293 });
294 }
295
296 fn visit_variable_arguments(
297 &mut self,
298 arguments: &'a E::Arguments<false>,
299 arguments_definition: &'a S::ArgumentsDefinition,
300 path: &Path<'a, E>,
301 ) {
302 arguments.iter().for_each(|argument| {
303 if let Some(ivd) = arguments_definition.get(argument.name()) {
304 self.visit_variable_argument(argument, ivd, path);
305 }
306 });
307 }
308
309 fn visit_const_argument(
310 &mut self,
311 argument: &'a E::Argument<true>,
312 input_value_definition: &'a S::InputValueDefinition,
313 ) {
314 self.visitor
315 .visit_const_argument(argument, input_value_definition);
316 }
317
318 fn visit_variable_argument(
319 &mut self,
320 argument: &'a E::Argument<false>,
321 input_value_definition: &'a S::InputValueDefinition,
322 path: &Path<'a, E>,
323 ) {
324 self.visitor
325 .visit_variable_argument(argument, input_value_definition, path);
326 }
327
328 pub fn validate(
329 executable_document: &'a E,
330 schema_definition: &'a S,
331 cache: &'a Cache<'a, E, S>,
332 ) -> <V as Rule<'a, E, S>>::Errors
333 where
334 V: Rule<'a, E, S>,
335 {
336 let mut instance = Self::new(executable_document, schema_definition, cache);
337 instance.visit();
338 instance.visitor.into_errors()
339 }
340
341 pub fn analyze(
342 executable_document: &'a E,
343 schema_definition: &'a S,
344 cache: &'a Cache<'a, E, S>,
345 ) -> <V as Analyzer<'a, E, S>>::Output
346 where
347 V: Analyzer<'a, E, S>,
348 {
349 let mut instance = Self::new(executable_document, schema_definition, cache);
350 instance.visit();
351 instance.visitor.into_output()
352 }
353}
354
355impl<'a, E: ExecutableDocument, S: SchemaDefinition, R: Rule<'a, E, S>, A: Analyzer<'a, E, S>>
356 Orchestrator<'a, E, S, (R, A)>
357{
358 pub fn validate_and_analyze(
359 executable_document: &'a E,
360 schema_definition: &'a S,
361 cache: &'a Cache<'a, E, S>,
362 ) -> (
363 <R as Rule<'a, E, S>>::Errors,
364 <A as Analyzer<'a, E, S>>::Output,
365 ) {
366 let mut instance = Self::new(executable_document, schema_definition, cache);
367 instance.visit();
368 (
369 instance.visitor.0.into_errors(),
370 instance.visitor.1.into_output(),
371 )
372 }
373}