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