Skip to main content

apollo_federation/utils/
normalize_schema.rs

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
15/// For any two [Schema]s that are considered "equal", normalizing them with this function will make
16/// it such that:
17/// 1. They compare as equal via [PartialEq]/[Eq].
18/// 2. They serialize to the same string via [std::fmt::Display].
19///
20/// Schema "equality" in this context is invariant to the order of:
21/// - Schema definitions/extensions, type definitions/extensions, and directive definitions
22/// - Field definitions
23/// - Argument definitions
24/// - Input field definitions
25/// - Enum value definitions
26/// - Root operation types in schema definitions/extensions
27/// - Members in union definitions/extensions
28/// - Implemented interfaces in object/interface definitions/extensions
29/// - Locations in directive definitions
30/// - Directive applications
31/// - Arguments of directive applications
32/// - Input fields in arguments of directive applications
33/// - Input fields in default values of argument/input field definitions
34///
35/// Note that [PartialEq]/[Eq] ignores whether a component comes from a schema/type definition or a
36/// schema/type extension, while [std::fmt::Display] serializes components per-extension.
37/// Accordingly, it may be preferable to serialize via [std::fmt::Display] to check for equality if
38/// component origin is relevant. We support this by specifically sorting component containers (e.g.
39/// directive lists) by content first, and then by component origin (where component origin sort
40/// order is determined by the content with that origin).
41///
42/// Also note that [Schema] uses vectors for (and accordingly [PartialEq]/[Eq] does not ignore the
43/// order of):
44/// - Argument definitions
45/// - Locations in directive definitions
46/// - Directive applications
47/// - Arguments of directive applications
48/// - Input fields in arguments of directive applications
49/// - Input fields in default values of argument/input field definitions
50pub 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
65/// The same as [normalize_schema], but for [Valid] [Schema]s. See that function's doc comment for
66/// details.
67pub fn normalize_valid_schema(schema: Valid<Schema>) -> Normalized<Valid<Schema>> {
68    let schema = normalize_schema(schema.into_inner());
69    // Schema normalization is just sorting, and does not affect GraphQL spec validity.
70    Normalized(Valid::assume_valid(schema.into_inner()))
71}
72
73/// The same as [normalize_schema], but also normalizes all descriptions.
74pub 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/// A marker wrapper that indicates the contained schema has been normalized via [normalize_schema].
134#[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            // Unlike most lists in schemas, order matters for value lists.
893            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
970/// Based on the [PartialOrd] impl for slices.
971fn compare_slices<T>(
972    left: &[T],
973    right: &[T],
974    mut compare: impl FnMut(&T, &T) -> Ordering,
975) -> Ordering {
976    // Slice to the loop iteration range to enable bounds check elimination in the compiler.
977    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
993/// Based on the [PartialOrd] impl for [Option]s.
994fn 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        // Snapshot what the normalized schema should look like.
1283        let normalized_schema = normalize_valid_schema(schema);
1284        let normalized_sdl = normalized_schema.to_string();
1285        insta::assert_snapshot!(normalized_sdl);
1286
1287        // Reparse the schema. Note that:
1288        // 1. At the time of writing this test, parsing-and-printing the normalized schema string
1289        //    results in the same schema string, but this isn't guaranteed in the future as it
1290        //    depends on parsing logic being aligned with printing logic in apollo-compiler, so we
1291        //    don't check that here.
1292        // 2. At the time of writing this test, the printed-and-parsed normalized Schema is not
1293        //    equal to the original normalized Schema. This is due to apollo-compiler parsing
1294        //    populating component containers in order of encountered definitions/extensions, while
1295        //    normalizing reorders component containers by their content. Since this parsing
1296        //    behavior may similarly change in the future, we similarly don't check that here.
1297        let reparsed_schema = Schema::parse_and_validate(&normalized_sdl, "schema.graphql")
1298            .expect("Reparsed test schema unexpectedly invalid.");
1299
1300        // Renormalize the reparsed schema, and confirm it's fully equal.
1301        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        // Normalize the schema with extensions.
1312        let normalized_schema = normalize_valid_schema(schema.clone());
1313        let normalized_sdl = normalized_schema.to_string();
1314
1315        // Normalize the schema without extensions, and confirm it's still equal with PartialEq but
1316        // not with Display.
1317        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        // Remove extensions from the original normalized schema, and confirm it's fully equal.
1328        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}