use crate::ast;
use crate::FileId;
use crate::Node;
use crate::NodeLocation;
use crate::NodeStr;
use crate::Parser;
use indexmap::IndexMap;
use indexmap::IndexSet;
use std::collections::HashMap;
use std::collections::HashSet;
use std::path::Path;
use std::sync::OnceLock;
mod component;
mod from_ast;
mod serialize;
mod validation;
pub use self::component::{Component, ComponentName, ComponentOrigin, ExtensionId};
pub use self::from_ast::SchemaBuilder;
pub use crate::ast::{
    Directive, DirectiveDefinition, DirectiveLocation, EnumValueDefinition, FieldDefinition,
    InputValueDefinition, Name, NamedType, Type, Value,
};
use crate::name;
use crate::validation::DiagnosticList;
#[derive(Clone)]
pub struct Schema {
    pub sources: crate::SourceMap,
    build_errors: Vec<BuildError>,
    pub schema_definition: Node<SchemaDefinition>,
    pub directive_definitions: IndexMap<Name, Node<DirectiveDefinition>>,
    pub types: IndexMap<NamedType, ExtendedType>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct SchemaDefinition {
    pub description: Option<NodeStr>,
    pub directives: DirectiveList,
    pub query: Option<ComponentName>,
    pub mutation: Option<ComponentName>,
    pub subscription: Option<ComponentName>,
}
#[derive(Clone, Eq, PartialEq, Hash, Default)]
pub struct DirectiveList(pub Vec<Component<Directive>>);
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExtendedType {
    Scalar(Node<ScalarType>),
    Object(Node<ObjectType>),
    Interface(Node<InterfaceType>),
    Union(Node<UnionType>),
    Enum(Node<EnumType>),
    InputObject(Node<InputObjectType>),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ScalarType {
    pub description: Option<NodeStr>,
    pub name: Name,
    pub directives: DirectiveList,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ObjectType {
    pub description: Option<NodeStr>,
    pub name: Name,
    pub implements_interfaces: IndexSet<ComponentName>,
    pub directives: DirectiveList,
    pub fields: IndexMap<Name, Component<FieldDefinition>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InterfaceType {
    pub description: Option<NodeStr>,
    pub name: Name,
    pub implements_interfaces: IndexSet<ComponentName>,
    pub directives: DirectiveList,
    pub fields: IndexMap<Name, Component<FieldDefinition>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnionType {
    pub description: Option<NodeStr>,
    pub name: Name,
    pub directives: DirectiveList,
    pub members: IndexSet<ComponentName>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumType {
    pub description: Option<NodeStr>,
    pub name: Name,
    pub directives: DirectiveList,
    pub values: IndexMap<Name, Component<EnumValueDefinition>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InputObjectType {
    pub description: Option<NodeStr>,
    pub name: Name,
    pub directives: DirectiveList,
    pub fields: IndexMap<Name, Component<InputValueDefinition>>,
}
#[derive(thiserror::Error, Debug, Clone)]
pub(crate) enum BuildError {
    #[error("a schema document must not contain {describe}")]
    ExecutableDefinition {
        location: Option<NodeLocation>,
        describe: &'static str,
    },
    #[error("must not have multiple `schema` definitions")]
    SchemaDefinitionCollision {
        location: Option<NodeLocation>,
        previous_location: Option<NodeLocation>,
    },
    #[error("the directive `@{name}` is defined multiple times in the schema")]
    DirectiveDefinitionCollision {
        location: Option<NodeLocation>,
        previous_location: Option<NodeLocation>,
        name: Name,
    },
    #[error("the type `{name}` is defined multiple times in the schema")]
    TypeDefinitionCollision {
        location: Option<NodeLocation>,
        previous_location: Option<NodeLocation>,
        name: Name,
    },
    #[error("built-in scalar definitions must be omitted")]
    BuiltInScalarTypeRedefinition { location: Option<NodeLocation> },
    #[error("schema extension without a schema definition")]
    OrphanSchemaExtension { location: Option<NodeLocation> },
    #[error("type extension for undefined type `{name}`")]
    OrphanTypeExtension {
        location: Option<NodeLocation>,
        name: Name,
    },
    #[error("adding {describe_ext}, but `{name}` is {describe_def}")]
    TypeExtensionKindMismatch {
        location: Option<NodeLocation>,
        name: Name,
        describe_ext: &'static str,
        def_location: Option<NodeLocation>,
        describe_def: &'static str,
    },
    #[error("duplicate definitions for the `{operation_type}` root operation type")]
    DuplicateRootOperation {
        location: Option<NodeLocation>,
        previous_location: Option<NodeLocation>,
        operation_type: &'static str,
    },
    #[error(
        "object type `{type_name}` implements interface `{name_at_previous_location}` \
         more than once"
    )]
    DuplicateImplementsInterfaceInObject {
        location: Option<NodeLocation>,
        name_at_previous_location: Name,
        type_name: Name,
    },
    #[error(
        "interface type `{type_name}` implements interface `{name_at_previous_location}` \
         more than once"
    )]
    DuplicateImplementsInterfaceInInterface {
        location: Option<NodeLocation>,
        name_at_previous_location: Name,
        type_name: Name,
    },
    #[error(
        "duplicate definitions for the `{name_at_previous_location}` \
         field of object type `{type_name}`"
    )]
    ObjectFieldNameCollision {
        location: Option<NodeLocation>,
        name_at_previous_location: Name,
        type_name: Name,
    },
    #[error(
        "duplicate definitions for the `{name_at_previous_location}` \
         field of interface type `{type_name}`"
    )]
    InterfaceFieldNameCollision {
        location: Option<NodeLocation>,
        name_at_previous_location: Name,
        type_name: Name,
    },
    #[error(
        "duplicate definitions for the `{name_at_previous_location}` \
         value of enum type `{type_name}`"
    )]
    EnumValueNameCollision {
        location: Option<NodeLocation>,
        name_at_previous_location: Name,
        type_name: Name,
    },
    #[error(
        "duplicate definitions for the `{name_at_previous_location}` \
         member of union type `{type_name}`"
    )]
    UnionMemberNameCollision {
        location: Option<NodeLocation>,
        name_at_previous_location: Name,
        type_name: Name,
    },
    #[error(
        "duplicate definitions for the `{name_at_previous_location}` \
         field of input object type `{type_name}`"
    )]
    InputFieldNameCollision {
        location: Option<NodeLocation>,
        name_at_previous_location: Name,
        type_name: Name,
    },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FieldLookupError<'schema> {
    NoSuchType,
    NoSuchField(&'schema NamedType, &'schema ExtendedType),
}
impl Schema {
    #[allow(clippy::new_without_default)] pub fn new() -> Self {
        SchemaBuilder::new().build()
    }
    pub fn parse(source_text: impl Into<String>, path: impl AsRef<Path>) -> Self {
        Parser::default().parse_schema(source_text, path)
    }
    pub fn builder() -> SchemaBuilder {
        SchemaBuilder::new()
    }
    pub fn validate(&self) -> Result<DiagnosticList, DiagnosticList> {
        let mut errors = DiagnosticList::new(None, self.sources.clone());
        let warnings_and_advice = validation::validate_schema(&mut errors, self);
        let valid = errors.is_empty();
        for diagnostic in warnings_and_advice {
            errors.push(
                diagnostic.location,
                crate::validation::Details::CompilerDiagnostic(diagnostic),
            )
        }
        errors.sort();
        if valid {
            Ok(errors)
        } else {
            Err(errors)
        }
    }
    pub fn get_scalar(&self, name: &str) -> Option<&Node<ScalarType>> {
        if let Some(ExtendedType::Scalar(ty)) = self.types.get(name) {
            Some(ty)
        } else {
            None
        }
    }
    pub fn get_object(&self, name: &str) -> Option<&Node<ObjectType>> {
        if let Some(ExtendedType::Object(ty)) = self.types.get(name) {
            Some(ty)
        } else {
            None
        }
    }
    pub fn get_interface(&self, name: &str) -> Option<&Node<InterfaceType>> {
        if let Some(ExtendedType::Interface(ty)) = self.types.get(name) {
            Some(ty)
        } else {
            None
        }
    }
    pub fn get_union(&self, name: &str) -> Option<&Node<UnionType>> {
        if let Some(ExtendedType::Union(ty)) = self.types.get(name) {
            Some(ty)
        } else {
            None
        }
    }
    pub fn get_enum(&self, name: &str) -> Option<&Node<EnumType>> {
        if let Some(ExtendedType::Enum(ty)) = self.types.get(name) {
            Some(ty)
        } else {
            None
        }
    }
    pub fn get_input_object(&self, name: &str) -> Option<&Node<InputObjectType>> {
        if let Some(ExtendedType::InputObject(ty)) = self.types.get(name) {
            Some(ty)
        } else {
            None
        }
    }
    pub fn root_operation(&self, operation_type: ast::OperationType) -> Option<&NamedType> {
        match operation_type {
            ast::OperationType::Query => &self.schema_definition.query,
            ast::OperationType::Mutation => &self.schema_definition.mutation,
            ast::OperationType::Subscription => &self.schema_definition.subscription,
        }
        .as_ref()
        .map(|component| &component.name)
    }
    pub fn type_field(
        &self,
        type_name: &str,
        field_name: &str,
    ) -> Result<&Component<FieldDefinition>, FieldLookupError<'_>> {
        let (ty_def_name, ty_def) = self
            .types
            .get_key_value(type_name)
            .ok_or(FieldLookupError::NoSuchType)?;
        self.meta_fields_definitions(type_name)
            .iter()
            .find(|def| def.name == field_name)
            .or_else(|| match ty_def {
                ExtendedType::Object(ty) => ty.fields.get(field_name),
                ExtendedType::Interface(ty) => ty.fields.get(field_name),
                ExtendedType::Scalar(_)
                | ExtendedType::Union(_)
                | ExtendedType::Enum(_)
                | ExtendedType::InputObject(_) => None,
            })
            .ok_or(FieldLookupError::NoSuchField(ty_def_name, ty_def))
    }
    #[doc(hidden)] pub fn implementers_map(&self) -> HashMap<Name, HashSet<Name>> {
        let mut map = HashMap::<Name, HashSet<Name>>::new();
        for (ty_name, ty) in &self.types {
            let interfaces = match ty {
                ExtendedType::Object(def) => &def.implements_interfaces,
                ExtendedType::Interface(def) => &def.implements_interfaces,
                ExtendedType::Scalar(_)
                | ExtendedType::Union(_)
                | ExtendedType::Enum(_)
                | ExtendedType::InputObject(_) => continue,
            };
            for interface in interfaces {
                map.entry(interface.name.clone())
                    .or_default()
                    .insert(ty_name.clone());
            }
        }
        map
    }
    pub fn is_subtype(&self, abstract_type: &str, maybe_subtype: &str) -> bool {
        self.types.get(abstract_type).is_some_and(|ty| match ty {
            ExtendedType::Interface(_) => self.types.get(maybe_subtype).is_some_and(|ty2| {
                match ty2 {
                    ExtendedType::Object(def) => &def.implements_interfaces,
                    ExtendedType::Interface(def) => &def.implements_interfaces,
                    ExtendedType::Scalar(_)
                    | ExtendedType::Union(_)
                    | ExtendedType::Enum(_)
                    | ExtendedType::InputObject(_) => return false,
                }
                .contains(abstract_type)
            }),
            ExtendedType::Union(def) => def.members.contains(maybe_subtype),
            ExtendedType::Scalar(_)
            | ExtendedType::Object(_)
            | ExtendedType::Enum(_)
            | ExtendedType::InputObject(_) => false,
        })
    }
    pub(crate) fn meta_fields_definitions(
        &self,
        type_name: &str,
    ) -> &'static [Component<FieldDefinition>] {
        static ROOT_QUERY_FIELDS: LazyLock<[Component<FieldDefinition>; 3]> = LazyLock::new(|| {
            [
                Component::new(FieldDefinition {
                    description: None,
                    name: name!("__typename"),
                    arguments: Vec::new(),
                    ty: Type::Named(name!("String")).non_null(),
                    directives: ast::DirectiveList::new(),
                }),
                Component::new(FieldDefinition {
                    description: None,
                    name: name!("__schema"),
                    arguments: Vec::new(),
                    ty: Type::Named(name!("__Schema")).non_null(),
                    directives: ast::DirectiveList::new(),
                }),
                Component::new(FieldDefinition {
                    description: None,
                    name: name!("__type"),
                    arguments: vec![InputValueDefinition {
                        description: None,
                        name: name!("name"),
                        ty: ast::Type::Named(name!("String")).non_null().into(),
                        default_value: None,
                        directives: ast::DirectiveList::new(),
                    }
                    .into()],
                    ty: Type::Named(name!("__Type")),
                    directives: ast::DirectiveList::new(),
                }),
            ]
        });
        if self
            .schema_definition
            .query
            .as_ref()
            .is_some_and(|n| n == type_name)
        {
            ROOT_QUERY_FIELDS.get()
        } else {
            std::slice::from_ref(&ROOT_QUERY_FIELDS.get()[0])
        }
    }
    pub fn is_input_type(&self, ty: &Type) -> bool {
        match self.types.get(ty.inner_named_type()) {
            Some(ExtendedType::Scalar(_))
            | Some(ExtendedType::Enum(_))
            | Some(ExtendedType::InputObject(_)) => true,
            Some(ExtendedType::Object(_))
            | Some(ExtendedType::Interface(_))
            | Some(ExtendedType::Union(_))
            | None => false,
        }
    }
    pub fn is_output_type(&self, ty: &Type) -> bool {
        match self.types.get(ty.inner_named_type()) {
            Some(ExtendedType::Scalar(_))
            | Some(ExtendedType::Object(_))
            | Some(ExtendedType::Interface(_))
            | Some(ExtendedType::Union(_))
            | Some(ExtendedType::Enum(_)) => true,
            Some(ExtendedType::InputObject(_)) | None => false,
        }
    }
    serialize_method!();
}
impl SchemaDefinition {
    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
        self.directives
            .iter()
            .flat_map(|dir| dir.origin.extension_id())
            .chain(
                self.query
                    .as_ref()
                    .and_then(|name| name.origin.extension_id()),
            )
            .chain(
                self.mutation
                    .as_ref()
                    .and_then(|name| name.origin.extension_id()),
            )
            .chain(
                self.subscription
                    .as_ref()
                    .and_then(|name| name.origin.extension_id()),
            )
            .collect()
    }
}
impl ExtendedType {
    pub fn name(&self) -> &Name {
        match self {
            Self::Scalar(def) => &def.name,
            Self::Object(def) => &def.name,
            Self::Interface(def) => &def.name,
            Self::Union(def) => &def.name,
            Self::Enum(def) => &def.name,
            Self::InputObject(def) => &def.name,
        }
    }
    pub fn location(&self) -> Option<NodeLocation> {
        match self {
            Self::Scalar(ty) => ty.location(),
            Self::Object(ty) => ty.location(),
            Self::Interface(ty) => ty.location(),
            Self::Union(ty) => ty.location(),
            Self::Enum(ty) => ty.location(),
            Self::InputObject(ty) => ty.location(),
        }
    }
    pub(crate) fn describe(&self) -> &'static str {
        match self {
            Self::Scalar(_) => "a scalar type",
            Self::Object(_) => "an object type",
            Self::Interface(_) => "an interface type",
            Self::Union(_) => "a union type",
            Self::Enum(_) => "an enum type",
            Self::InputObject(_) => "an input object type",
        }
    }
    pub fn is_scalar(&self) -> bool {
        matches!(self, Self::Scalar(_))
    }
    pub fn is_object(&self) -> bool {
        matches!(self, Self::Object(_))
    }
    pub fn is_interface(&self) -> bool {
        matches!(self, Self::Interface(_))
    }
    pub fn is_union(&self) -> bool {
        matches!(self, Self::Union(_))
    }
    pub fn is_enum(&self) -> bool {
        matches!(self, Self::Enum(_))
    }
    pub fn is_input_object(&self) -> bool {
        matches!(self, Self::InputObject(_))
    }
    pub fn is_input_type(&self) -> bool {
        matches!(self, Self::Scalar(_) | Self::Enum(_) | Self::InputObject(_))
    }
    pub fn is_output_type(&self) -> bool {
        matches!(
            self,
            Self::Scalar(_) | Self::Enum(_) | Self::Object(_) | Self::Interface(_) | Self::Union(_)
        )
    }
    pub fn is_built_in(&self) -> bool {
        match self {
            Self::Scalar(ty) => ty.is_built_in(),
            Self::Object(ty) => ty.is_built_in(),
            Self::Interface(ty) => ty.is_built_in(),
            Self::Union(ty) => ty.is_built_in(),
            Self::Enum(ty) => ty.is_built_in(),
            Self::InputObject(ty) => ty.is_built_in(),
        }
    }
    pub fn directives(&self) -> &DirectiveList {
        match self {
            Self::Scalar(ty) => &ty.directives,
            Self::Object(ty) => &ty.directives,
            Self::Interface(ty) => &ty.directives,
            Self::Union(ty) => &ty.directives,
            Self::Enum(ty) => &ty.directives,
            Self::InputObject(ty) => &ty.directives,
        }
    }
    pub fn description(&self) -> Option<&NodeStr> {
        match self {
            Self::Scalar(ty) => ty.description.as_ref(),
            Self::Object(ty) => ty.description.as_ref(),
            Self::Interface(ty) => ty.description.as_ref(),
            Self::Union(ty) => ty.description.as_ref(),
            Self::Enum(ty) => ty.description.as_ref(),
            Self::InputObject(ty) => ty.description.as_ref(),
        }
    }
    serialize_method!();
}
impl ScalarType {
    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
        self.directives
            .iter()
            .flat_map(|dir| dir.origin.extension_id())
            .collect()
    }
    serialize_method!();
}
impl ObjectType {
    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
        self.directives
            .iter()
            .flat_map(|dir| dir.origin.extension_id())
            .chain(
                self.implements_interfaces
                    .iter()
                    .flat_map(|component| component.origin.extension_id()),
            )
            .chain(
                self.fields
                    .values()
                    .flat_map(|field| field.origin.extension_id()),
            )
            .collect()
    }
    serialize_method!();
}
impl InterfaceType {
    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
        self.directives
            .iter()
            .flat_map(|dir| dir.origin.extension_id())
            .chain(
                self.implements_interfaces
                    .iter()
                    .flat_map(|component| component.origin.extension_id()),
            )
            .chain(
                self.fields
                    .values()
                    .flat_map(|field| field.origin.extension_id()),
            )
            .collect()
    }
    serialize_method!();
}
impl UnionType {
    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
        self.directives
            .iter()
            .flat_map(|dir| dir.origin.extension_id())
            .chain(
                self.members
                    .iter()
                    .flat_map(|component| component.origin.extension_id()),
            )
            .collect()
    }
    serialize_method!();
}
impl EnumType {
    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
        self.directives
            .iter()
            .flat_map(|dir| dir.origin.extension_id())
            .chain(
                self.values
                    .values()
                    .flat_map(|value| value.origin.extension_id()),
            )
            .collect()
    }
    serialize_method!();
}
impl InputObjectType {
    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
        self.directives
            .iter()
            .flat_map(|dir| dir.origin.extension_id())
            .chain(
                self.fields
                    .values()
                    .flat_map(|field| field.origin.extension_id()),
            )
            .collect()
    }
    serialize_method!();
}
impl DirectiveList {
    pub const fn new() -> Self {
        Self(Vec::new())
    }
    pub fn get_all<'def: 'name, 'name>(
        &'def self,
        name: &'name str,
    ) -> impl Iterator<Item = &'def Component<Directive>> + 'name {
        self.0.iter().filter(move |dir| dir.name == name)
    }
    pub fn get(&self, name: &str) -> Option<&Component<Directive>> {
        self.get_all(name).next()
    }
    pub fn has(&self, name: &str) -> bool {
        self.get(name).is_some()
    }
    serialize_method!();
}
impl std::fmt::Debug for DirectiveList {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}
impl std::ops::Deref for DirectiveList {
    type Target = Vec<Component<Directive>>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
impl std::ops::DerefMut for DirectiveList {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
impl<'a> IntoIterator for &'a DirectiveList {
    type Item = &'a Component<Directive>;
    type IntoIter = std::slice::Iter<'a, Component<Directive>>;
    fn into_iter(self) -> Self::IntoIter {
        self.0.iter()
    }
}
impl<'a> IntoIterator for &'a mut DirectiveList {
    type Item = &'a mut Component<Directive>;
    type IntoIter = std::slice::IterMut<'a, Component<Directive>>;
    fn into_iter(self) -> Self::IntoIter {
        self.0.iter_mut()
    }
}
impl<D> FromIterator<D> for DirectiveList
where
    D: Into<Component<Directive>>,
{
    fn from_iter<T: IntoIterator<Item = D>>(iter: T) -> Self {
        Self(iter.into_iter().map(Into::into).collect())
    }
}
impl Eq for Schema {}
impl PartialEq for Schema {
    fn eq(&self, other: &Self) -> bool {
        let Self {
            sources: _,      build_errors: _, schema_definition: root_operations,
            directive_definitions,
            types,
        } = self;
        *root_operations == other.schema_definition
            && *directive_definitions == other.directive_definitions
            && *types == other.types
    }
}
impl From<Node<ScalarType>> for ExtendedType {
    fn from(ty: Node<ScalarType>) -> Self {
        Self::Scalar(ty)
    }
}
impl From<Node<ObjectType>> for ExtendedType {
    fn from(ty: Node<ObjectType>) -> Self {
        Self::Object(ty)
    }
}
impl From<Node<InterfaceType>> for ExtendedType {
    fn from(ty: Node<InterfaceType>) -> Self {
        Self::Interface(ty)
    }
}
impl From<Node<UnionType>> for ExtendedType {
    fn from(ty: Node<UnionType>) -> Self {
        Self::Union(ty)
    }
}
impl From<Node<EnumType>> for ExtendedType {
    fn from(ty: Node<EnumType>) -> Self {
        Self::Enum(ty)
    }
}
impl From<Node<InputObjectType>> for ExtendedType {
    fn from(ty: Node<InputObjectType>) -> Self {
        Self::InputObject(ty)
    }
}
impl From<ScalarType> for ExtendedType {
    fn from(ty: ScalarType) -> Self {
        Self::Scalar(ty.into())
    }
}
impl From<ObjectType> for ExtendedType {
    fn from(ty: ObjectType) -> Self {
        Self::Object(ty.into())
    }
}
impl From<InterfaceType> for ExtendedType {
    fn from(ty: InterfaceType) -> Self {
        Self::Interface(ty.into())
    }
}
impl From<UnionType> for ExtendedType {
    fn from(ty: UnionType) -> Self {
        Self::Union(ty.into())
    }
}
impl From<EnumType> for ExtendedType {
    fn from(ty: EnumType) -> Self {
        Self::Enum(ty.into())
    }
}
impl From<InputObjectType> for ExtendedType {
    fn from(ty: InputObjectType) -> Self {
        Self::InputObject(ty.into())
    }
}
impl std::fmt::Debug for Schema {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let Self {
            sources,
            build_errors,
            schema_definition,
            directive_definitions,
            types,
        } = self;
        f.debug_struct("Schema")
            .field("sources", sources)
            .field("build_errors", build_errors)
            .field("schema_definition", schema_definition)
            .field(
                "directive_definitions",
                &DebugDirectiveDefinitions(directive_definitions),
            )
            .field("types", &DebugTypes(types))
            .finish()
    }
}
struct DebugDirectiveDefinitions<'a>(&'a IndexMap<Name, Node<DirectiveDefinition>>);
struct DebugTypes<'a>(&'a IndexMap<Name, ExtendedType>);
impl std::fmt::Debug for DebugDirectiveDefinitions<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut map = f.debug_map();
        for (name, def) in self.0 {
            if !def.is_built_in() {
                map.entry(name, def);
            } else {
                map.entry(name, &format_args!("built_in_directive!({name:?})"));
            }
        }
        map.finish()
    }
}
impl std::fmt::Debug for DebugTypes<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut map = f.debug_map();
        for (name, def) in self.0 {
            if !def.is_built_in() {
                map.entry(name, def);
            } else {
                map.entry(name, &format_args!("built_in_type!({name:?})"));
            }
        }
        map.finish()
    }
}
struct LazyLock<T> {
    value: OnceLock<T>,
    init: fn() -> T,
}
impl<T> LazyLock<T> {
    const fn new(init: fn() -> T) -> Self {
        Self {
            value: OnceLock::new(),
            init,
        }
    }
    fn get(&self) -> &T {
        self.value.get_or_init(self.init)
    }
}