apollo_compiler/schema/
from_ast.rs

1use super::*;
2use crate::ast::OperationType;
3use crate::validation::WithErrors;
4use indexmap::map::Entry;
5use std::sync::Arc;
6
7#[derive(Clone)]
8pub struct SchemaBuilder {
9    adopt_orphan_extensions: bool,
10    ignore_builtin_redefinitions: bool,
11    pub(crate) schema: Schema,
12    schema_definition: SchemaDefinitionStatus,
13    orphan_type_extensions: IndexMap<Name, Vec<ast::Definition>>,
14    pub(crate) errors: DiagnosticList,
15}
16
17#[derive(Clone)]
18enum SchemaDefinitionStatus {
19    Found,
20    NoneSoFar {
21        orphan_extensions: Vec<Node<ast::SchemaExtension>>,
22    },
23}
24
25impl Default for SchemaBuilder {
26    fn default() -> Self {
27        Self::new()
28    }
29}
30
31impl SchemaBuilder {
32    pub(crate) fn built_in() -> &'static Self {
33        static BUILT_IN: std::sync::OnceLock<SchemaBuilder> = std::sync::OnceLock::new();
34        BUILT_IN.get_or_init(|| {
35            let mut builder = SchemaBuilder {
36                adopt_orphan_extensions: false,
37                ignore_builtin_redefinitions: false,
38                schema: Schema {
39                    sources: Default::default(),
40                    schema_definition: Node::new(SchemaDefinition {
41                        description: None,
42                        directives: DirectiveList::default(),
43                        query: None,
44                        mutation: None,
45                        subscription: None,
46                    }),
47                    directive_definitions: IndexMap::with_hasher(Default::default()),
48                    types: IndexMap::with_hasher(Default::default()),
49                },
50                schema_definition: SchemaDefinitionStatus::NoneSoFar {
51                    orphan_extensions: Vec::new(),
52                },
53                orphan_type_extensions: IndexMap::with_hasher(Default::default()),
54                errors: DiagnosticList::new(Default::default()),
55            };
56            let input = include_str!("../built_in_types.graphql").to_owned();
57            let path = "built_in.graphql";
58            let id = FileId::BUILT_IN;
59            let ast = ast::Document::parser().parse_ast_inner(input, path, id, &mut builder.errors);
60            let executable_definitions_are_errors = true;
61            builder.add_ast_document(&ast, executable_definitions_are_errors);
62            assert!(builder.errors.is_empty());
63            builder
64        })
65    }
66
67    /// Returns a new schema builder initialized with built-in directives, built-in scalars,
68    /// and introspection types
69    pub fn new() -> Self {
70        Self::built_in().clone()
71    }
72
73    /// Configure the builder so that “orphan” schema extensions and type extensions
74    /// (without a corresponding definition) are “adopted”:
75    /// accepted as if extending an empty definition instead of being rejected as errors.
76    pub fn adopt_orphan_extensions(mut self) -> Self {
77        self.adopt_orphan_extensions = true;
78        self
79    }
80
81    /// Configure the builder to allow SDL to contain built-in type re-definitions.
82    /// Re-definitions are going to be effectively ignored and compiler will continue to use
83    /// built-in GraphQL spec definitions.
84    pub fn ignore_builtin_redefinitions(mut self) -> Self {
85        self.ignore_builtin_redefinitions = true;
86        self
87    }
88
89    /// Parse an input file with the default configuration as an additional input for this schema.
90    ///
91    /// Create a [`Parser`] to use different parser configuration.
92    pub fn parse(mut self, source_text: impl Into<String>, path: impl AsRef<Path>) -> Self {
93        Parser::new().parse_into_schema_builder(source_text, path, &mut self);
94        self
95    }
96
97    /// Add an AST document to the schema being built
98    ///
99    /// Executable definitions, if any, will be silently ignored.
100    pub fn add_ast(mut self, document: &ast::Document) -> Self {
101        let executable_definitions_are_errors = true;
102        self.add_ast_document(document, executable_definitions_are_errors);
103        self
104    }
105
106    pub(crate) fn add_ast_document(
107        &mut self,
108        document: &ast::Document,
109        executable_definitions_are_errors: bool,
110    ) {
111        Arc::make_mut(&mut self.errors.sources)
112            .extend(document.sources.iter().map(|(k, v)| (*k, v.clone())));
113        self.add_ast_document_not_adding_sources(document, executable_definitions_are_errors)
114    }
115
116    pub(crate) fn add_ast_document_not_adding_sources(
117        &mut self,
118        document: &ast::Document,
119        executable_definitions_are_errors: bool,
120    ) {
121        for definition in &document.definitions {
122            macro_rules! type_definition {
123                ($def: ident, $Type: ident, is_scalar = $is_scalar: literal) => {
124                    match self.schema.types.entry($def.name.clone()) {
125                        Entry::Vacant(entry) => {
126                            let extended_def = $Type::from_ast(
127                                &mut self.errors,
128                                $def,
129                                self.orphan_type_extensions
130                                    .shift_remove(&$def.name)
131                                    .unwrap_or_default(),
132                            );
133                            entry.insert(extended_def.into());
134                        }
135                        Entry::Occupied(entry) => {
136                            let previous = entry.get();
137                            if self.ignore_builtin_redefinitions && previous.is_built_in() {
138                                continue;
139                            }
140
141                            if $is_scalar && previous.is_built_in() {
142                                self.errors.push(
143                                    $def.location(),
144                                    BuildError::BuiltInScalarTypeRedefinition,
145                                )
146                            } else {
147                                self.errors.push(
148                                    $def.name.location(),
149                                    BuildError::TypeDefinitionCollision {
150                                        previous_location: previous.name().location(),
151                                        name: $def.name.clone(),
152                                    },
153                                )
154                            }
155                        }
156                    }
157                };
158            }
159            macro_rules! type_extension {
160                ($ext: ident, $Kind: ident) => {
161                    if let Some(ty) = self.schema.types.get_mut(&$ext.name) {
162                        if let ExtendedType::$Kind(ty) = ty {
163                            ty.make_mut().extend_ast(&mut self.errors, $ext)
164                        } else {
165                            self.errors.push(
166                                $ext.name.location(),
167                                BuildError::TypeExtensionKindMismatch {
168                                    name: $ext.name.clone(),
169                                    describe_ext: definition.describe(),
170                                    def_location: ty.name().location(),
171                                    describe_def: ty.describe(),
172                                },
173                            )
174                        }
175                    } else {
176                        self.orphan_type_extensions
177                            .entry($ext.name.clone())
178                            .or_default()
179                            .push(definition.clone())
180                    }
181                };
182            }
183            match definition {
184                ast::Definition::SchemaDefinition(def) => match &self.schema_definition {
185                    SchemaDefinitionStatus::NoneSoFar { orphan_extensions } => {
186                        self.schema.schema_definition =
187                            SchemaDefinition::from_ast(&mut self.errors, def, orphan_extensions);
188                        self.schema_definition = SchemaDefinitionStatus::Found;
189                    }
190                    SchemaDefinitionStatus::Found => self.errors.push(
191                        def.location(),
192                        BuildError::SchemaDefinitionCollision {
193                            previous_location: self.schema.schema_definition.location(),
194                        },
195                    ),
196                },
197                ast::Definition::DirectiveDefinition(def) => {
198                    match self.schema.directive_definitions.entry(def.name.clone()) {
199                        Entry::Vacant(entry) => {
200                            entry.insert(def.clone());
201                        }
202                        Entry::Occupied(mut entry) => {
203                            let previous = entry.get_mut();
204                            if previous.is_built_in() {
205                                // https://github.com/apollographql/apollo-rs/issues/656
206                                // Re-defining a built-in definition is allowed, but only once.
207                                // (`is_built_in` is based on file ID, not directive name,
208                                // so the new definition won’t be considered built-in.)
209                                *previous = def.clone()
210                            } else {
211                                self.errors.push(
212                                    def.name.location(),
213                                    BuildError::DirectiveDefinitionCollision {
214                                        previous_location: previous.name.location(),
215                                        name: def.name.clone(),
216                                    },
217                                )
218                            }
219                        }
220                    }
221                }
222                ast::Definition::ScalarTypeDefinition(def) => {
223                    type_definition!(def, ScalarType, is_scalar = true)
224                }
225                ast::Definition::ObjectTypeDefinition(def) => {
226                    type_definition!(def, ObjectType, is_scalar = false)
227                }
228                ast::Definition::InterfaceTypeDefinition(def) => {
229                    type_definition!(def, InterfaceType, is_scalar = false)
230                }
231                ast::Definition::UnionTypeDefinition(def) => {
232                    type_definition!(def, UnionType, is_scalar = false)
233                }
234                ast::Definition::EnumTypeDefinition(def) => {
235                    type_definition!(def, EnumType, is_scalar = false)
236                }
237                ast::Definition::InputObjectTypeDefinition(def) => {
238                    type_definition!(def, InputObjectType, is_scalar = false)
239                }
240                ast::Definition::SchemaExtension(ext) => match &mut self.schema_definition {
241                    SchemaDefinitionStatus::Found => self
242                        .schema
243                        .schema_definition
244                        .make_mut()
245                        .extend_ast(&mut self.errors, ext),
246                    SchemaDefinitionStatus::NoneSoFar { orphan_extensions } => {
247                        orphan_extensions.push(ext.clone())
248                    }
249                },
250                ast::Definition::ScalarTypeExtension(ext) => type_extension!(ext, Scalar),
251                ast::Definition::ObjectTypeExtension(ext) => type_extension!(ext, Object),
252                ast::Definition::InterfaceTypeExtension(ext) => type_extension!(ext, Interface),
253                ast::Definition::UnionTypeExtension(ext) => type_extension!(ext, Union),
254                ast::Definition::EnumTypeExtension(ext) => type_extension!(ext, Enum),
255                ast::Definition::InputObjectTypeExtension(ext) => type_extension!(ext, InputObject),
256                ast::Definition::OperationDefinition(_)
257                | ast::Definition::FragmentDefinition(_) => {
258                    if executable_definitions_are_errors {
259                        self.errors.push(
260                            definition.location(),
261                            BuildError::ExecutableDefinition {
262                                describe: definition.describe(),
263                            },
264                        )
265                    }
266                }
267            }
268        }
269    }
270
271    /// Returns the schema built from all added documents
272    #[allow(clippy::result_large_err)] // Typically not called very often
273    pub fn build(self) -> Result<Schema, WithErrors<Schema>> {
274        let (schema, errors) = self.build_inner();
275        errors.into_result_with(schema)
276    }
277
278    pub(crate) fn build_inner(self) -> (Schema, DiagnosticList) {
279        let SchemaBuilder {
280            adopt_orphan_extensions,
281            ignore_builtin_redefinitions: _allow_builtin_redefinitions,
282            mut schema,
283            schema_definition,
284            orphan_type_extensions,
285            mut errors,
286        } = self;
287        schema.sources = errors.sources.clone();
288
289        // process orphan type extensions (https://github.com/apollographql/apollo-rs/pull/678) first,
290        // so they can be reflected on the implicit schema definition below
291        if adopt_orphan_extensions {
292            for (type_name, extensions) in orphan_type_extensions {
293                let type_def = adopt_type_extensions(&mut errors, &type_name, &extensions);
294                let previous = schema.types.insert(type_name, type_def);
295                assert!(previous.is_none());
296            }
297        } else {
298            for extensions in orphan_type_extensions.values() {
299                for ext in extensions {
300                    let name = ext.name().unwrap().clone();
301                    errors.push(name.location(), BuildError::OrphanTypeExtension { name })
302                }
303            }
304        }
305
306        match schema_definition {
307            SchemaDefinitionStatus::Found => {}
308            SchemaDefinitionStatus::NoneSoFar { orphan_extensions } => {
309                // This a macro rather than a closure to generate separate `static`s
310                let schema_def = schema.schema_definition.make_mut();
311                if adopt_orphan_extensions {
312                    // https://github.com/apollographql/apollo-rs/pull/678
313                    // In this opt-in mode we unconditionally assume
314                    // an implicit schema definition to extend
315                    for ext in &orphan_extensions {
316                        schema_def.extend_ast(&mut errors, ext)
317                    }
318                    if schema_def.query.is_none()
319                        && schema_def.mutation.is_none()
320                        && schema_def.subscription.is_none()
321                    {
322                        add_implicit_root_types(schema_def, &schema.types);
323                    }
324                } else {
325                    let has_implicit_root_operation =
326                        add_implicit_root_types(schema_def, &schema.types);
327                    if has_implicit_root_operation {
328                        // https://github.com/apollographql/apollo-rs/issues/682
329                        // If we have no explict `schema` definition but do have object type(s)
330                        // with a default type name for root operations,
331                        // an implicit schema definition is generated with those root operations.
332                        // That implict definition can be extended:
333                        for ext in &orphan_extensions {
334                            schema_def.extend_ast(&mut errors, ext)
335                        }
336                    } else {
337                        for ext in &orphan_extensions {
338                            errors.push(ext.location(), BuildError::OrphanSchemaExtension)
339                        }
340                    }
341                }
342            }
343        }
344        (schema, errors)
345    }
346}
347
348fn add_implicit_root_types(
349    schema_def: &mut SchemaDefinition,
350    types: &IndexMap<Name, ExtendedType>,
351) -> bool {
352    let mut has_implicit_root_operation = false;
353    for (operation_type, root_operation) in [
354        (OperationType::Query, &mut schema_def.query),
355        (OperationType::Mutation, &mut schema_def.mutation),
356        (OperationType::Subscription, &mut schema_def.subscription),
357    ] {
358        let name = operation_type.default_type_name();
359        if types.get(&name).is_some_and(|def| def.is_object()) {
360            *root_operation = Some(name.into());
361            has_implicit_root_operation = true
362        }
363    }
364    has_implicit_root_operation
365}
366
367fn adopt_type_extensions(
368    errors: &mut DiagnosticList,
369    type_name: &Name,
370    extensions: &[ast::Definition],
371) -> ExtendedType {
372    macro_rules! extend {
373        ($( $ExtensionVariant: path => $describe: literal $empty_def: expr )+) => {
374            match &extensions[0] {
375                $(
376                    $ExtensionVariant(_) => {
377                        let mut def = $empty_def;
378                        for ext in extensions {
379                            if let $ExtensionVariant(ext) = ext {
380                                def.extend_ast(errors, ext)
381                            } else {
382                                let ext_name = ext.name().unwrap();
383                                errors.push(
384                                    ext_name.location(),
385                                    BuildError::TypeExtensionKindMismatch {
386                                        name: ext_name.clone(),
387                                        describe_ext: ext.describe(),
388                                        def_location: type_name.location(),
389                                        describe_def: $describe,
390                                    }
391                                )
392                            }
393                        }
394                        def.into()
395                    }
396                )+
397                _ => unreachable!(),
398            }
399        };
400    }
401    let name = type_name.clone();
402    extend! {
403        ast::Definition::ScalarTypeExtension => "a scalar type" ScalarType {
404            description: Default::default(),
405            name,
406            directives: Default::default(),
407        }
408        ast::Definition::ObjectTypeExtension => "an object type" ObjectType {
409            description: Default::default(),
410            name,
411            implements_interfaces: Default::default(),
412            directives: Default::default(),
413            fields: Default::default(),
414        }
415        ast::Definition::InterfaceTypeExtension => "an interface type" InterfaceType {
416            description: Default::default(),
417            name,
418            implements_interfaces: Default::default(),
419            directives: Default::default(),
420            fields: Default::default(),
421        }
422        ast::Definition::UnionTypeExtension => "a union type" UnionType {
423            description: Default::default(),
424            name,
425            directives: Default::default(),
426            members: Default::default(),
427        }
428        ast::Definition::EnumTypeExtension => "an enum type" EnumType {
429            description: Default::default(),
430            name,
431            directives: Default::default(),
432            values: Default::default(),
433        }
434        ast::Definition::InputObjectTypeExtension => "an input object type" InputObjectType {
435            description: Default::default(),
436            name,
437            directives: Default::default(),
438            fields: Default::default(),
439        }
440    }
441}
442
443impl SchemaDefinition {
444    fn from_ast(
445        errors: &mut DiagnosticList,
446        definition: &Node<ast::SchemaDefinition>,
447        extensions: &[Node<ast::SchemaExtension>],
448    ) -> Node<Self> {
449        let mut root = Self {
450            description: definition.description.clone(),
451            directives: definition
452                .directives
453                .iter()
454                .map(|d| d.to_component(ComponentOrigin::Definition))
455                .collect(),
456            query: None,
457            mutation: None,
458            subscription: None,
459        };
460        root.add_root_operations(
461            errors,
462            ComponentOrigin::Definition,
463            &definition.root_operations,
464        );
465        for ext in extensions {
466            root.extend_ast(errors, ext)
467        }
468        definition.same_location(root)
469    }
470
471    fn extend_ast(&mut self, errors: &mut DiagnosticList, extension: &Node<ast::SchemaExtension>) {
472        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
473        self.directives.extend(
474            extension
475                .directives
476                .iter()
477                .map(|d| d.to_component(origin.clone())),
478        );
479        self.add_root_operations(errors, origin, &extension.root_operations)
480    }
481
482    fn add_root_operations(
483        &mut self,
484        errors: &mut DiagnosticList,
485        origin: ComponentOrigin,
486        root_operations: &[Node<(OperationType, Name)>],
487    ) {
488        for op in root_operations {
489            let (operation_type, object_type_name) = &**op;
490            let entry = match operation_type {
491                OperationType::Query => &mut self.query,
492                OperationType::Mutation => &mut self.mutation,
493                OperationType::Subscription => &mut self.subscription,
494            };
495            match entry {
496                None => *entry = Some(object_type_name.to_component(origin.clone())),
497                Some(previous) => errors.push(
498                    op.location(),
499                    BuildError::DuplicateRootOperation {
500                        previous_location: previous.location(),
501                        operation_type: operation_type.name(),
502                    },
503                ),
504            }
505        }
506    }
507}
508
509impl ScalarType {
510    fn from_ast(
511        errors: &mut DiagnosticList,
512        definition: &Node<ast::ScalarTypeDefinition>,
513        extensions: Vec<ast::Definition>,
514    ) -> Node<Self> {
515        let mut ty = Self {
516            description: definition.description.clone(),
517            name: definition.name.clone(),
518            directives: definition
519                .directives
520                .iter()
521                .map(|d| d.to_component(ComponentOrigin::Definition))
522                .collect(),
523        };
524        for def in &extensions {
525            if let ast::Definition::ScalarTypeExtension(ext) = def {
526                ty.extend_ast(errors, ext)
527            }
528        }
529        definition.same_location(ty)
530    }
531
532    fn extend_ast(
533        &mut self,
534        _errors: &mut DiagnosticList,
535        extension: &Node<ast::ScalarTypeExtension>,
536    ) {
537        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
538        self.directives.extend(
539            extension
540                .directives
541                .iter()
542                .map(|d| d.to_component(origin.clone())),
543        );
544    }
545}
546
547impl ObjectType {
548    fn from_ast(
549        errors: &mut DiagnosticList,
550        definition: &Node<ast::ObjectTypeDefinition>,
551        extensions: Vec<ast::Definition>,
552    ) -> Node<Self> {
553        let mut ty = Self {
554            description: definition.description.clone(),
555            name: definition.name.clone(),
556            implements_interfaces: collect_sticky_set(
557                definition
558                    .implements_interfaces
559                    .iter()
560                    .map(|name| name.to_component(ComponentOrigin::Definition)),
561                |prev, dup| {
562                    errors.push(
563                        dup.location(),
564                        BuildError::DuplicateImplementsInterfaceInObject {
565                            name_at_previous_location: prev.name.clone(),
566                            type_name: definition.name.clone(),
567                        },
568                    )
569                },
570            ),
571            directives: definition
572                .directives
573                .iter()
574                .map(|d| d.to_component(ComponentOrigin::Definition))
575                .collect(),
576            fields: collect_sticky(
577                definition
578                    .fields
579                    .iter()
580                    .map(|field| (&field.name, field.to_component(ComponentOrigin::Definition))),
581                |prev_key, dup_value| {
582                    errors.push(
583                        dup_value.location(),
584                        BuildError::ObjectFieldNameCollision {
585                            name_at_previous_location: prev_key.clone(),
586                            type_name: definition.name.clone(),
587                        },
588                    )
589                },
590            ),
591        };
592        for def in &extensions {
593            if let ast::Definition::ObjectTypeExtension(ext) = def {
594                ty.extend_ast(errors, ext)
595            }
596        }
597        definition.same_location(ty)
598    }
599
600    fn extend_ast(
601        &mut self,
602        errors: &mut DiagnosticList,
603        extension: &Node<ast::ObjectTypeExtension>,
604    ) {
605        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
606        self.directives.extend(
607            extension
608                .directives
609                .iter()
610                .map(|d| d.to_component(origin.clone())),
611        );
612        extend_sticky_set(
613            &mut self.implements_interfaces,
614            extension
615                .implements_interfaces
616                .iter()
617                .map(|name| name.to_component(origin.clone())),
618            |prev, dup| {
619                errors.push(
620                    dup.location(),
621                    BuildError::DuplicateImplementsInterfaceInObject {
622                        name_at_previous_location: prev.name.clone(),
623                        type_name: extension.name.clone(),
624                    },
625                )
626            },
627        );
628        extend_sticky(
629            &mut self.fields,
630            extension
631                .fields
632                .iter()
633                .map(|field| (&field.name, field.to_component(origin.clone()))),
634            |prev_key, dup_value| {
635                errors.push(
636                    dup_value.location(),
637                    BuildError::ObjectFieldNameCollision {
638                        name_at_previous_location: prev_key.clone(),
639                        type_name: extension.name.clone(),
640                    },
641                )
642            },
643        );
644    }
645}
646
647impl InterfaceType {
648    fn from_ast(
649        errors: &mut DiagnosticList,
650        definition: &Node<ast::InterfaceTypeDefinition>,
651        extensions: Vec<ast::Definition>,
652    ) -> Node<Self> {
653        let mut ty = Self {
654            description: definition.description.clone(),
655            name: definition.name.clone(),
656            implements_interfaces: collect_sticky_set(
657                definition
658                    .implements_interfaces
659                    .iter()
660                    .map(|name| name.to_component(ComponentOrigin::Definition)),
661                |prev, dup| {
662                    errors.push(
663                        dup.location(),
664                        BuildError::DuplicateImplementsInterfaceInInterface {
665                            name_at_previous_location: prev.name.clone(),
666                            type_name: definition.name.clone(),
667                        },
668                    )
669                },
670            ),
671            directives: definition
672                .directives
673                .iter()
674                .map(|d| d.to_component(ComponentOrigin::Definition))
675                .collect(),
676            fields: collect_sticky(
677                definition
678                    .fields
679                    .iter()
680                    .map(|field| (&field.name, field.to_component(ComponentOrigin::Definition))),
681                |prev_key, dup_value| {
682                    errors.push(
683                        dup_value.location(),
684                        BuildError::InterfaceFieldNameCollision {
685                            name_at_previous_location: prev_key.clone(),
686                            type_name: definition.name.clone(),
687                        },
688                    )
689                },
690            ),
691        };
692        for def in &extensions {
693            if let ast::Definition::InterfaceTypeExtension(ext) = def {
694                ty.extend_ast(errors, ext)
695            }
696        }
697        definition.same_location(ty)
698    }
699
700    fn extend_ast(
701        &mut self,
702        errors: &mut DiagnosticList,
703        extension: &Node<ast::InterfaceTypeExtension>,
704    ) {
705        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
706        self.directives.extend(
707            extension
708                .directives
709                .iter()
710                .map(|d| d.to_component(origin.clone())),
711        );
712        extend_sticky_set(
713            &mut self.implements_interfaces,
714            extension
715                .implements_interfaces
716                .iter()
717                .map(|name| name.to_component(origin.clone())),
718            |prev, dup| {
719                errors.push(
720                    dup.location(),
721                    BuildError::DuplicateImplementsInterfaceInInterface {
722                        name_at_previous_location: prev.name.clone(),
723                        type_name: extension.name.clone(),
724                    },
725                )
726            },
727        );
728        extend_sticky(
729            &mut self.fields,
730            extension
731                .fields
732                .iter()
733                .map(|field| (&field.name, field.to_component(origin.clone()))),
734            |prev_key, dup_value| {
735                errors.push(
736                    dup_value.location(),
737                    BuildError::InterfaceFieldNameCollision {
738                        name_at_previous_location: prev_key.clone(),
739                        type_name: extension.name.clone(),
740                    },
741                )
742            },
743        );
744    }
745}
746
747impl UnionType {
748    fn from_ast(
749        errors: &mut DiagnosticList,
750        definition: &Node<ast::UnionTypeDefinition>,
751        extensions: Vec<ast::Definition>,
752    ) -> Node<Self> {
753        let mut ty = Self {
754            description: definition.description.clone(),
755            name: definition.name.clone(),
756            directives: definition
757                .directives
758                .iter()
759                .map(|d| d.to_component(ComponentOrigin::Definition))
760                .collect(),
761            members: collect_sticky_set(
762                definition
763                    .members
764                    .iter()
765                    .map(|name| name.to_component(ComponentOrigin::Definition)),
766                |prev, dup| {
767                    errors.push(
768                        dup.location(),
769                        BuildError::UnionMemberNameCollision {
770                            name_at_previous_location: prev.name.clone(),
771                            type_name: definition.name.clone(),
772                        },
773                    )
774                },
775            ),
776        };
777        for def in &extensions {
778            if let ast::Definition::UnionTypeExtension(ext) = def {
779                ty.extend_ast(errors, ext)
780            }
781        }
782        definition.same_location(ty)
783    }
784
785    fn extend_ast(
786        &mut self,
787        errors: &mut DiagnosticList,
788        extension: &Node<ast::UnionTypeExtension>,
789    ) {
790        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
791        self.directives.extend(
792            extension
793                .directives
794                .iter()
795                .map(|d| d.to_component(origin.clone())),
796        );
797        extend_sticky_set(
798            &mut self.members,
799            extension
800                .members
801                .iter()
802                .map(|name| name.to_component(origin.clone())),
803            |prev, dup| {
804                errors.push(
805                    dup.location(),
806                    BuildError::UnionMemberNameCollision {
807                        name_at_previous_location: prev.name.clone(),
808                        type_name: extension.name.clone(),
809                    },
810                )
811            },
812        );
813    }
814}
815
816impl EnumType {
817    fn from_ast(
818        errors: &mut DiagnosticList,
819        definition: &Node<ast::EnumTypeDefinition>,
820        extensions: Vec<ast::Definition>,
821    ) -> Node<Self> {
822        let mut ty = Self {
823            description: definition.description.clone(),
824            name: definition.name.clone(),
825            directives: definition
826                .directives
827                .iter()
828                .map(|d| d.to_component(ComponentOrigin::Definition))
829                .collect(),
830            values: collect_sticky(
831                definition.values.iter().map(|value_def| {
832                    (
833                        &value_def.value,
834                        value_def.to_component(ComponentOrigin::Definition),
835                    )
836                }),
837                |prev_key, dup_value| {
838                    errors.push(
839                        dup_value.location(),
840                        BuildError::EnumValueNameCollision {
841                            name_at_previous_location: prev_key.clone(),
842                            type_name: definition.name.clone(),
843                        },
844                    )
845                },
846            ),
847        };
848        for def in &extensions {
849            if let ast::Definition::EnumTypeExtension(ext) = def {
850                ty.extend_ast(errors, ext)
851            }
852        }
853        definition.same_location(ty)
854    }
855
856    fn extend_ast(
857        &mut self,
858        errors: &mut DiagnosticList,
859        extension: &Node<ast::EnumTypeExtension>,
860    ) {
861        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
862        self.directives.extend(
863            extension
864                .directives
865                .iter()
866                .map(|d| d.to_component(origin.clone())),
867        );
868        extend_sticky(
869            &mut self.values,
870            extension
871                .values
872                .iter()
873                .map(|value_def| (&value_def.value, value_def.to_component(origin.clone()))),
874            |prev_key, dup_value| {
875                errors.push(
876                    dup_value.location(),
877                    BuildError::EnumValueNameCollision {
878                        name_at_previous_location: prev_key.clone(),
879                        type_name: extension.name.clone(),
880                    },
881                )
882            },
883        )
884    }
885}
886
887impl InputObjectType {
888    fn from_ast(
889        errors: &mut DiagnosticList,
890        definition: &Node<ast::InputObjectTypeDefinition>,
891        extensions: Vec<ast::Definition>,
892    ) -> Node<Self> {
893        let mut ty = Self {
894            description: definition.description.clone(),
895            name: definition.name.clone(),
896            directives: definition
897                .directives
898                .iter()
899                .map(|d| d.to_component(ComponentOrigin::Definition))
900                .collect(),
901            fields: collect_sticky(
902                definition
903                    .fields
904                    .iter()
905                    .map(|field| (&field.name, field.to_component(ComponentOrigin::Definition))),
906                |prev_key, dup_value| {
907                    errors.push(
908                        dup_value.location(),
909                        BuildError::InputFieldNameCollision {
910                            name_at_previous_location: prev_key.clone(),
911                            type_name: definition.name.clone(),
912                        },
913                    )
914                },
915            ),
916        };
917        for def in &extensions {
918            if let ast::Definition::InputObjectTypeExtension(ext) = def {
919                ty.extend_ast(errors, ext)
920            }
921        }
922        definition.same_location(ty)
923    }
924
925    fn extend_ast(
926        &mut self,
927        errors: &mut DiagnosticList,
928        extension: &Node<ast::InputObjectTypeExtension>,
929    ) {
930        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
931        self.directives.extend(
932            extension
933                .directives
934                .iter()
935                .map(|d| d.to_component(origin.clone())),
936        );
937        extend_sticky(
938            &mut self.fields,
939            extension
940                .fields
941                .iter()
942                .map(|field| (&field.name, field.to_component(origin.clone()))),
943            |prev_key, dup_value| {
944                errors.push(
945                    dup_value.location(),
946                    BuildError::InputFieldNameCollision {
947                        name_at_previous_location: prev_key.clone(),
948                        type_name: extension.name.clone(),
949                    },
950                )
951            },
952        )
953    }
954}
955
956/// Like `IndexMap::extend`, but does not replace a value if an equivalent key is already in the map.
957///
958/// On collision, calls `duplicate` with the previous key and the value not inserted
959fn extend_sticky<'a, V>(
960    map: &mut IndexMap<Name, V>,
961    iter: impl IntoIterator<Item = (&'a Name, V)>,
962    mut duplicate: impl FnMut(&Name, V),
963) {
964    for (key, value) in iter.into_iter() {
965        match map.get_key_value(key) {
966            None => {
967                map.insert(key.clone(), value);
968            }
969            Some((prev_key, _)) => duplicate(prev_key, value),
970        }
971    }
972}
973
974/// Like `IndexMap::from_iterator`, but does not replace a value if an equivalent key is already in the map.
975///
976/// On collision, calls `duplicate` with the previous key and the value not inserted
977fn collect_sticky<'a, V>(
978    iter: impl IntoIterator<Item = (&'a Name, V)>,
979    duplicate: impl FnMut(&Name, V),
980) -> IndexMap<Name, V> {
981    let mut map = IndexMap::with_hasher(Default::default());
982    extend_sticky(&mut map, iter, duplicate);
983    map
984}
985
986fn extend_sticky_set(
987    set: &mut IndexSet<ComponentName>,
988    iter: impl IntoIterator<Item = ComponentName>,
989    mut duplicate: impl FnMut(&ComponentName, ComponentName),
990) {
991    for value in iter.into_iter() {
992        match set.get(&value) {
993            None => {
994                set.insert(value);
995            }
996            Some(previous) => duplicate(previous, value),
997        }
998    }
999}
1000fn collect_sticky_set(
1001    iter: impl IntoIterator<Item = ComponentName>,
1002    duplicate: impl FnMut(&ComponentName, ComponentName),
1003) -> IndexSet<ComponentName> {
1004    let mut set = IndexSet::with_hasher(Default::default());
1005    extend_sticky_set(&mut set, iter, duplicate);
1006    set
1007}