1use crate::executable::{
2 document::{Error, Rule, Visitor},
3 Cache,
4};
5use bluejay_core::definition::{
6 FieldDefinition, FieldsDefinition, ObjectTypeDefinition, OutputType, OutputTypeReference,
7 SchemaDefinition, TypeDefinitionReference,
8};
9use bluejay_core::executable::{
10 ExecutableDocument, Field, FragmentDefinition, FragmentSpread, InlineFragment, Selection,
11 SelectionReference,
12};
13use bluejay_core::{Arguments, AsIter, Indexed};
14use std::collections::{BTreeMap, HashMap};
15use std::ops::Not;
16
17pub struct FieldSelectionMerging<'a, E: ExecutableDocument, S: SchemaDefinition> {
18 cache: &'a Cache<'a, E, S>,
19 schema_definition: &'a S,
20 cached_errors: BTreeMap<Indexed<'a, E::SelectionSet>, Vec<Error<'a, E, S>>>,
21}
22
23impl<'a, E: ExecutableDocument + 'a, S: SchemaDefinition> Visitor<'a, E, S>
24 for FieldSelectionMerging<'a, E, S>
25{
26 fn new(_: &'a E, schema_definition: &'a S, cache: &'a Cache<'a, E, S>) -> Self {
27 Self {
28 cache,
29 schema_definition,
30 cached_errors: BTreeMap::new(),
31 }
32 }
33
34 fn visit_selection_set(
35 &mut self,
36 selection_set: &'a E::SelectionSet,
37 r#type: TypeDefinitionReference<'a, S::TypeDefinition>,
38 ) {
39 self.selection_set_valid(selection_set, r#type);
40 }
41}
42
43impl<'a, E: ExecutableDocument + 'a, S: SchemaDefinition + 'a> FieldSelectionMerging<'a, E, S> {
44 fn selection_set_valid(
45 &mut self,
46 selection_set: &'a E::SelectionSet,
47 parent_type: TypeDefinitionReference<'a, S::TypeDefinition>,
48 ) -> bool {
49 if let Some(errors) = self.cached_errors.get(&Indexed(selection_set)) {
50 errors.is_empty()
51 } else {
52 self.cached_errors
53 .insert(Indexed(selection_set), Vec::new());
54
55 let grouped_fields = self.selection_set_contained_fields(selection_set, parent_type);
56
57 let errors = self.fields_in_set_can_merge(grouped_fields, selection_set);
58
59 let is_valid = errors.is_empty();
60
61 self.cached_errors.insert(Indexed(selection_set), errors);
62
63 is_valid
64 }
65 }
66
67 fn fields_in_set_can_merge(
68 &mut self,
69 grouped_fields: HashMap<&'a str, Vec<FieldContext<'a, E, S>>>,
70 selection_set: &'a E::SelectionSet,
71 ) -> Vec<Error<'a, E, S>> {
72 let mut errors = Vec::new();
73
74 grouped_fields.values().for_each(|fields_for_name| {
75 self.same_response_shape(fields_for_name, selection_set, &mut errors);
76 self.same_for_common_parents_by_name(
77 fields_for_name.as_slice(),
78 selection_set,
79 &mut errors,
80 );
81 });
82
83 errors
84 }
85
86 fn same_response_shape(
87 &mut self,
88 fields_for_name: &[FieldContext<'a, E, S>],
89 selection_set: &'a E::SelectionSet,
90 errors: &mut Vec<Error<'a, E, S>>,
91 ) {
92 if fields_for_name.len() <= 1 {
93 return;
94 }
95
96 let (first, rest) = fields_for_name.split_first().unwrap();
97 let prev_len = errors.len();
98 errors.extend(rest.iter().filter_map(|other| {
99 Self::same_output_type_shape(
100 self.schema_definition,
101 first.field_definition.r#type(),
102 other.field_definition.r#type(),
103 )
104 .not()
105 .then_some(Error::FieldSelectionsDoNotMergeIncompatibleTypes {
106 selection_set,
107 field_a: first.field,
108 field_definition_a: first.field_definition,
109 field_b: other.field,
110 field_definition_b: other.field_definition,
111 })
112 }));
113
114 if errors.len() == prev_len {
115 let nested_grouped_fields =
116 self.field_contexts_contained_fields(fields_for_name.iter());
117
118 for nested_fields_for_name in nested_grouped_fields.values() {
119 self.same_response_shape(nested_fields_for_name, selection_set, errors);
120 }
121 }
122 }
123
124 fn same_for_common_parents_by_name(
125 &mut self,
126 fields_for_name: &[FieldContext<'a, E, S>],
127 selection_set: &'a E::SelectionSet,
128 errors: &mut Vec<Error<'a, E, S>>,
129 ) {
130 if fields_for_name.len() <= 1 {
131 return;
132 }
133
134 let all_same_parent =
136 fields_for_name
137 .windows(2)
138 .all(|w| match (&w[0].parent_type, &w[1].parent_type) {
139 (TypeDefinitionReference::Object(a), TypeDefinitionReference::Object(b)) => {
140 a.name() == b.name()
141 }
142 (
145 TypeDefinitionReference::Interface(_),
146 TypeDefinitionReference::Interface(_),
147 ) => true,
148 _ => false,
149 });
150
151 if all_same_parent {
152 let refs: Vec<_> = fields_for_name.iter().collect();
154 self.check_common_parent_group(&refs, selection_set, errors);
155 return;
156 }
157
158 type Group<'a, 'b, E, S> = Vec<&'b FieldContext<'a, E, S>>;
159 type ConcreteGroups<'a, 'b, E, S> = HashMap<&'a str, Group<'a, 'b, E, S>>;
160
161 let (abstract_group, concrete_groups): (Group<'a, '_, E, S>, ConcreteGroups<'a, '_, E, S>) =
162 fields_for_name.iter().fold(
163 (Vec::new(), HashMap::new()),
164 |(mut abstract_group, mut concrete_groups), field_context| {
165 match field_context.parent_type {
166 TypeDefinitionReference::Object(otd) => concrete_groups
167 .entry(otd.name())
168 .or_default()
169 .push(field_context),
170 TypeDefinitionReference::Interface(_) => abstract_group.push(field_context),
171 _ => {}
172 }
173 (abstract_group, concrete_groups)
174 },
175 );
176
177 if concrete_groups.is_empty() {
178 self.check_common_parent_group(&abstract_group, selection_set, errors);
179 } else {
180 for mut group in concrete_groups.into_values() {
181 group.extend(&abstract_group);
182 self.check_common_parent_group(&group, selection_set, errors);
183 }
184 }
185 }
186
187 fn check_common_parent_group(
188 &mut self,
189 fields_for_common_parent: &[&FieldContext<'a, E, S>],
190 selection_set: &'a E::SelectionSet,
191 errors: &mut Vec<Error<'a, E, S>>,
192 ) {
193 let Some((first, rest)) = fields_for_common_parent.split_first() else {
194 return;
195 };
196
197 let prev_len = errors.len();
198 errors.extend(rest.iter().filter_map(|other| {
199 if first.field.name() != other.field.name() {
200 Some(Error::FieldSelectionsDoNotMergeDifferingNames {
201 selection_set,
202 field_a: first.field,
203 field_b: other.field,
204 })
205 } else if !<E::Arguments<false> as Arguments<false>>::equivalent(
206 first.field.arguments(),
207 other.field.arguments(),
208 ) {
209 Some(Error::FieldSelectionsDoNotMergeDifferingArguments {
210 selection_set,
211 field_a: first.field,
212 field_b: other.field,
213 })
214 } else {
215 None
216 }
217 }));
218
219 if errors.len() == prev_len {
220 let nested_grouped_fields =
221 self.field_contexts_contained_fields(fields_for_common_parent.iter().copied());
222
223 for nested_fields_for_name in nested_grouped_fields.values() {
224 self.same_for_common_parents_by_name(
225 nested_fields_for_name.as_slice(),
226 selection_set,
227 errors,
228 );
229 }
230 }
231 }
232
233 fn selection_set_contained_fields(
234 &mut self,
235 selection_set: &'a E::SelectionSet,
236 parent_type: TypeDefinitionReference<'a, S::TypeDefinition>,
237 ) -> HashMap<&'a str, Vec<FieldContext<'a, E, S>>> {
238 let mut fields = HashMap::new();
239 self.visit_selections_for_fields(selection_set.iter(), &mut fields, parent_type, &[]);
240 fields
241 }
242
243 fn field_contexts_contained_fields<'b>(
244 &mut self,
245 field_contexts: impl Iterator<Item = &'b FieldContext<'a, E, S>>,
246 ) -> HashMap<&'a str, Vec<FieldContext<'a, E, S>>>
247 where
248 'a: 'b,
249 {
250 let mut fields = HashMap::new();
251 field_contexts.for_each(|field_context| {
252 if let Some(selection_set) = field_context.field.selection_set() {
253 if let Some(parent_type) = self
254 .schema_definition
255 .get_type_definition(field_context.field_definition.r#type().base_name())
256 {
257 if self.selection_set_valid(selection_set, parent_type) {
258 self.visit_selections_for_fields(
259 selection_set.iter(),
260 &mut fields,
261 parent_type,
262 &field_context.parent_fragments,
263 );
264 }
265 }
266 }
267 });
268 fields
269 }
270
271 fn visit_selections_for_fields(
272 &mut self,
273 selections: impl Iterator<Item = &'a E::Selection>,
274 fields: &mut HashMap<&'a str, Vec<FieldContext<'a, E, S>>>,
275 parent_type: TypeDefinitionReference<'a, S::TypeDefinition>,
276 parent_fragments: &[&'a str],
277 ) {
278 selections.for_each(|selection| match selection.as_ref() {
279 SelectionReference::Field(field) => {
280 let fields_definition = parent_type.fields_definition();
281 if let Some(field_definition) = fields_definition
282 .and_then(|fields_definition| fields_definition.get(field.name()))
283 {
284 fields
285 .entry(field.response_name())
286 .or_default()
287 .push(FieldContext {
288 field,
289 field_definition,
290 parent_type,
291 parent_fragments: parent_fragments.to_vec(),
292 });
293 }
294 }
295 SelectionReference::FragmentSpread(fs) => {
296 let fragment_name = fs.name();
297 if !parent_fragments.contains(&fragment_name) {
298 if let Some(fragment_definition) = self.cache.fragment_definition(fragment_name)
299 {
300 let type_condition = fragment_definition.type_condition();
301 if let Some(scoped_type) =
302 self.schema_definition.get_type_definition(type_condition)
303 {
304 if self.selection_set_valid(
305 fragment_definition.selection_set(),
306 parent_type,
307 ) {
308 let mut new_parent_fragments =
309 Vec::with_capacity(parent_fragments.len() + 1);
310 new_parent_fragments.extend_from_slice(parent_fragments);
311 new_parent_fragments.push(fragment_name);
312 self.visit_selections_for_fields(
313 fragment_definition.selection_set().iter(),
314 fields,
315 scoped_type,
316 &new_parent_fragments,
317 );
318 }
319 }
320 }
321 }
322 }
323 SelectionReference::InlineFragment(i) => {
324 let scoped_type = match i.type_condition() {
325 Some(type_condition) => {
326 self.schema_definition.get_type_definition(type_condition)
327 }
328 None => Some(parent_type),
329 };
330 if let Some(scoped_type) = scoped_type {
331 if self.selection_set_valid(i.selection_set(), scoped_type) {
332 self.visit_selections_for_fields(
333 i.selection_set().iter(),
334 fields,
335 scoped_type,
336 parent_fragments,
337 );
338 }
339 }
340 }
341 });
342 }
343
344 fn same_output_type_shape(
345 schema_definition: &S,
346 type_a: &S::OutputType,
347 type_b: &S::OutputType,
348 ) -> bool {
349 match (
350 type_a.as_ref(schema_definition),
351 type_b.as_ref(schema_definition),
352 ) {
353 (
354 OutputTypeReference::Base(type_a_base, type_a_required),
355 OutputTypeReference::Base(type_b_base, type_b_required),
356 ) if type_a_required == type_b_required => {
357 !(type_a_base.is_scalar_or_enum() || type_b_base.is_scalar_or_enum())
358 || type_a_base.name() == type_b_base.name()
359 }
360 (
361 OutputTypeReference::List(type_a_inner, type_a_required),
362 OutputTypeReference::List(type_b_inner, type_b_required),
363 ) if type_a_required == type_b_required => {
364 Self::same_output_type_shape(schema_definition, type_a_inner, type_b_inner)
365 }
366 _ => false,
367 }
368 }
369}
370
371impl<'a, E: ExecutableDocument + 'a, S: SchemaDefinition + 'a> Rule<'a, E, S>
372 for FieldSelectionMerging<'a, E, S>
373{
374 type Error = Error<'a, E, S>;
375 type Errors = std::iter::Flatten<
376 std::collections::btree_map::IntoValues<Indexed<'a, E::SelectionSet>, Vec<Error<'a, E, S>>>,
377 >;
378
379 fn into_errors(self) -> Self::Errors {
380 self.cached_errors.into_values().flatten()
381 }
382}
383
384struct FieldContext<'a, E: ExecutableDocument, S: SchemaDefinition> {
385 field: &'a E::Field,
386 field_definition: &'a S::FieldDefinition,
387 parent_type: TypeDefinitionReference<'a, S::TypeDefinition>,
388 parent_fragments: Vec<&'a str>,
389}