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    pub fn iter_orphan_extension_types(&self) -> impl Iterator<Item = &Name> {
272        self.orphan_type_extensions.keys()
273    }
274
275    /// Returns the schema built from all added documents
276    #[allow(clippy::result_large_err)] // Typically not called very often
277    pub fn build(self) -> Result<Schema, WithErrors<Schema>> {
278        let (schema, errors) = self.build_inner();
279        errors.into_result_with(schema)
280    }
281
282    pub(crate) fn build_inner(self) -> (Schema, DiagnosticList) {
283        let SchemaBuilder {
284            adopt_orphan_extensions,
285            ignore_builtin_redefinitions: _allow_builtin_redefinitions,
286            mut schema,
287            schema_definition,
288            orphan_type_extensions,
289            mut errors,
290        } = self;
291        schema.sources = errors.sources.clone();
292
293        // process orphan type extensions (https://github.com/apollographql/apollo-rs/pull/678) first,
294        // so they can be reflected on the implicit schema definition below
295        if adopt_orphan_extensions {
296            for (type_name, extensions) in orphan_type_extensions {
297                let type_def = adopt_type_extensions(&mut errors, &type_name, &extensions);
298                let previous = schema.types.insert(type_name, type_def);
299                assert!(previous.is_none());
300            }
301        } else {
302            for extensions in orphan_type_extensions.values() {
303                for ext in extensions {
304                    let name = ext.name().unwrap().clone();
305                    errors.push(name.location(), BuildError::OrphanTypeExtension { name })
306                }
307            }
308        }
309
310        match schema_definition {
311            SchemaDefinitionStatus::Found => {}
312            SchemaDefinitionStatus::NoneSoFar { orphan_extensions } => {
313                // This a macro rather than a closure to generate separate `static`s
314                let schema_def = schema.schema_definition.make_mut();
315                if adopt_orphan_extensions {
316                    // https://github.com/apollographql/apollo-rs/pull/678
317                    // In this opt-in mode we unconditionally assume
318                    // an implicit schema definition to extend
319                    for ext in &orphan_extensions {
320                        schema_def.extend_ast(&mut errors, ext)
321                    }
322                    if schema_def.query.is_none()
323                        && schema_def.mutation.is_none()
324                        && schema_def.subscription.is_none()
325                    {
326                        add_implicit_root_types(schema_def, &schema.types);
327                    }
328                } else {
329                    let has_implicit_root_operation =
330                        add_implicit_root_types(schema_def, &schema.types);
331                    if has_implicit_root_operation {
332                        // https://github.com/apollographql/apollo-rs/issues/682
333                        // If we have no explict `schema` definition but do have object type(s)
334                        // with a default type name for root operations,
335                        // an implicit schema definition is generated with those root operations.
336                        // That implict definition can be extended:
337                        for ext in &orphan_extensions {
338                            schema_def.extend_ast(&mut errors, ext)
339                        }
340                    } else {
341                        for ext in &orphan_extensions {
342                            errors.push(ext.location(), BuildError::OrphanSchemaExtension)
343                        }
344                    }
345                }
346            }
347        }
348        (schema, errors)
349    }
350}
351
352fn add_implicit_root_types(
353    schema_def: &mut SchemaDefinition,
354    types: &IndexMap<Name, ExtendedType>,
355) -> bool {
356    let mut has_implicit_root_operation = false;
357    for (operation_type, root_operation) in [
358        (OperationType::Query, &mut schema_def.query),
359        (OperationType::Mutation, &mut schema_def.mutation),
360        (OperationType::Subscription, &mut schema_def.subscription),
361    ] {
362        let name = operation_type.default_type_name();
363        if types.get(&name).is_some_and(|def| def.is_object()) {
364            *root_operation = Some(name.into());
365            has_implicit_root_operation = true
366        }
367    }
368    has_implicit_root_operation
369}
370
371fn adopt_type_extensions(
372    errors: &mut DiagnosticList,
373    type_name: &Name,
374    extensions: &[ast::Definition],
375) -> ExtendedType {
376    macro_rules! extend {
377        ($( $ExtensionVariant: path => $describe: literal $empty_def: expr )+) => {
378            match &extensions[0] {
379                $(
380                    $ExtensionVariant(_) => {
381                        let mut def = $empty_def;
382                        for ext in extensions {
383                            if let $ExtensionVariant(ext) = ext {
384                                def.extend_ast(errors, ext)
385                            } else {
386                                let ext_name = ext.name().unwrap();
387                                errors.push(
388                                    ext_name.location(),
389                                    BuildError::TypeExtensionKindMismatch {
390                                        name: ext_name.clone(),
391                                        describe_ext: ext.describe(),
392                                        def_location: type_name.location(),
393                                        describe_def: $describe,
394                                    }
395                                )
396                            }
397                        }
398                        def.into()
399                    }
400                )+
401                _ => unreachable!(),
402            }
403        };
404    }
405    let name = type_name.clone();
406    extend! {
407        ast::Definition::ScalarTypeExtension => "a scalar type" ScalarType {
408            description: Default::default(),
409            name,
410            directives: Default::default(),
411        }
412        ast::Definition::ObjectTypeExtension => "an object type" ObjectType {
413            description: Default::default(),
414            name,
415            implements_interfaces: Default::default(),
416            directives: Default::default(),
417            fields: Default::default(),
418        }
419        ast::Definition::InterfaceTypeExtension => "an interface type" InterfaceType {
420            description: Default::default(),
421            name,
422            implements_interfaces: Default::default(),
423            directives: Default::default(),
424            fields: Default::default(),
425        }
426        ast::Definition::UnionTypeExtension => "a union type" UnionType {
427            description: Default::default(),
428            name,
429            directives: Default::default(),
430            members: Default::default(),
431        }
432        ast::Definition::EnumTypeExtension => "an enum type" EnumType {
433            description: Default::default(),
434            name,
435            directives: Default::default(),
436            values: Default::default(),
437        }
438        ast::Definition::InputObjectTypeExtension => "an input object type" InputObjectType {
439            description: Default::default(),
440            name,
441            directives: Default::default(),
442            fields: Default::default(),
443        }
444    }
445}
446
447impl SchemaDefinition {
448    fn from_ast(
449        errors: &mut DiagnosticList,
450        definition: &Node<ast::SchemaDefinition>,
451        extensions: &[Node<ast::SchemaExtension>],
452    ) -> Node<Self> {
453        let mut root = Self {
454            description: definition.description.clone(),
455            directives: definition
456                .directives
457                .iter()
458                .map(|d| d.to_component(ComponentOrigin::Definition))
459                .collect(),
460            query: None,
461            mutation: None,
462            subscription: None,
463        };
464        root.add_root_operations(
465            errors,
466            ComponentOrigin::Definition,
467            &definition.root_operations,
468        );
469        for ext in extensions {
470            root.extend_ast(errors, ext)
471        }
472        definition.same_location(root)
473    }
474
475    fn extend_ast(&mut self, errors: &mut DiagnosticList, extension: &Node<ast::SchemaExtension>) {
476        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
477        self.directives.extend(
478            extension
479                .directives
480                .iter()
481                .map(|d| d.to_component(origin.clone())),
482        );
483        self.add_root_operations(errors, origin, &extension.root_operations)
484    }
485
486    fn add_root_operations(
487        &mut self,
488        errors: &mut DiagnosticList,
489        origin: ComponentOrigin,
490        root_operations: &[Node<(OperationType, Name)>],
491    ) {
492        for op in root_operations {
493            let (operation_type, object_type_name) = &**op;
494            let entry = match operation_type {
495                OperationType::Query => &mut self.query,
496                OperationType::Mutation => &mut self.mutation,
497                OperationType::Subscription => &mut self.subscription,
498            };
499            match entry {
500                None => *entry = Some(object_type_name.to_component(origin.clone())),
501                Some(previous) => errors.push(
502                    op.location(),
503                    BuildError::DuplicateRootOperation {
504                        previous_location: previous.location(),
505                        operation_type: operation_type.name(),
506                    },
507                ),
508            }
509        }
510    }
511}
512
513impl ScalarType {
514    fn from_ast(
515        errors: &mut DiagnosticList,
516        definition: &Node<ast::ScalarTypeDefinition>,
517        extensions: Vec<ast::Definition>,
518    ) -> Node<Self> {
519        let mut ty = Self {
520            description: definition.description.clone(),
521            name: definition.name.clone(),
522            directives: definition
523                .directives
524                .iter()
525                .map(|d| d.to_component(ComponentOrigin::Definition))
526                .collect(),
527        };
528        for def in &extensions {
529            if let ast::Definition::ScalarTypeExtension(ext) = def {
530                ty.extend_ast(errors, ext)
531            }
532        }
533        definition.same_location(ty)
534    }
535
536    fn extend_ast(
537        &mut self,
538        _errors: &mut DiagnosticList,
539        extension: &Node<ast::ScalarTypeExtension>,
540    ) {
541        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
542        self.directives.extend(
543            extension
544                .directives
545                .iter()
546                .map(|d| d.to_component(origin.clone())),
547        );
548    }
549}
550
551impl ObjectType {
552    fn from_ast(
553        errors: &mut DiagnosticList,
554        definition: &Node<ast::ObjectTypeDefinition>,
555        extensions: Vec<ast::Definition>,
556    ) -> Node<Self> {
557        let mut ty = Self {
558            description: definition.description.clone(),
559            name: definition.name.clone(),
560            implements_interfaces: collect_sticky_set(
561                definition
562                    .implements_interfaces
563                    .iter()
564                    .map(|name| name.to_component(ComponentOrigin::Definition)),
565                |prev, dup| {
566                    errors.push(
567                        dup.location(),
568                        BuildError::DuplicateImplementsInterfaceInObject {
569                            name_at_previous_location: prev.name.clone(),
570                            type_name: definition.name.clone(),
571                        },
572                    )
573                },
574            ),
575            directives: definition
576                .directives
577                .iter()
578                .map(|d| d.to_component(ComponentOrigin::Definition))
579                .collect(),
580            fields: collect_sticky(
581                definition
582                    .fields
583                    .iter()
584                    .map(|field| (&field.name, field.to_component(ComponentOrigin::Definition))),
585                |prev_key, dup_value| {
586                    errors.push(
587                        dup_value.location(),
588                        BuildError::ObjectFieldNameCollision {
589                            name_at_previous_location: prev_key.clone(),
590                            type_name: definition.name.clone(),
591                        },
592                    )
593                },
594            ),
595        };
596        for def in &extensions {
597            if let ast::Definition::ObjectTypeExtension(ext) = def {
598                ty.extend_ast(errors, ext)
599            }
600        }
601        definition.same_location(ty)
602    }
603
604    fn extend_ast(
605        &mut self,
606        errors: &mut DiagnosticList,
607        extension: &Node<ast::ObjectTypeExtension>,
608    ) {
609        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
610        self.directives.extend(
611            extension
612                .directives
613                .iter()
614                .map(|d| d.to_component(origin.clone())),
615        );
616        extend_sticky_set(
617            &mut self.implements_interfaces,
618            extension
619                .implements_interfaces
620                .iter()
621                .map(|name| name.to_component(origin.clone())),
622            |prev, dup| {
623                errors.push(
624                    dup.location(),
625                    BuildError::DuplicateImplementsInterfaceInObject {
626                        name_at_previous_location: prev.name.clone(),
627                        type_name: extension.name.clone(),
628                    },
629                )
630            },
631        );
632        extend_sticky(
633            &mut self.fields,
634            extension
635                .fields
636                .iter()
637                .map(|field| (&field.name, field.to_component(origin.clone()))),
638            |prev_key, dup_value| {
639                errors.push(
640                    dup_value.location(),
641                    BuildError::ObjectFieldNameCollision {
642                        name_at_previous_location: prev_key.clone(),
643                        type_name: extension.name.clone(),
644                    },
645                )
646            },
647        );
648    }
649}
650
651impl InterfaceType {
652    fn from_ast(
653        errors: &mut DiagnosticList,
654        definition: &Node<ast::InterfaceTypeDefinition>,
655        extensions: Vec<ast::Definition>,
656    ) -> Node<Self> {
657        let mut ty = Self {
658            description: definition.description.clone(),
659            name: definition.name.clone(),
660            implements_interfaces: collect_sticky_set(
661                definition
662                    .implements_interfaces
663                    .iter()
664                    .map(|name| name.to_component(ComponentOrigin::Definition)),
665                |prev, dup| {
666                    errors.push(
667                        dup.location(),
668                        BuildError::DuplicateImplementsInterfaceInInterface {
669                            name_at_previous_location: prev.name.clone(),
670                            type_name: definition.name.clone(),
671                        },
672                    )
673                },
674            ),
675            directives: definition
676                .directives
677                .iter()
678                .map(|d| d.to_component(ComponentOrigin::Definition))
679                .collect(),
680            fields: collect_sticky(
681                definition
682                    .fields
683                    .iter()
684                    .map(|field| (&field.name, field.to_component(ComponentOrigin::Definition))),
685                |prev_key, dup_value| {
686                    errors.push(
687                        dup_value.location(),
688                        BuildError::InterfaceFieldNameCollision {
689                            name_at_previous_location: prev_key.clone(),
690                            type_name: definition.name.clone(),
691                        },
692                    )
693                },
694            ),
695        };
696        for def in &extensions {
697            if let ast::Definition::InterfaceTypeExtension(ext) = def {
698                ty.extend_ast(errors, ext)
699            }
700        }
701        definition.same_location(ty)
702    }
703
704    fn extend_ast(
705        &mut self,
706        errors: &mut DiagnosticList,
707        extension: &Node<ast::InterfaceTypeExtension>,
708    ) {
709        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
710        self.directives.extend(
711            extension
712                .directives
713                .iter()
714                .map(|d| d.to_component(origin.clone())),
715        );
716        extend_sticky_set(
717            &mut self.implements_interfaces,
718            extension
719                .implements_interfaces
720                .iter()
721                .map(|name| name.to_component(origin.clone())),
722            |prev, dup| {
723                errors.push(
724                    dup.location(),
725                    BuildError::DuplicateImplementsInterfaceInInterface {
726                        name_at_previous_location: prev.name.clone(),
727                        type_name: extension.name.clone(),
728                    },
729                )
730            },
731        );
732        extend_sticky(
733            &mut self.fields,
734            extension
735                .fields
736                .iter()
737                .map(|field| (&field.name, field.to_component(origin.clone()))),
738            |prev_key, dup_value| {
739                errors.push(
740                    dup_value.location(),
741                    BuildError::InterfaceFieldNameCollision {
742                        name_at_previous_location: prev_key.clone(),
743                        type_name: extension.name.clone(),
744                    },
745                )
746            },
747        );
748    }
749}
750
751impl UnionType {
752    fn from_ast(
753        errors: &mut DiagnosticList,
754        definition: &Node<ast::UnionTypeDefinition>,
755        extensions: Vec<ast::Definition>,
756    ) -> Node<Self> {
757        let mut ty = Self {
758            description: definition.description.clone(),
759            name: definition.name.clone(),
760            directives: definition
761                .directives
762                .iter()
763                .map(|d| d.to_component(ComponentOrigin::Definition))
764                .collect(),
765            members: collect_sticky_set(
766                definition
767                    .members
768                    .iter()
769                    .map(|name| name.to_component(ComponentOrigin::Definition)),
770                |prev, dup| {
771                    errors.push(
772                        dup.location(),
773                        BuildError::UnionMemberNameCollision {
774                            name_at_previous_location: prev.name.clone(),
775                            type_name: definition.name.clone(),
776                        },
777                    )
778                },
779            ),
780        };
781        for def in &extensions {
782            if let ast::Definition::UnionTypeExtension(ext) = def {
783                ty.extend_ast(errors, ext)
784            }
785        }
786        definition.same_location(ty)
787    }
788
789    fn extend_ast(
790        &mut self,
791        errors: &mut DiagnosticList,
792        extension: &Node<ast::UnionTypeExtension>,
793    ) {
794        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
795        self.directives.extend(
796            extension
797                .directives
798                .iter()
799                .map(|d| d.to_component(origin.clone())),
800        );
801        extend_sticky_set(
802            &mut self.members,
803            extension
804                .members
805                .iter()
806                .map(|name| name.to_component(origin.clone())),
807            |prev, dup| {
808                errors.push(
809                    dup.location(),
810                    BuildError::UnionMemberNameCollision {
811                        name_at_previous_location: prev.name.clone(),
812                        type_name: extension.name.clone(),
813                    },
814                )
815            },
816        );
817    }
818}
819
820impl EnumType {
821    fn from_ast(
822        errors: &mut DiagnosticList,
823        definition: &Node<ast::EnumTypeDefinition>,
824        extensions: Vec<ast::Definition>,
825    ) -> Node<Self> {
826        let mut ty = Self {
827            description: definition.description.clone(),
828            name: definition.name.clone(),
829            directives: definition
830                .directives
831                .iter()
832                .map(|d| d.to_component(ComponentOrigin::Definition))
833                .collect(),
834            values: collect_sticky(
835                definition.values.iter().map(|value_def| {
836                    (
837                        &value_def.value,
838                        value_def.to_component(ComponentOrigin::Definition),
839                    )
840                }),
841                |prev_key, dup_value| {
842                    errors.push(
843                        dup_value.location(),
844                        BuildError::EnumValueNameCollision {
845                            name_at_previous_location: prev_key.clone(),
846                            type_name: definition.name.clone(),
847                        },
848                    )
849                },
850            ),
851        };
852        for def in &extensions {
853            if let ast::Definition::EnumTypeExtension(ext) = def {
854                ty.extend_ast(errors, ext)
855            }
856        }
857        definition.same_location(ty)
858    }
859
860    fn extend_ast(
861        &mut self,
862        errors: &mut DiagnosticList,
863        extension: &Node<ast::EnumTypeExtension>,
864    ) {
865        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
866        self.directives.extend(
867            extension
868                .directives
869                .iter()
870                .map(|d| d.to_component(origin.clone())),
871        );
872        extend_sticky(
873            &mut self.values,
874            extension
875                .values
876                .iter()
877                .map(|value_def| (&value_def.value, value_def.to_component(origin.clone()))),
878            |prev_key, dup_value| {
879                errors.push(
880                    dup_value.location(),
881                    BuildError::EnumValueNameCollision {
882                        name_at_previous_location: prev_key.clone(),
883                        type_name: extension.name.clone(),
884                    },
885                )
886            },
887        )
888    }
889}
890
891impl InputObjectType {
892    fn from_ast(
893        errors: &mut DiagnosticList,
894        definition: &Node<ast::InputObjectTypeDefinition>,
895        extensions: Vec<ast::Definition>,
896    ) -> Node<Self> {
897        let mut ty = Self {
898            description: definition.description.clone(),
899            name: definition.name.clone(),
900            directives: definition
901                .directives
902                .iter()
903                .map(|d| d.to_component(ComponentOrigin::Definition))
904                .collect(),
905            fields: collect_sticky(
906                definition
907                    .fields
908                    .iter()
909                    .map(|field| (&field.name, field.to_component(ComponentOrigin::Definition))),
910                |prev_key, dup_value| {
911                    errors.push(
912                        dup_value.location(),
913                        BuildError::InputFieldNameCollision {
914                            name_at_previous_location: prev_key.clone(),
915                            type_name: definition.name.clone(),
916                        },
917                    )
918                },
919            ),
920        };
921        for def in &extensions {
922            if let ast::Definition::InputObjectTypeExtension(ext) = def {
923                ty.extend_ast(errors, ext)
924            }
925        }
926        definition.same_location(ty)
927    }
928
929    fn extend_ast(
930        &mut self,
931        errors: &mut DiagnosticList,
932        extension: &Node<ast::InputObjectTypeExtension>,
933    ) {
934        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
935        self.directives.extend(
936            extension
937                .directives
938                .iter()
939                .map(|d| d.to_component(origin.clone())),
940        );
941        extend_sticky(
942            &mut self.fields,
943            extension
944                .fields
945                .iter()
946                .map(|field| (&field.name, field.to_component(origin.clone()))),
947            |prev_key, dup_value| {
948                errors.push(
949                    dup_value.location(),
950                    BuildError::InputFieldNameCollision {
951                        name_at_previous_location: prev_key.clone(),
952                        type_name: extension.name.clone(),
953                    },
954                )
955            },
956        )
957    }
958}
959
960/// Like `IndexMap::extend`, but does not replace a value if an equivalent key is already in the map.
961///
962/// On collision, calls `duplicate` with the previous key and the value not inserted
963fn extend_sticky<'a, V>(
964    map: &mut IndexMap<Name, V>,
965    iter: impl IntoIterator<Item = (&'a Name, V)>,
966    mut duplicate: impl FnMut(&Name, V),
967) {
968    for (key, value) in iter.into_iter() {
969        match map.get_key_value(key) {
970            None => {
971                map.insert(key.clone(), value);
972            }
973            Some((prev_key, _)) => duplicate(prev_key, value),
974        }
975    }
976}
977
978/// Like `IndexMap::from_iterator`, but does not replace a value if an equivalent key is already in the map.
979///
980/// On collision, calls `duplicate` with the previous key and the value not inserted
981fn collect_sticky<'a, V>(
982    iter: impl IntoIterator<Item = (&'a Name, V)>,
983    duplicate: impl FnMut(&Name, V),
984) -> IndexMap<Name, V> {
985    let mut map = IndexMap::with_hasher(Default::default());
986    extend_sticky(&mut map, iter, duplicate);
987    map
988}
989
990fn extend_sticky_set(
991    set: &mut IndexSet<ComponentName>,
992    iter: impl IntoIterator<Item = ComponentName>,
993    mut duplicate: impl FnMut(&ComponentName, ComponentName),
994) {
995    for value in iter.into_iter() {
996        match set.get(&value) {
997            None => {
998                set.insert(value);
999            }
1000            Some(previous) => duplicate(previous, value),
1001        }
1002    }
1003}
1004fn collect_sticky_set(
1005    iter: impl IntoIterator<Item = ComponentName>,
1006    duplicate: impl FnMut(&ComponentName, ComponentName),
1007) -> IndexSet<ComponentName> {
1008    let mut set = IndexSet::with_hasher(Default::default());
1009    extend_sticky_set(&mut set, iter, duplicate);
1010    set
1011}