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