Skip to main content

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