1use std::cmp::Ordering;
2
3use apollo_compiler::Name;
4use apollo_compiler::Node;
5use apollo_compiler::Schema;
6use apollo_compiler::collections::IndexMap;
7use apollo_compiler::collections::IndexSet;
8use apollo_compiler::schema::Component;
9use apollo_compiler::schema::ComponentName;
10use apollo_compiler::schema::ComponentOrigin;
11use apollo_compiler::validation::Valid;
12use itertools::Itertools;
13use itertools::kmerge_by;
14
15pub fn normalize_schema(mut schema: Schema) -> Normalized<Schema> {
51 sort_schema_definition(&mut schema.schema_definition);
52 schema
53 .types
54 .values_mut()
55 .for_each(sort_extended_type_definition);
56 schema.types.sort_unstable_keys();
57 schema
58 .directive_definitions
59 .values_mut()
60 .for_each(sort_directive_definition);
61 schema.directive_definitions.sort_unstable_keys();
62 Normalized(schema)
63}
64
65pub fn normalize_valid_schema(schema: Valid<Schema>) -> Normalized<Valid<Schema>> {
68 let schema = normalize_schema(schema.into_inner());
69 Normalized(Valid::assume_valid(schema.into_inner()))
71}
72
73#[derive(Debug, Clone, Eq, PartialEq)]
75#[repr(transparent)]
76pub struct Normalized<T>(T);
77
78impl<T> Normalized<T> {
79 pub fn into_inner(self) -> T {
80 self.0
81 }
82}
83
84impl<T> std::ops::Deref for Normalized<T> {
85 type Target = T;
86
87 fn deref(&self) -> &Self::Target {
88 &self.0
89 }
90}
91
92impl<T> AsRef<T> for Normalized<T> {
93 fn as_ref(&self) -> &T {
94 &self.0
95 }
96}
97
98impl<T: std::fmt::Display> std::fmt::Display for Normalized<T> {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 self.0.fmt(f)
101 }
102}
103
104fn sort_schema_definition(definition: &mut Node<apollo_compiler::schema::SchemaDefinition>) {
105 let definition = definition.make_mut();
106 let grouped_query = group_components_by_origin_and_sort(
107 definition.query.take(),
108 |name| name.origin.clone(),
109 |_| {},
110 compare_component_names,
111 );
112 let grouped_mutation = group_components_by_origin_and_sort(
113 definition.mutation.take(),
114 |name| name.origin.clone(),
115 |_| {},
116 compare_component_names,
117 );
118 let grouped_subscription = group_components_by_origin_and_sort(
119 definition.subscription.take(),
120 |name| name.origin.clone(),
121 |_| {},
122 compare_component_names,
123 );
124 let grouped_directives = group_components_by_origin_and_sort(
125 definition.directives.drain(..),
126 |directive| directive.origin.clone(),
127 sort_component_directive,
128 compare_sorted_component_directives,
129 );
130 let mut origins: IndexSet<ComponentOrigin> = grouped_query
131 .keys()
132 .chain(grouped_mutation.keys())
133 .chain(grouped_subscription.keys())
134 .chain(grouped_directives.keys())
135 .cloned()
136 .collect();
137 sort_origins(&mut origins, |left, right| {
138 match compare_origins_by_sorted_components(
139 left,
140 right,
141 &grouped_query,
142 compare_component_names,
143 ) {
144 Ordering::Equal => (),
145 non_equal => return non_equal,
146 }
147 match compare_origins_by_sorted_components(
148 left,
149 right,
150 &grouped_mutation,
151 compare_component_names,
152 ) {
153 Ordering::Equal => (),
154 non_equal => return non_equal,
155 }
156 match compare_origins_by_sorted_components(
157 left,
158 right,
159 &grouped_subscription,
160 compare_component_names,
161 ) {
162 Ordering::Equal => (),
163 non_equal => return non_equal,
164 }
165 compare_origins_by_sorted_components(
166 left,
167 right,
168 &grouped_directives,
169 compare_sorted_component_directives,
170 )
171 });
172 definition.query = kmerge_sorted_components_and_origins(
173 grouped_query,
174 &origins,
175 |name| &name.origin,
176 compare_component_names,
177 )
178 .next();
179 definition.mutation = kmerge_sorted_components_and_origins(
180 grouped_mutation,
181 &origins,
182 |name| &name.origin,
183 compare_component_names,
184 )
185 .next();
186 definition.subscription = kmerge_sorted_components_and_origins(
187 grouped_subscription,
188 &origins,
189 |name| &name.origin,
190 compare_component_names,
191 )
192 .next();
193 definition
194 .directives
195 .extend(kmerge_sorted_components_and_origins(
196 grouped_directives,
197 &origins,
198 |directive| &directive.origin,
199 compare_sorted_component_directives,
200 ));
201}
202
203fn sort_extended_type_definition(definition: &mut apollo_compiler::schema::ExtendedType) {
204 use apollo_compiler::schema::ExtendedType;
205 match definition {
206 ExtendedType::Scalar(definition) => sort_scalar_type_definition(definition),
207 ExtendedType::Object(definition) => sort_object_type_definition(definition),
208 ExtendedType::Interface(definition) => sort_interface_type_definition(definition),
209 ExtendedType::Union(definition) => sort_union_type_definition(definition),
210 ExtendedType::Enum(definition) => sort_enum_type_definition(definition),
211 ExtendedType::InputObject(definition) => sort_input_object_type_definition(definition),
212 }
213}
214
215fn sort_scalar_type_definition(definition: &mut Node<apollo_compiler::schema::ScalarType>) {
216 let definition = definition.make_mut();
217 let grouped_directives = group_components_by_origin_and_sort(
218 definition.directives.drain(..),
219 |directive| directive.origin.clone(),
220 sort_component_directive,
221 compare_sorted_component_directives,
222 );
223 let mut origins: IndexSet<ComponentOrigin> = grouped_directives.keys().cloned().collect();
224 sort_origins(&mut origins, |left, right| {
225 compare_origins_by_sorted_components(
226 left,
227 right,
228 &grouped_directives,
229 compare_sorted_component_directives,
230 )
231 });
232 definition
233 .directives
234 .extend(kmerge_sorted_components_and_origins(
235 grouped_directives,
236 &origins,
237 |directive| &directive.origin,
238 compare_sorted_component_directives,
239 ));
240}
241
242fn sort_object_type_definition(definition: &mut Node<apollo_compiler::schema::ObjectType>) {
243 let definition = definition.make_mut();
244 let grouped_fields = group_components_by_origin_and_sort(
245 definition.fields.drain(..),
246 |(_, field)| field.origin.clone(),
247 sort_component_field_definition,
248 compare_sorted_component_field_definitions,
249 );
250 let grouped_implements_interfaces = group_components_by_origin_and_sort(
251 definition.implements_interfaces.drain(..),
252 |implements_interface| implements_interface.origin.clone(),
253 |_| {},
254 compare_component_names,
255 );
256 let grouped_directives = group_components_by_origin_and_sort(
257 definition.directives.drain(..),
258 |directive| directive.origin.clone(),
259 sort_component_directive,
260 compare_sorted_component_directives,
261 );
262 let mut origins: IndexSet<ComponentOrigin> = grouped_fields
263 .keys()
264 .chain(grouped_implements_interfaces.keys())
265 .chain(grouped_directives.keys())
266 .cloned()
267 .collect();
268 sort_origins(&mut origins, |left, right| {
269 match compare_origins_by_sorted_components(
270 left,
271 right,
272 &grouped_fields,
273 compare_sorted_component_field_definitions,
274 ) {
275 Ordering::Equal => (),
276 non_equal => return non_equal,
277 }
278 match compare_origins_by_sorted_components(
279 left,
280 right,
281 &grouped_implements_interfaces,
282 compare_component_names,
283 ) {
284 Ordering::Equal => (),
285 non_equal => return non_equal,
286 }
287 compare_origins_by_sorted_components(
288 left,
289 right,
290 &grouped_directives,
291 compare_sorted_component_directives,
292 )
293 });
294 definition
295 .fields
296 .extend(kmerge_sorted_components_and_origins(
297 grouped_fields,
298 &origins,
299 |(_, field)| &field.origin,
300 compare_sorted_component_field_definitions,
301 ));
302 definition
303 .implements_interfaces
304 .extend(kmerge_sorted_components_and_origins(
305 grouped_implements_interfaces,
306 &origins,
307 |implements_interface| &implements_interface.origin,
308 compare_component_names,
309 ));
310 definition
311 .directives
312 .extend(kmerge_sorted_components_and_origins(
313 grouped_directives,
314 &origins,
315 |directive| &directive.origin,
316 compare_sorted_component_directives,
317 ));
318}
319
320fn sort_interface_type_definition(definition: &mut Node<apollo_compiler::schema::InterfaceType>) {
321 let definition = definition.make_mut();
322 let grouped_fields = group_components_by_origin_and_sort(
323 definition.fields.drain(..),
324 |(_, field)| field.origin.clone(),
325 sort_component_field_definition,
326 compare_sorted_component_field_definitions,
327 );
328 let grouped_implements_interfaces = group_components_by_origin_and_sort(
329 definition.implements_interfaces.drain(..),
330 |implements_interface| implements_interface.origin.clone(),
331 |_| {},
332 compare_component_names,
333 );
334 let grouped_directives = group_components_by_origin_and_sort(
335 definition.directives.drain(..),
336 |directive| directive.origin.clone(),
337 sort_component_directive,
338 compare_sorted_component_directives,
339 );
340 let mut origins: IndexSet<ComponentOrigin> = grouped_fields
341 .keys()
342 .chain(grouped_implements_interfaces.keys())
343 .chain(grouped_directives.keys())
344 .cloned()
345 .collect();
346 sort_origins(&mut origins, |left, right| {
347 match compare_origins_by_sorted_components(
348 left,
349 right,
350 &grouped_fields,
351 compare_sorted_component_field_definitions,
352 ) {
353 Ordering::Equal => (),
354 non_equal => return non_equal,
355 }
356 match compare_origins_by_sorted_components(
357 left,
358 right,
359 &grouped_implements_interfaces,
360 compare_component_names,
361 ) {
362 Ordering::Equal => (),
363 non_equal => return non_equal,
364 }
365 compare_origins_by_sorted_components(
366 left,
367 right,
368 &grouped_directives,
369 compare_sorted_component_directives,
370 )
371 });
372 definition
373 .fields
374 .extend(kmerge_sorted_components_and_origins(
375 grouped_fields,
376 &origins,
377 |(_, field)| &field.origin,
378 compare_sorted_component_field_definitions,
379 ));
380 definition
381 .implements_interfaces
382 .extend(kmerge_sorted_components_and_origins(
383 grouped_implements_interfaces,
384 &origins,
385 |implements_interface| &implements_interface.origin,
386 compare_component_names,
387 ));
388 definition
389 .directives
390 .extend(kmerge_sorted_components_and_origins(
391 grouped_directives,
392 &origins,
393 |directive| &directive.origin,
394 compare_sorted_component_directives,
395 ));
396}
397
398fn sort_union_type_definition(definition: &mut Node<apollo_compiler::schema::UnionType>) {
399 let definition = definition.make_mut();
400 let grouped_members = group_components_by_origin_and_sort(
401 definition.members.drain(..),
402 |member| member.origin.clone(),
403 |_| {},
404 compare_component_names,
405 );
406 let grouped_directives = group_components_by_origin_and_sort(
407 definition.directives.drain(..),
408 |directive| directive.origin.clone(),
409 sort_component_directive,
410 compare_sorted_component_directives,
411 );
412 let mut origins: IndexSet<ComponentOrigin> = grouped_members
413 .keys()
414 .chain(grouped_directives.keys())
415 .cloned()
416 .collect();
417 sort_origins(&mut origins, |left, right| {
418 match compare_origins_by_sorted_components(
419 left,
420 right,
421 &grouped_members,
422 compare_component_names,
423 ) {
424 Ordering::Equal => (),
425 non_equal => return non_equal,
426 }
427 compare_origins_by_sorted_components(
428 left,
429 right,
430 &grouped_directives,
431 compare_sorted_component_directives,
432 )
433 });
434 definition
435 .members
436 .extend(kmerge_sorted_components_and_origins(
437 grouped_members,
438 &origins,
439 |member| &member.origin,
440 compare_component_names,
441 ));
442 definition
443 .directives
444 .extend(kmerge_sorted_components_and_origins(
445 grouped_directives,
446 &origins,
447 |directive| &directive.origin,
448 compare_sorted_component_directives,
449 ));
450}
451
452fn sort_enum_type_definition(definition: &mut Node<apollo_compiler::schema::EnumType>) {
453 let definition = definition.make_mut();
454 let grouped_values = group_components_by_origin_and_sort(
455 definition.values.drain(..),
456 |(_, value)| value.origin.clone(),
457 sort_component_enum_value_definition,
458 compare_sorted_component_enum_value_definitions,
459 );
460 let grouped_directives = group_components_by_origin_and_sort(
461 definition.directives.drain(..),
462 |directive| directive.origin.clone(),
463 sort_component_directive,
464 compare_sorted_component_directives,
465 );
466 let mut origins: IndexSet<ComponentOrigin> = grouped_values
467 .keys()
468 .chain(grouped_directives.keys())
469 .cloned()
470 .collect();
471 sort_origins(&mut origins, |left, right| {
472 match compare_origins_by_sorted_components(
473 left,
474 right,
475 &grouped_values,
476 compare_sorted_component_enum_value_definitions,
477 ) {
478 Ordering::Equal => (),
479 non_equal => return non_equal,
480 }
481 compare_origins_by_sorted_components(
482 left,
483 right,
484 &grouped_directives,
485 compare_sorted_component_directives,
486 )
487 });
488 definition
489 .values
490 .extend(kmerge_sorted_components_and_origins(
491 grouped_values,
492 &origins,
493 |(_, value)| &value.origin,
494 compare_sorted_component_enum_value_definitions,
495 ));
496 definition
497 .directives
498 .extend(kmerge_sorted_components_and_origins(
499 grouped_directives,
500 &origins,
501 |directive| &directive.origin,
502 compare_sorted_component_directives,
503 ));
504}
505
506fn sort_input_object_type_definition(
507 definition: &mut Node<apollo_compiler::schema::InputObjectType>,
508) {
509 let definition = definition.make_mut();
510 let grouped_fields = group_components_by_origin_and_sort(
511 definition.fields.drain(..),
512 |(_, field)| field.origin.clone(),
513 sort_component_input_value_definition,
514 compare_sorted_component_input_value_definitions,
515 );
516 let grouped_directives = group_components_by_origin_and_sort(
517 definition.directives.drain(..),
518 |directive| directive.origin.clone(),
519 sort_component_directive,
520 compare_sorted_component_directives,
521 );
522 let mut origins: IndexSet<ComponentOrigin> = grouped_fields
523 .keys()
524 .chain(grouped_directives.keys())
525 .cloned()
526 .collect();
527 sort_origins(&mut origins, |left, right| {
528 match compare_origins_by_sorted_components(
529 left,
530 right,
531 &grouped_fields,
532 compare_sorted_component_input_value_definitions,
533 ) {
534 Ordering::Equal => (),
535 non_equal => return non_equal,
536 }
537 compare_origins_by_sorted_components(
538 left,
539 right,
540 &grouped_directives,
541 compare_sorted_component_directives,
542 )
543 });
544 definition
545 .fields
546 .extend(kmerge_sorted_components_and_origins(
547 grouped_fields,
548 &origins,
549 |(_, value)| &value.origin,
550 compare_sorted_component_input_value_definitions,
551 ));
552 definition
553 .directives
554 .extend(kmerge_sorted_components_and_origins(
555 grouped_directives,
556 &origins,
557 |directive| &directive.origin,
558 compare_sorted_component_directives,
559 ));
560}
561
562fn sort_component_field_definition(
563 definition: &mut (Name, Component<apollo_compiler::schema::FieldDefinition>),
564) {
565 let definition = definition.1.make_mut();
566 sort_slice(
567 &mut definition.arguments,
568 sort_input_value_definition,
569 compare_sorted_input_value_definitions,
570 );
571 sort_slice(
572 &mut definition.directives,
573 sort_directive,
574 compare_sorted_directives,
575 );
576}
577
578fn compare_sorted_component_field_definitions(
579 left: &(Name, Component<apollo_compiler::schema::FieldDefinition>),
580 right: &(Name, Component<apollo_compiler::schema::FieldDefinition>),
581) -> Ordering {
582 let left = &left.1;
583 let right = &right.1;
584 match left.name.cmp(&right.name) {
585 Ordering::Equal => (),
586 non_equal => return non_equal,
587 }
588 match compare_types(&left.ty, &right.ty) {
589 Ordering::Equal => (),
590 non_equal => return non_equal,
591 }
592 match compare_slices(
593 &left.arguments,
594 &right.arguments,
595 compare_sorted_input_value_definitions,
596 ) {
597 Ordering::Equal => (),
598 non_equal => return non_equal,
599 }
600 match compare_slices(
601 &left.directives,
602 &right.directives,
603 compare_sorted_directives,
604 ) {
605 Ordering::Equal => (),
606 non_equal => return non_equal,
607 }
608 compare_options(&left.description, &right.description, compare_descriptions)
609}
610
611fn sort_component_input_value_definition(
612 definition: &mut (Name, Component<apollo_compiler::ast::InputValueDefinition>),
613) {
614 sort_input_value_definition(&mut definition.1);
615}
616
617fn compare_sorted_component_input_value_definitions(
618 left: &(Name, Component<apollo_compiler::ast::InputValueDefinition>),
619 right: &(Name, Component<apollo_compiler::ast::InputValueDefinition>),
620) -> Ordering {
621 compare_sorted_input_value_definitions(&left.1, &right.1)
622}
623
624fn sort_component_enum_value_definition(
625 definition: &mut (Name, Component<apollo_compiler::ast::EnumValueDefinition>),
626) {
627 sort_slice(
628 &mut definition.1.make_mut().directives,
629 sort_directive,
630 compare_sorted_directives,
631 );
632}
633
634fn compare_sorted_component_enum_value_definitions(
635 left: &(Name, Component<apollo_compiler::ast::EnumValueDefinition>),
636 right: &(Name, Component<apollo_compiler::ast::EnumValueDefinition>),
637) -> Ordering {
638 let left = &left.1;
639 let right = &right.1;
640 match left.value.cmp(&right.value) {
641 Ordering::Equal => (),
642 non_equal => return non_equal,
643 }
644 match compare_slices(
645 &left.directives,
646 &right.directives,
647 compare_sorted_directives,
648 ) {
649 Ordering::Equal => (),
650 non_equal => return non_equal,
651 }
652 compare_options(&left.description, &right.description, compare_descriptions)
653}
654
655fn sort_component_directive(directive: &mut Component<apollo_compiler::ast::Directive>) {
656 sort_directive(directive);
657}
658
659fn compare_sorted_component_directives(
660 left: &Component<apollo_compiler::ast::Directive>,
661 right: &Component<apollo_compiler::ast::Directive>,
662) -> Ordering {
663 compare_sorted_directives(left, right)
664}
665
666fn compare_component_names(left: &ComponentName, right: &ComponentName) -> Ordering {
667 left.name.cmp(&right.name)
668}
669
670fn group_components_by_origin_and_sort<T>(
671 iter: impl IntoIterator<Item = T>,
672 mut origin: impl FnMut(&T) -> ComponentOrigin,
673 mut sort: impl FnMut(&mut T),
674 mut compare: impl FnMut(&T, &T) -> Ordering,
675) -> IndexMap<ComponentOrigin, Vec<T>> {
676 iter.into_iter()
677 .chunk_by(|component| origin(component))
678 .into_iter()
679 .map(|(origin, chunk)| {
680 let mut chunk: Vec<T> = chunk.collect();
681 sort_slice(&mut chunk, &mut sort, &mut compare);
682 (origin, chunk)
683 })
684 .collect()
685}
686
687fn sort_origins(
688 origins: &mut IndexSet<ComponentOrigin>,
689 mut compare: impl FnMut(&ComponentOrigin, &ComponentOrigin) -> Ordering,
690) {
691 origins.sort_unstable_by(|left, right| {
692 match (left, right) {
693 (ComponentOrigin::Definition, ComponentOrigin::Extension(_)) => return Ordering::Less,
694 (ComponentOrigin::Extension(_), ComponentOrigin::Definition) => {
695 return Ordering::Greater;
696 }
697 _ => (),
698 }
699 compare(left, right)
700 })
701}
702
703fn compare_origins_by_sorted_components<T>(
704 left: &ComponentOrigin,
705 right: &ComponentOrigin,
706 components: &IndexMap<ComponentOrigin, Vec<T>>,
707 compare: impl FnMut(&T, &T) -> Ordering,
708) -> Ordering {
709 compare_slices(
710 components.get(left).unwrap_or(&vec![]),
711 components.get(right).unwrap_or(&vec![]),
712 compare,
713 )
714}
715
716fn kmerge_sorted_components_and_origins<T>(
717 components: IndexMap<ComponentOrigin, Vec<T>>,
718 origins: &IndexSet<ComponentOrigin>,
719 mut origin: impl FnMut(&T) -> &ComponentOrigin,
720 mut compare: impl FnMut(&T, &T) -> Ordering,
721) -> impl Iterator<Item = T> {
722 kmerge_by(components.into_values(), move |left: &T, right: &T| {
723 match compare(left, right) {
724 Ordering::Equal => (),
725 non_equal => return non_equal == Ordering::Less,
726 }
727 origins
728 .get_index_of(origin(left))
729 .cmp(&origins.get_index_of(origin(right)))
730 == Ordering::Less
731 })
732}
733
734fn sort_directive_definition(definition: &mut Node<apollo_compiler::ast::DirectiveDefinition>) {
735 let definition = definition.make_mut();
736 sort_slice(
737 &mut definition.arguments,
738 sort_input_value_definition,
739 compare_sorted_input_value_definitions,
740 );
741 definition
742 .locations
743 .sort_unstable_by_key(discriminant_directive_location);
744}
745
746fn sort_input_value_definition(definition: &mut Node<apollo_compiler::ast::InputValueDefinition>) {
747 let definition = definition.make_mut();
748 sort_option(&mut definition.default_value, sort_value);
749 sort_slice(
750 &mut definition.directives,
751 sort_directive,
752 compare_sorted_directives,
753 );
754}
755
756fn compare_sorted_input_value_definitions(
757 left: &Node<apollo_compiler::ast::InputValueDefinition>,
758 right: &Node<apollo_compiler::ast::InputValueDefinition>,
759) -> Ordering {
760 match left.name.cmp(&right.name) {
761 Ordering::Equal => (),
762 non_equal => return non_equal,
763 }
764 match compare_types(&left.ty, &right.ty) {
765 Ordering::Equal => (),
766 non_equal => return non_equal,
767 }
768 match compare_options(
769 &left.default_value,
770 &right.default_value,
771 compare_sorted_values,
772 ) {
773 Ordering::Equal => (),
774 non_equal => return non_equal,
775 }
776 match compare_slices(
777 &left.directives,
778 &right.directives,
779 compare_sorted_directives,
780 ) {
781 Ordering::Equal => (),
782 non_equal => return non_equal,
783 }
784 compare_options(&left.description, &right.description, compare_descriptions)
785}
786
787fn sort_directive(directive: &mut Node<apollo_compiler::ast::Directive>) {
788 sort_slice(
789 &mut directive.make_mut().arguments,
790 sort_argument,
791 compare_sorted_arguments,
792 );
793}
794
795fn compare_sorted_directives(
796 left: &Node<apollo_compiler::ast::Directive>,
797 right: &Node<apollo_compiler::ast::Directive>,
798) -> Ordering {
799 match left.name.cmp(&right.name) {
800 Ordering::Equal => (),
801 non_equal => return non_equal,
802 }
803 compare_slices(&left.arguments, &right.arguments, compare_sorted_arguments)
804}
805
806fn sort_argument(argument: &mut Node<apollo_compiler::ast::Argument>) {
807 sort_value(&mut argument.make_mut().value);
808}
809
810fn compare_sorted_arguments(
811 left: &Node<apollo_compiler::ast::Argument>,
812 right: &Node<apollo_compiler::ast::Argument>,
813) -> Ordering {
814 match left.name.cmp(&right.name) {
815 Ordering::Equal => (),
816 non_equal => return non_equal,
817 }
818 compare_sorted_values(&left.value, &right.value)
819}
820
821fn sort_value(value: &mut Node<apollo_compiler::ast::Value>) {
822 use apollo_compiler::ast::Value;
823 match value.make_mut() {
824 Value::Null
825 | Value::Enum(_)
826 | Value::Variable(_)
827 | Value::String(_)
828 | Value::Float(_)
829 | Value::Int(_)
830 | Value::Boolean(_) => {}
831 Value::List(values) => {
832 sort_ordered_slice(values, sort_value);
834 }
835 Value::Object(values) => {
836 sort_slice(values, sort_key_value, compare_sorted_key_values);
837 }
838 }
839}
840
841fn compare_sorted_values(
842 left: &Node<apollo_compiler::ast::Value>,
843 right: &Node<apollo_compiler::ast::Value>,
844) -> Ordering {
845 use apollo_compiler::ast::Value;
846 match (left.as_ref(), &right.as_ref()) {
847 (Value::Null, Value::Null) => Ordering::Equal,
848 (Value::Enum(left), Value::Enum(right)) => left.cmp(right),
849 (Value::Variable(left), Value::Variable(right)) => left.cmp(right),
850 (Value::String(left), Value::String(right)) => left.cmp(right),
851 (Value::Float(left), Value::Float(right)) => left.as_str().cmp(right.as_str()),
852 (Value::Int(left), Value::Int(right)) => left.as_str().cmp(right.as_str()),
853 (Value::Boolean(left), Value::Boolean(right)) => left.cmp(right),
854 (Value::List(left), Value::List(right)) => {
855 compare_slices(left, right, compare_sorted_values)
856 }
857 (Value::Object(left), Value::Object(right)) => {
858 compare_slices(left, right, compare_sorted_key_values)
859 }
860 (left, right) => discriminant_value(left).cmp(&discriminant_value(right)),
861 }
862}
863
864fn sort_key_value(key_value: &mut (Name, Node<apollo_compiler::ast::Value>)) {
865 sort_value(&mut key_value.1)
866}
867
868fn compare_sorted_key_values(
869 left: &(Name, Node<apollo_compiler::ast::Value>),
870 right: &(Name, Node<apollo_compiler::ast::Value>),
871) -> Ordering {
872 match left.0.cmp(&right.0) {
873 Ordering::Equal => (),
874 non_equal => return non_equal,
875 }
876 compare_sorted_values(&left.1, &right.1)
877}
878
879fn compare_types(
880 left: &apollo_compiler::ast::Type,
881 right: &apollo_compiler::ast::Type,
882) -> Ordering {
883 use apollo_compiler::ast::Type;
884 match (left, right) {
885 (Type::Named(left), Type::Named(right)) => left.cmp(right),
886 (Type::NonNullNamed(left), Type::NonNullNamed(right)) => left.cmp(right),
887 (Type::List(left), Type::List(right)) => compare_types(left, right),
888 (Type::NonNullList(left), Type::NonNullList(right)) => compare_types(left, right),
889 (left, right) => discriminant_type(left).cmp(&discriminant_type(right)),
890 }
891}
892
893fn compare_descriptions(left: &Node<str>, right: &Node<str>) -> Ordering {
894 left.cmp(right)
895}
896
897fn sort_slice<T>(
898 slice: &mut [T],
899 sort: impl FnMut(&mut T),
900 compare: impl FnMut(&T, &T) -> Ordering,
901) {
902 sort_ordered_slice(slice, sort);
903 slice.sort_unstable_by(compare);
904}
905
906fn sort_ordered_slice<T>(slice: &mut [T], sort: impl FnMut(&mut T)) {
907 slice.iter_mut().for_each(sort);
908}
909
910fn compare_slices<T>(
912 left: &[T],
913 right: &[T],
914 mut compare: impl FnMut(&T, &T) -> Ordering,
915) -> Ordering {
916 let len_common = std::cmp::min(left.len(), right.len());
918 let left_common = &left[..len_common];
919 let right_common = &right[..len_common];
920 for i in 0..len_common {
921 match compare(&left_common[i], &right_common[i]) {
922 Ordering::Equal => continue,
923 non_equal => return non_equal,
924 }
925 }
926 left.len().cmp(&right.len())
927}
928
929fn sort_option<T>(option: &mut Option<T>, sort: impl FnMut(&mut T)) {
930 option.iter_mut().for_each(sort);
931}
932
933fn compare_options<T>(
935 left: &Option<T>,
936 right: &Option<T>,
937 mut compare: impl FnMut(&T, &T) -> Ordering,
938) -> Ordering {
939 match (left, right) {
940 (Some(left), Some(right)) => compare(left, right),
941 (Some(_), None) => Ordering::Greater,
942 (None, Some(_)) => Ordering::Less,
943 (None, None) => Ordering::Equal,
944 }
945}
946
947fn discriminant_directive_location(location: &apollo_compiler::ast::DirectiveLocation) -> u8 {
948 use apollo_compiler::ast::DirectiveLocation;
949 match location {
950 DirectiveLocation::Query => 0,
951 DirectiveLocation::Mutation => 1,
952 DirectiveLocation::Subscription => 2,
953 DirectiveLocation::Field => 3,
954 DirectiveLocation::FragmentDefinition => 4,
955 DirectiveLocation::FragmentSpread => 5,
956 DirectiveLocation::InlineFragment => 6,
957 DirectiveLocation::VariableDefinition => 7,
958 DirectiveLocation::Schema => 8,
959 DirectiveLocation::Scalar => 9,
960 DirectiveLocation::Object => 10,
961 DirectiveLocation::FieldDefinition => 11,
962 DirectiveLocation::ArgumentDefinition => 12,
963 DirectiveLocation::Interface => 13,
964 DirectiveLocation::Union => 14,
965 DirectiveLocation::Enum => 15,
966 DirectiveLocation::EnumValue => 16,
967 DirectiveLocation::InputObject => 17,
968 DirectiveLocation::InputFieldDefinition => 18,
969 }
970}
971
972fn discriminant_value(value: &apollo_compiler::ast::Value) -> u8 {
973 use apollo_compiler::ast::Value;
974 match value {
975 Value::Null => 0,
976 Value::Enum(_) => 1,
977 Value::Variable(_) => 2,
978 Value::String(_) => 3,
979 Value::Float(_) => 4,
980 Value::Int(_) => 5,
981 Value::Boolean(_) => 6,
982 Value::List(_) => 7,
983 Value::Object(_) => 8,
984 }
985}
986
987fn discriminant_type(ty: &apollo_compiler::ast::Type) -> u8 {
988 use apollo_compiler::ast::Type;
989 match ty {
990 Type::Named(_) => 0,
991 Type::NonNullNamed(_) => 1,
992 Type::List(_) => 2,
993 Type::NonNullList(_) => 3,
994 }
995}
996
997#[cfg(test)]
998mod tests {
999 use apollo_compiler::schema::ExtendedType;
1000
1001 use super::*;
1002
1003 static TEST_SCHEMA: &str = r#"
1004 directive @d2(
1005 a2: I1
1006 a1: String
1007 ) repeatable on
1008 | FIELD_DEFINITION
1009 | SCALAR
1010 | ENUM
1011 | UNION
1012 | SUBSCRIPTION
1013 | ARGUMENT_DEFINITION
1014 | INTERFACE
1015 | FRAGMENT_DEFINITION
1016 | OBJECT
1017 | FIELD
1018 | INPUT_FIELD_DEFINITION
1019 | SCHEMA
1020 | INLINE_FRAGMENT
1021 | QUERY
1022 | INPUT_OBJECT
1023 | ENUM_VALUE
1024
1025 directive @d1(
1026 a1: String
1027 a3: String
1028 a2: I2
1029 ) repeatable on
1030 | ENUM
1031 | INPUT_OBJECT
1032 | SCHEMA
1033 | INTERFACE
1034 | FRAGMENT_SPREAD
1035 | FIELD_DEFINITION
1036 | ENUM_VALUE
1037 | MUTATION
1038 | INPUT_FIELD_DEFINITION
1039 | OBJECT
1040 | VARIABLE_DEFINITION
1041 | SCALAR
1042 | ARGUMENT_DEFINITION
1043 | UNION
1044
1045 extend schema @d1(a1: "0") {
1046 query: T3
1047 }
1048
1049 extend schema @d2(a1: "0") @d1(a1: "2")
1050
1051 schema @d1(a3: "2") {
1052 mutation: T1
1053 }
1054
1055 extend schema @d1(a1: "1") @d2(a1: "0") @d1(a3: "2", a1: "0")
1056
1057 extend schema {
1058 subscription: T2
1059 }
1060
1061 extend schema @d2(a1: "0") @d1(a1: "2")
1062
1063 extend input I1 {
1064 f1: String
1065 f3: [String!]
1066 }
1067
1068 type T3 implements N1 & N2 @d2(a1: "1") {
1069 f2(
1070 a2: I1 @d1(a2: { f3: "ID2", f1: "ID1" }) @d1(a1: "ID0")
1071 a1: I2 @d1(a2: { f3: "ID0", f1: "ID2" }) @d1(a2: { f1: "ID1", f3: "ID1" })
1072 ): T1! @d2(a1: "1") @d1(a1: "1")
1073 }
1074
1075 interface N2 @d2(a1: "1") @d1(a1: "2") {
1076 f3: ID!
1077 }
1078
1079 input I2 {
1080 f2: I1
1081 f3: String
1082 f1: ID
1083 }
1084
1085 extend type T3 implements N3 @d1(a1: "1") {
1086 f3: ID!
1087 f1(a1: Int): String
1088 }
1089
1090 scalar S2
1091
1092 type T1 {
1093 f2(a1: I2): E1
1094 }
1095
1096 extend interface N2 implements N1 @d2(a1: "2") @d1(a1: "1") {
1097 f1(a1: Int): String
1098 }
1099
1100 extend union U1 @d1 @d2 @d1 = T3
1101
1102 input I1 {
1103 f2: I2
1104 }
1105
1106 extend enum E1 @d1(a1: "0") @d1(a1: "0") {
1107 V3
1108 V1
1109 }
1110
1111 union U1 @d2 @d1 @d2 = T1 | T2
1112
1113 interface N3 {
1114 f2(a2: I1, a1: I2): T1
1115 }
1116
1117 scalar S1 @d1(a3: "2")
1118
1119 type T2 {
1120 f1: Float
1121 }
1122
1123 interface N1 {
1124 f3: ID!
1125 }
1126
1127 enum E1 @d1(a1: "0") @d1(a1: "0") {
1128 V2 @d2(a2: { f3: ["2", "1"] }) @d2(a2: { f3: ["1", "3"] })
1129 }
1130 "#;
1131
1132 fn remove_extensions(schema: &mut Schema) {
1133 fn handle_component<T>(component: &mut Component<T>) {
1134 component.origin = ComponentOrigin::Definition
1135 }
1136 fn handle_component_name(component: &mut ComponentName) {
1137 component.origin = ComponentOrigin::Definition
1138 }
1139 fn handle_indexset(components: &mut IndexSet<ComponentName>) {
1140 *components = components
1141 .drain(..)
1142 .map(|mut component| {
1143 handle_component_name(&mut component);
1144 component
1145 })
1146 .collect();
1147 }
1148 let schema_definition = schema.schema_definition.make_mut();
1149 schema_definition
1150 .query
1151 .iter_mut()
1152 .for_each(handle_component_name);
1153 schema_definition
1154 .mutation
1155 .iter_mut()
1156 .for_each(handle_component_name);
1157 schema_definition
1158 .subscription
1159 .iter_mut()
1160 .for_each(handle_component_name);
1161 schema_definition
1162 .directives
1163 .iter_mut()
1164 .for_each(handle_component);
1165 schema
1166 .types
1167 .values_mut()
1168 .for_each(|definition| match definition {
1169 ExtendedType::Scalar(definition) => {
1170 let definition = definition.make_mut();
1171 definition.directives.iter_mut().for_each(handle_component);
1172 }
1173 ExtendedType::Object(definition) => {
1174 let definition = definition.make_mut();
1175 definition.fields.values_mut().for_each(handle_component);
1176 handle_indexset(&mut definition.implements_interfaces);
1177 definition.directives.iter_mut().for_each(handle_component);
1178 }
1179 ExtendedType::Interface(definition) => {
1180 let definition = definition.make_mut();
1181 definition.fields.values_mut().for_each(handle_component);
1182 handle_indexset(&mut definition.implements_interfaces);
1183 definition.directives.iter_mut().for_each(handle_component);
1184 }
1185 ExtendedType::Union(definition) => {
1186 let definition = definition.make_mut();
1187 handle_indexset(&mut definition.members);
1188 definition.directives.iter_mut().for_each(handle_component);
1189 }
1190 ExtendedType::Enum(definition) => {
1191 let definition = definition.make_mut();
1192 definition.values.values_mut().for_each(handle_component);
1193 definition.directives.iter_mut().for_each(handle_component);
1194 }
1195 ExtendedType::InputObject(definition) => {
1196 let definition = definition.make_mut();
1197 definition.fields.values_mut().for_each(handle_component);
1198 definition.directives.iter_mut().for_each(handle_component);
1199 }
1200 })
1201 }
1202
1203 #[test]
1204 fn test_round_trip_equality() {
1205 let schema = Schema::parse_and_validate(TEST_SCHEMA, "schema.graphql")
1206 .expect("Test schema unexpectedly invalid.");
1207
1208 let normalized_schema = normalize_valid_schema(schema);
1210 let normalized_sdl = normalized_schema.to_string();
1211 insta::assert_snapshot!(normalized_sdl);
1212
1213 let reparsed_schema = Schema::parse_and_validate(&normalized_sdl, "schema.graphql")
1224 .expect("Reparsed test schema unexpectedly invalid.");
1225
1226 let normalized_reparsed_schema = normalize_valid_schema(reparsed_schema);
1228 assert_eq!(normalized_reparsed_schema, normalized_schema);
1229 assert_eq!(normalized_reparsed_schema.to_string(), normalized_sdl);
1230 }
1231
1232 #[test]
1233 fn test_extension_equality() {
1234 let schema = Schema::parse_and_validate(TEST_SCHEMA, "schema.graphql")
1235 .expect("Test schema unexpectedly invalid.");
1236
1237 let normalized_schema = normalize_valid_schema(schema.clone());
1239 let normalized_sdl = normalized_schema.to_string();
1240
1241 let mut schema_no_exts = schema.into_inner();
1244 remove_extensions(&mut schema_no_exts);
1245 let schema_no_exts = schema_no_exts
1246 .validate()
1247 .expect("Test schema without extensions unexpectedly invalid.");
1248 let normalized_schema_no_exts = normalize_valid_schema(schema_no_exts);
1249 let normalized_sdl_no_exts = normalized_schema_no_exts.to_string();
1250 assert_eq!(normalized_schema_no_exts, normalized_schema);
1251 assert_ne!(normalized_sdl_no_exts, normalized_sdl);
1252
1253 let mut normalized_schema = normalized_schema.into_inner().into_inner();
1255 remove_extensions(&mut normalized_schema);
1256 let normalized_schema = normalized_schema
1257 .validate()
1258 .expect("Test schema without extensions unexpectedly invalid.");
1259 assert_eq!(&normalized_schema, normalized_schema_no_exts.as_ref());
1260 assert_eq!(normalized_schema.to_string(), normalized_sdl_no_exts);
1261 }
1262}