use super::*;
use indexmap::map::Entry;
pub struct SchemaBuilder {
    schema: Schema,
    orphan_schema_extensions: Vec<Node<ast::SchemaExtension>>,
    orphan_type_extensions: IndexMap<Name, Vec<ast::Definition>>,
}
impl Default for SchemaBuilder {
    fn default() -> Self {
        Self::new()
    }
}
impl SchemaBuilder {
    pub fn new() -> Self {
        let mut builder = SchemaBuilder {
            schema: Schema {
                sources: IndexMap::new(),
                build_errors: Vec::new(),
                schema_definition: None,
                directive_definitions: IndexMap::new(),
                types: IndexMap::new(),
            },
            orphan_schema_extensions: Vec::new(),
            orphan_type_extensions: IndexMap::new(),
        };
        static BUILT_IN_TYPES: std::sync::OnceLock<ast::Document> = std::sync::OnceLock::new();
        let built_in = BUILT_IN_TYPES.get_or_init(|| {
            let input = include_str!("../built_in_types.graphql").to_owned();
            let path = "built_in.graphql".into();
            let id = FileId::BUILT_IN;
            let document = ast::Document::parser().parse_with_file_id(input, path, id);
            debug_assert!(document.check_parse_errors().is_ok());
            document
        });
        let executable_definitions_are_errors = true;
        builder.add_ast_document(built_in, executable_definitions_are_errors);
        debug_assert!(
            builder.schema.build_errors.is_empty()
                && builder.orphan_type_extensions.is_empty()
                && builder.orphan_schema_extensions.is_empty()
                && builder.schema.schema_definition.is_none(),
        );
        builder
    }
    pub fn parse(mut self, source_text: impl Into<String>, path: impl AsRef<Path>) -> Self {
        Parser::new().parse_into_schema_builder(source_text, path, &mut self);
        self
    }
    pub(crate) fn add_ast_document(
        &mut self,
        document: &ast::Document,
        executable_definitions_are_errors: bool,
    ) {
        if let Some((file_id, source_file)) = document.source.clone() {
            self.schema.sources.insert(file_id, source_file);
        }
        for definition in &document.definitions {
            match definition {
                ast::Definition::SchemaDefinition(def) => match &self.schema.schema_definition {
                    None => {
                        self.schema.schema_definition = Some(SchemaDefinition::from_ast(
                            &mut self.schema.build_errors,
                            def,
                            &self.orphan_schema_extensions,
                        ));
                        self.orphan_schema_extensions = Vec::new();
                    }
                    Some(previous) => {
                        self.schema
                            .build_errors
                            .push(BuildError::SchemaDefinitionCollision {
                                location: def.location(),
                                previous_location: previous.location(),
                            })
                    }
                },
                ast::Definition::DirectiveDefinition(def) => {
                    if let Err((_prev_name, previous)) =
                        insert_sticky(&mut self.schema.directive_definitions, &def.name, || {
                            def.clone()
                        })
                    {
                        self.schema
                            .build_errors
                            .push(BuildError::DirectiveDefinitionCollision {
                                location: def.name.location(),
                                previous_location: previous.name.location(),
                                name: def.name.clone(),
                            })
                    }
                }
                ast::Definition::ScalarTypeDefinition(def) => {
                    if let Err((prev_name, previous)) =
                        insert_sticky(&mut self.schema.types, &def.name, || {
                            ExtendedType::Scalar(ScalarType::from_ast(
                                def,
                                self.orphan_type_extensions
                                    .remove(&def.name)
                                    .unwrap_or_default(),
                            ))
                        })
                    {
                        self.schema.build_errors.push(if previous.is_built_in() {
                            BuildError::BuiltInScalarTypeRedefinition {
                                location: def.location(),
                            }
                        } else {
                            BuildError::TypeDefinitionCollision {
                                location: def.name.location(),
                                previous_location: prev_name.location(),
                                name: def.name.clone(),
                            }
                        })
                    }
                }
                ast::Definition::ObjectTypeDefinition(def) => {
                    if let Err((prev_name, _previous)) =
                        insert_sticky(&mut self.schema.types, &def.name, || {
                            ExtendedType::Object(ObjectType::from_ast(
                                &mut self.schema.build_errors,
                                def,
                                self.orphan_type_extensions
                                    .remove(&def.name)
                                    .unwrap_or_default(),
                            ))
                        })
                    {
                        self.schema
                            .build_errors
                            .push(BuildError::TypeDefinitionCollision {
                                location: def.name.location(),
                                previous_location: prev_name.location(),
                                name: def.name.clone(),
                            })
                    }
                }
                ast::Definition::InterfaceTypeDefinition(def) => {
                    if let Err((prev_name, _previous)) =
                        insert_sticky(&mut self.schema.types, &def.name, || {
                            ExtendedType::Interface(InterfaceType::from_ast(
                                &mut self.schema.build_errors,
                                def,
                                self.orphan_type_extensions
                                    .remove(&def.name)
                                    .unwrap_or_default(),
                            ))
                        })
                    {
                        self.schema
                            .build_errors
                            .push(BuildError::TypeDefinitionCollision {
                                location: def.name.location(),
                                previous_location: prev_name.location(),
                                name: def.name.clone(),
                            })
                    }
                }
                ast::Definition::UnionTypeDefinition(def) => {
                    if let Err((prev_name, _)) =
                        insert_sticky(&mut self.schema.types, &def.name, || {
                            ExtendedType::Union(UnionType::from_ast(
                                &mut self.schema.build_errors,
                                def,
                                self.orphan_type_extensions
                                    .remove(&def.name)
                                    .unwrap_or_default(),
                            ))
                        })
                    {
                        self.schema
                            .build_errors
                            .push(BuildError::TypeDefinitionCollision {
                                location: def.name.location(),
                                previous_location: prev_name.location(),
                                name: def.name.clone(),
                            })
                    }
                }
                ast::Definition::EnumTypeDefinition(def) => {
                    if let Err((prev_name, _previous)) =
                        insert_sticky(&mut self.schema.types, &def.name, || {
                            ExtendedType::Enum(EnumType::from_ast(
                                &mut self.schema.build_errors,
                                def,
                                self.orphan_type_extensions
                                    .remove(&def.name)
                                    .unwrap_or_default(),
                            ))
                        })
                    {
                        self.schema
                            .build_errors
                            .push(BuildError::TypeDefinitionCollision {
                                location: def.name.location(),
                                previous_location: prev_name.location(),
                                name: def.name.clone(),
                            })
                    }
                }
                ast::Definition::InputObjectTypeDefinition(def) => {
                    if let Err((prev_name, _previous)) =
                        insert_sticky(&mut self.schema.types, &def.name, || {
                            ExtendedType::InputObject(InputObjectType::from_ast(
                                &mut self.schema.build_errors,
                                def,
                                self.orphan_type_extensions
                                    .remove(&def.name)
                                    .unwrap_or_default(),
                            ))
                        })
                    {
                        self.schema
                            .build_errors
                            .push(BuildError::TypeDefinitionCollision {
                                location: def.name.location(),
                                previous_location: prev_name.location(),
                                name: def.name.clone(),
                            })
                    }
                }
                ast::Definition::SchemaExtension(ext) => {
                    if let Some(root) = &mut self.schema.schema_definition {
                        root.make_mut()
                            .extend_ast(&mut self.schema.build_errors, ext)
                    } else {
                        self.orphan_schema_extensions.push(ext.clone())
                    }
                }
                ast::Definition::ScalarTypeExtension(ext) => {
                    if let Some((_, ty_name, ty)) = self.schema.types.get_full_mut(&ext.name) {
                        if let ExtendedType::Scalar(ty) = ty {
                            ty.make_mut().extend_ast(ext)
                        } else {
                            self.schema
                                .build_errors
                                .push(BuildError::TypeExtensionKindMismatch {
                                    location: ext.name.location(),
                                    name: ext.name.clone(),
                                    describe_ext: definition.describe(),
                                    def_location: ty_name.location(),
                                    describe_def: ty.describe(),
                                })
                        }
                    } else {
                        self.orphan_type_extensions
                            .entry(ext.name.clone())
                            .or_default()
                            .push(definition.clone())
                    }
                }
                ast::Definition::ObjectTypeExtension(ext) => {
                    if let Some((_, ty_name, ty)) = self.schema.types.get_full_mut(&ext.name) {
                        if let ExtendedType::Object(ty) = ty {
                            ty.make_mut().extend_ast(&mut self.schema.build_errors, ext)
                        } else {
                            self.schema
                                .build_errors
                                .push(BuildError::TypeExtensionKindMismatch {
                                    location: ext.name.location(),
                                    name: ext.name.clone(),
                                    describe_ext: definition.describe(),
                                    def_location: ty_name.location(),
                                    describe_def: ty.describe(),
                                })
                        }
                    } else {
                        self.orphan_type_extensions
                            .entry(ext.name.clone())
                            .or_default()
                            .push(definition.clone())
                    }
                }
                ast::Definition::InterfaceTypeExtension(ext) => {
                    if let Some((_, ty_name, ty)) = self.schema.types.get_full_mut(&ext.name) {
                        if let ExtendedType::Interface(ty) = ty {
                            ty.make_mut().extend_ast(&mut self.schema.build_errors, ext)
                        } else {
                            self.schema
                                .build_errors
                                .push(BuildError::TypeExtensionKindMismatch {
                                    location: ext.name.location(),
                                    name: ext.name.clone(),
                                    describe_ext: definition.describe(),
                                    def_location: ty_name.location(),
                                    describe_def: ty.describe(),
                                })
                        }
                    } else {
                        self.orphan_type_extensions
                            .entry(ext.name.clone())
                            .or_default()
                            .push(definition.clone())
                    }
                }
                ast::Definition::UnionTypeExtension(ext) => {
                    if let Some((_, ty_name, ty)) = self.schema.types.get_full_mut(&ext.name) {
                        if let ExtendedType::Union(ty) = ty {
                            ty.make_mut().extend_ast(&mut self.schema.build_errors, ext)
                        } else {
                            self.schema
                                .build_errors
                                .push(BuildError::TypeExtensionKindMismatch {
                                    location: ext.name.location(),
                                    name: ext.name.clone(),
                                    describe_ext: definition.describe(),
                                    def_location: ty_name.location(),
                                    describe_def: ty.describe(),
                                })
                        }
                    } else {
                        self.orphan_type_extensions
                            .entry(ext.name.clone())
                            .or_default()
                            .push(definition.clone())
                    }
                }
                ast::Definition::EnumTypeExtension(ext) => {
                    if let Some((_, ty_name, ty)) = self.schema.types.get_full_mut(&ext.name) {
                        if let ExtendedType::Enum(ty) = ty {
                            ty.make_mut().extend_ast(&mut self.schema.build_errors, ext)
                        } else {
                            self.schema
                                .build_errors
                                .push(BuildError::TypeExtensionKindMismatch {
                                    location: ext.name.location(),
                                    name: ext.name.clone(),
                                    describe_ext: definition.describe(),
                                    def_location: ty_name.location(),
                                    describe_def: ty.describe(),
                                })
                        }
                    } else {
                        self.orphan_type_extensions
                            .entry(ext.name.clone())
                            .or_default()
                            .push(definition.clone())
                    }
                }
                ast::Definition::InputObjectTypeExtension(ext) => {
                    if let Some((_, ty_name, ty)) = self.schema.types.get_full_mut(&ext.name) {
                        if let ExtendedType::InputObject(ty) = ty {
                            ty.make_mut().extend_ast(&mut self.schema.build_errors, ext)
                        } else {
                            self.schema
                                .build_errors
                                .push(BuildError::TypeExtensionKindMismatch {
                                    location: ext.name.location(),
                                    name: ext.name.clone(),
                                    describe_ext: definition.describe(),
                                    def_location: ty_name.location(),
                                    describe_def: ty.describe(),
                                })
                        }
                    } else {
                        self.orphan_type_extensions
                            .entry(ext.name.clone())
                            .or_default()
                            .push(definition.clone())
                    }
                }
                ast::Definition::OperationDefinition(_)
                | ast::Definition::FragmentDefinition(_) => {
                    if executable_definitions_are_errors {
                        self.schema
                            .build_errors
                            .push(BuildError::ExecutableDefinition {
                                location: definition.location(),
                                describe: definition.describe(),
                            })
                    }
                }
            }
        }
    }
    pub fn build(self) -> Schema {
        let SchemaBuilder {
            mut schema,
            orphan_schema_extensions,
            orphan_type_extensions,
        } = self;
        schema
            .build_errors
            .extend(orphan_schema_extensions.into_iter().map(|ext| {
                BuildError::OrphanSchemaExtension {
                    location: ext.location(),
                }
            }));
        schema
            .build_errors
            .extend(orphan_type_extensions.into_values().flatten().map(|ext| {
                let name = ext.name().unwrap().clone();
                BuildError::OrphanTypeExtension {
                    location: name.location(),
                    name,
                }
            }));
        schema
    }
}
impl SchemaDefinition {
    fn from_ast(
        errors: &mut Vec<BuildError>,
        definition: &Node<ast::SchemaDefinition>,
        extensions: &[Node<ast::SchemaExtension>],
    ) -> Node<Self> {
        let mut root = Self {
            description: definition.description.clone(),
            directives: definition
                .directives
                .iter()
                .map(|d| d.to_component(ComponentOrigin::Definition))
                .collect(),
            query: None,
            mutation: None,
            subscription: None,
        };
        root.add_root_operations(
            errors,
            ComponentOrigin::Definition,
            &definition.root_operations,
        );
        for ext in extensions {
            root.extend_ast(errors, ext)
        }
        definition.same_location(root)
    }
    fn extend_ast(&mut self, errors: &mut Vec<BuildError>, extension: &Node<ast::SchemaExtension>) {
        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
        self.directives.extend(
            extension
                .directives
                .iter()
                .map(|d| d.to_component(origin.clone())),
        );
        self.add_root_operations(errors, origin, &extension.root_operations)
    }
    fn add_root_operations(
        &mut self,
        errors: &mut Vec<BuildError>,
        origin: ComponentOrigin,
        root_operations: &[Node<(ast::OperationType, Name)>],
    ) {
        for op in root_operations {
            let (operation_type, object_type_name) = &**op;
            let entry = match operation_type {
                ast::OperationType::Query => &mut self.query,
                ast::OperationType::Mutation => &mut self.mutation,
                ast::OperationType::Subscription => &mut self.subscription,
            };
            match entry {
                None => *entry = Some(object_type_name.to_component(origin.clone())),
                Some(previous) => errors.push(BuildError::DuplicateRootOperation {
                    location: op.location(),
                    previous_location: previous.location(),
                    operation_type: operation_type.name(),
                }),
            }
        }
    }
}
impl ScalarType {
    fn from_ast(
        definition: &Node<ast::ScalarTypeDefinition>,
        extensions: Vec<ast::Definition>,
    ) -> Node<Self> {
        let mut ty = Self {
            description: definition.description.clone(),
            directives: definition
                .directives
                .iter()
                .map(|d| d.to_component(ComponentOrigin::Definition))
                .collect(),
        };
        for def in &extensions {
            if let ast::Definition::ScalarTypeExtension(ext) = def {
                ty.extend_ast(ext)
            }
        }
        definition.same_location(ty)
    }
    fn extend_ast(&mut self, extension: &Node<ast::ScalarTypeExtension>) {
        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
        self.directives.extend(
            extension
                .directives
                .iter()
                .map(|d| d.to_component(origin.clone())),
        );
    }
}
impl ObjectType {
    fn from_ast(
        errors: &mut Vec<BuildError>,
        definition: &Node<ast::ObjectTypeDefinition>,
        extensions: Vec<ast::Definition>,
    ) -> Node<Self> {
        let mut ty = Self {
            description: definition.description.clone(),
            implements_interfaces: collect_sticky_set(
                definition
                    .implements_interfaces
                    .iter()
                    .map(|name| name.to_component(ComponentOrigin::Definition)),
                |prev, dup| {
                    errors.push(BuildError::DuplicateImplementsInterfaceInObject {
                        location: dup.location(),
                        name_at_previous_location: prev.node.clone(),
                        type_name: definition.name.clone(),
                    })
                },
            ),
            directives: definition
                .directives
                .iter()
                .map(|d| d.to_component(ComponentOrigin::Definition))
                .collect(),
            fields: collect_sticky(
                definition
                    .fields
                    .iter()
                    .map(|field| (&field.name, field.to_component(ComponentOrigin::Definition))),
                |prev_key, dup_value| {
                    errors.push(BuildError::ObjectFieldNameCollision {
                        location: dup_value.location(),
                        name_at_previous_location: prev_key.clone(),
                        type_name: definition.name.clone(),
                    })
                },
            ),
        };
        for def in &extensions {
            if let ast::Definition::ObjectTypeExtension(ext) = def {
                ty.extend_ast(errors, ext)
            }
        }
        definition.same_location(ty)
    }
    fn extend_ast(
        &mut self,
        errors: &mut Vec<BuildError>,
        extension: &Node<ast::ObjectTypeExtension>,
    ) {
        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
        self.directives.extend(
            extension
                .directives
                .iter()
                .map(|d| d.to_component(origin.clone())),
        );
        extend_sticky_set(
            &mut self.implements_interfaces,
            extension
                .implements_interfaces
                .iter()
                .map(|name| name.to_component(origin.clone())),
            |prev, dup| {
                errors.push(BuildError::DuplicateImplementsInterfaceInObject {
                    location: dup.location(),
                    name_at_previous_location: prev.node.clone(),
                    type_name: extension.name.clone(),
                })
            },
        );
        extend_sticky(
            &mut self.fields,
            extension
                .fields
                .iter()
                .map(|field| (&field.name, field.to_component(origin.clone()))),
            |prev_key, dup_value| {
                errors.push(BuildError::ObjectFieldNameCollision {
                    location: dup_value.location(),
                    name_at_previous_location: prev_key.clone(),
                    type_name: extension.name.clone(),
                })
            },
        );
    }
}
impl InterfaceType {
    fn from_ast(
        errors: &mut Vec<BuildError>,
        definition: &Node<ast::InterfaceTypeDefinition>,
        extensions: Vec<ast::Definition>,
    ) -> Node<Self> {
        let mut ty = Self {
            description: definition.description.clone(),
            implements_interfaces: collect_sticky_set(
                definition
                    .implements_interfaces
                    .iter()
                    .map(|name| name.to_component(ComponentOrigin::Definition)),
                |prev, dup| {
                    errors.push(BuildError::DuplicateImplementsInterfaceInInterface {
                        location: dup.location(),
                        name_at_previous_location: prev.node.clone(),
                        type_name: definition.name.clone(),
                    })
                },
            ),
            directives: definition
                .directives
                .iter()
                .map(|d| d.to_component(ComponentOrigin::Definition))
                .collect(),
            fields: collect_sticky(
                definition
                    .fields
                    .iter()
                    .map(|field| (&field.name, field.to_component(ComponentOrigin::Definition))),
                |prev_key, dup_value| {
                    errors.push(BuildError::InterfaceFieldNameCollision {
                        location: dup_value.location(),
                        name_at_previous_location: prev_key.clone(),
                        type_name: definition.name.clone(),
                    })
                },
            ),
        };
        for def in &extensions {
            if let ast::Definition::InterfaceTypeExtension(ext) = def {
                ty.extend_ast(errors, ext)
            }
        }
        definition.same_location(ty)
    }
    fn extend_ast(
        &mut self,
        errors: &mut Vec<BuildError>,
        extension: &Node<ast::InterfaceTypeExtension>,
    ) {
        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
        self.directives.extend(
            extension
                .directives
                .iter()
                .map(|d| d.to_component(origin.clone())),
        );
        extend_sticky_set(
            &mut self.implements_interfaces,
            extension
                .implements_interfaces
                .iter()
                .map(|name| name.to_component(origin.clone())),
            |prev, dup| {
                errors.push(BuildError::DuplicateImplementsInterfaceInInterface {
                    location: dup.location(),
                    name_at_previous_location: prev.node.clone(),
                    type_name: extension.name.clone(),
                })
            },
        );
        extend_sticky(
            &mut self.fields,
            extension
                .fields
                .iter()
                .map(|field| (&field.name, field.to_component(origin.clone()))),
            |prev_key, dup_value| {
                errors.push(BuildError::InterfaceFieldNameCollision {
                    location: dup_value.location(),
                    name_at_previous_location: prev_key.clone(),
                    type_name: extension.name.clone(),
                })
            },
        );
    }
}
impl UnionType {
    fn from_ast(
        errors: &mut Vec<BuildError>,
        definition: &Node<ast::UnionTypeDefinition>,
        extensions: Vec<ast::Definition>,
    ) -> Node<Self> {
        let mut ty = Self {
            description: definition.description.clone(),
            directives: definition
                .directives
                .iter()
                .map(|d| d.to_component(ComponentOrigin::Definition))
                .collect(),
            members: collect_sticky_set(
                definition
                    .members
                    .iter()
                    .map(|name| name.to_component(ComponentOrigin::Definition)),
                |prev, dup| {
                    errors.push(BuildError::UnionMemberNameCollision {
                        location: dup.location(),
                        name_at_previous_location: prev.node.clone(),
                        type_name: definition.name.clone(),
                    })
                },
            ),
        };
        for def in &extensions {
            if let ast::Definition::UnionTypeExtension(ext) = def {
                ty.extend_ast(errors, ext)
            }
        }
        definition.same_location(ty)
    }
    fn extend_ast(
        &mut self,
        errors: &mut Vec<BuildError>,
        extension: &Node<ast::UnionTypeExtension>,
    ) {
        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
        self.directives.extend(
            extension
                .directives
                .iter()
                .map(|d| d.to_component(origin.clone())),
        );
        extend_sticky_set(
            &mut self.members,
            extension
                .members
                .iter()
                .map(|name| name.to_component(origin.clone())),
            |prev, dup| {
                errors.push(BuildError::UnionMemberNameCollision {
                    location: dup.location(),
                    name_at_previous_location: prev.node.clone(),
                    type_name: extension.name.clone(),
                })
            },
        );
    }
}
impl EnumType {
    fn from_ast(
        errors: &mut Vec<BuildError>,
        definition: &Node<ast::EnumTypeDefinition>,
        extensions: Vec<ast::Definition>,
    ) -> Node<Self> {
        let mut ty = Self {
            description: definition.description.clone(),
            directives: definition
                .directives
                .iter()
                .map(|d| d.to_component(ComponentOrigin::Definition))
                .collect(),
            values: collect_sticky(
                definition.values.iter().map(|value_def| {
                    (
                        &value_def.value,
                        value_def.to_component(ComponentOrigin::Definition),
                    )
                }),
                |prev_key, dup_value| {
                    errors.push(BuildError::EnumValueNameCollision {
                        location: dup_value.location(),
                        name_at_previous_location: prev_key.clone(),
                        type_name: definition.name.clone(),
                    })
                },
            ),
        };
        for def in &extensions {
            if let ast::Definition::EnumTypeExtension(ext) = def {
                ty.extend_ast(errors, ext)
            }
        }
        definition.same_location(ty)
    }
    fn extend_ast(
        &mut self,
        errors: &mut Vec<BuildError>,
        extension: &Node<ast::EnumTypeExtension>,
    ) {
        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
        self.directives.extend(
            extension
                .directives
                .iter()
                .map(|d| d.to_component(origin.clone())),
        );
        extend_sticky(
            &mut self.values,
            extension
                .values
                .iter()
                .map(|value_def| (&value_def.value, value_def.to_component(origin.clone()))),
            |prev_key, dup_value| {
                errors.push(BuildError::EnumValueNameCollision {
                    location: dup_value.location(),
                    name_at_previous_location: prev_key.clone(),
                    type_name: extension.name.clone(),
                })
            },
        )
    }
}
impl InputObjectType {
    fn from_ast(
        errors: &mut Vec<BuildError>,
        definition: &Node<ast::InputObjectTypeDefinition>,
        extensions: Vec<ast::Definition>,
    ) -> Node<Self> {
        let mut ty = Self {
            description: definition.description.clone(),
            directives: definition
                .directives
                .iter()
                .map(|d| d.to_component(ComponentOrigin::Definition))
                .collect(),
            fields: collect_sticky(
                definition
                    .fields
                    .iter()
                    .map(|field| (&field.name, field.to_component(ComponentOrigin::Definition))),
                |prev_key, dup_value| {
                    errors.push(BuildError::InputFieldNameCollision {
                        location: dup_value.location(),
                        name_at_previous_location: prev_key.clone(),
                        type_name: definition.name.clone(),
                    })
                },
            ),
        };
        for def in &extensions {
            if let ast::Definition::InputObjectTypeExtension(ext) = def {
                ty.extend_ast(errors, ext)
            }
        }
        definition.same_location(ty)
    }
    fn extend_ast(
        &mut self,
        errors: &mut Vec<BuildError>,
        extension: &Node<ast::InputObjectTypeExtension>,
    ) {
        let origin = ComponentOrigin::Extension(ExtensionId::new(extension));
        self.directives.extend(
            extension
                .directives
                .iter()
                .map(|d| d.to_component(origin.clone())),
        );
        extend_sticky(
            &mut self.fields,
            extension
                .fields
                .iter()
                .map(|field| (&field.name, field.to_component(origin.clone()))),
            |prev_key, dup_value| {
                errors.push(BuildError::InputFieldNameCollision {
                    location: dup_value.location(),
                    name_at_previous_location: prev_key.clone(),
                    type_name: extension.name.clone(),
                })
            },
        )
    }
}
fn insert_sticky<'map, V>(
    map: &'map mut IndexMap<Name, V>,
    key: &Name,
    make_value: impl FnOnce() -> V,
) -> Result<(), (&'map Name, &'map V)> {
    match map.entry(key.clone()) {
        Entry::Vacant(entry) => {
            entry.insert(make_value());
            Ok(())
        }
        Entry::Occupied(_) => Err(map.get_key_value(key).unwrap()),
    }
}
fn extend_sticky<'a, V>(
    map: &mut IndexMap<Name, V>,
    iter: impl IntoIterator<Item = (&'a Name, V)>,
    mut duplicate: impl FnMut(&Name, V),
) {
    for (key, value) in iter.into_iter() {
        match map.get_key_value(key) {
            None => {
                map.insert(key.clone(), value);
            }
            Some((prev_key, _)) => duplicate(prev_key, value),
        }
    }
}
fn collect_sticky<'a, V>(
    iter: impl IntoIterator<Item = (&'a Name, V)>,
    duplicate: impl FnMut(&Name, V),
) -> IndexMap<Name, V> {
    let mut map = IndexMap::new();
    extend_sticky(&mut map, iter, duplicate);
    map
}
fn extend_sticky_set(
    set: &mut IndexSet<ComponentStr>,
    iter: impl IntoIterator<Item = ComponentStr>,
    mut duplicate: impl FnMut(&ComponentStr, ComponentStr),
) {
    for value in iter.into_iter() {
        match set.get(&value) {
            None => {
                set.insert(value);
            }
            Some(previous) => duplicate(previous, value),
        }
    }
}
fn collect_sticky_set(
    iter: impl IntoIterator<Item = ComponentStr>,
    duplicate: impl FnMut(&ComponentStr, ComponentStr),
) -> IndexSet<ComponentStr> {
    let mut set = IndexSet::new();
    extend_sticky_set(&mut set, iter, duplicate);
    set
}