Skip to main content

apollo_compiler/schema/
mod.rs

1//! High-level representation of a GraphQL type system document a.k.a. schema.
2//!
3//! Compared to an [`ast::Document`] which follows closely the structure of GraphQL syntax,
4//! a [`Schema`] is organized for semantics first:
5//!
6//! * Wherever something is meant to have a unique name (for example fields of a given object type),
7//!   a collection is stored as [`IndexMap<Name, _>`] instead of [`Vec<_>`]
8//!   in order to facilitate lookup by name while preserving source ordering.
9//!
10//! * Everything from [type system extensions] is stored
11//!   together with corresponding “main” definitions,
12//!   while still preserving extension origins with [`Component<_>`].
13//!   so that most consumers don’t need to care about extensions at all,
14//!   (For example, some directives can be applied to an object type extensions to affect
15//!   fields defined in the same extension but not other fields of the object type.)
16//!   See [`Component`].
17//!
18//! [type system extensions]: https://spec.graphql.org/September2025/#sec-Type-System-Extensions
19//!
20//! In some cases like [`SchemaDefinition`], this module and the [`ast`] module
21//! define different Rust types with the same names.
22//! In other cases like [`Directive`] there is no data structure difference needed,
23//! so this module reuses and publicly re-exports some Rust types from the [`ast`] module.
24//!
25//! ## “Build” errors
26//!
27//! As a result of how `Schema` is structured,
28//! not all AST documents (even if filtering out executable definitions) can be fully represented:
29//! creating a `Schema` can cause errors (on top of any potential syntax error)
30//! for cases like name collisions.
31//!
32//! When such errors (or in [`Schema::parse`], syntax errors) happen,
33//! a partial schema is returned together with a list of diagnostics.
34//!
35//! ## Structural sharing and mutation
36//!
37//! Many parts of a `Schema` are reference-counted with [`Node`] (like in AST) or [`Component`].
38//! This allows sharing nodes between documents without cloning entire subtrees.
39//! To modify a node or component,
40//! the [`make_mut`][Node::make_mut] method provides copy-on-write semantics.
41//!
42//! ## Validation
43//!
44//! The [Type System] section of the GraphQL specification defines validation rules
45//! beyond syntax errors and errors detected while constructing a `Schema`.
46//! The [`validate`][Schema::validate] method returns either:
47//!
48//! * An immutable [`Valid<Schema>`] type wrapper, or
49//! * The schema together with a list of diagnostics
50//!
51//! If there is no mutation needed between parsing and validation,
52//! [`Schema::parse_and_validate`] does both in one step.
53//!
54//! [Type System]: https://spec.graphql.org/September2025/#sec-Type-System
55//!
56//! ## Serialization
57//!
58//! [`Schema`] and other types types implement [`Display`][std::fmt::Display]
59//! and [`ToString`] by serializing to GraphQL syntax with a default configuration.
60//! [`serialize`][Schema::serialize] methods return a builder
61//! that has chaining methods for setting serialization configuration,
62//! and also implements `Display` and `ToString`.
63
64use crate::ast;
65use crate::collections::HashMap;
66use crate::collections::IndexMap;
67use crate::collections::IndexSet;
68use crate::name;
69use crate::parser::FileId;
70use crate::parser::Parser;
71use crate::parser::SourceSpan;
72use crate::ty;
73use crate::validation::DiagnosticList;
74use crate::validation::Valid;
75use crate::validation::WithErrors;
76pub use crate::Name;
77use crate::Node;
78use std::path::Path;
79use std::sync::OnceLock;
80
81mod component;
82mod from_ast;
83mod serialize;
84pub(crate) mod validation;
85
86pub use self::component::Component;
87pub use self::component::ComponentName;
88pub use self::component::ComponentOrigin;
89pub use self::component::ExtensionId;
90pub use self::from_ast::SchemaBuilder;
91pub use crate::ast::Directive;
92pub use crate::ast::DirectiveDefinition;
93pub use crate::ast::DirectiveLocation;
94pub use crate::ast::EnumValueDefinition;
95pub use crate::ast::FieldDefinition;
96pub use crate::ast::InputValueDefinition;
97pub use crate::ast::NamedType;
98pub use crate::ast::Type;
99pub use crate::ast::Value;
100
101/// High-level representation of a GraphQL type system document a.k.a. schema.
102#[derive(Clone)]
103pub struct Schema {
104    /// Source files, if any, that were parsed to contribute to this schema.
105    ///
106    /// The schema (including parsed definitions) may have been modified since parsing.
107    pub sources: crate::parser::SourceMap,
108
109    /// The `schema` definition and its extensions, defining root operations
110    pub schema_definition: Node<SchemaDefinition>,
111
112    /// Built-in and explicit directive definitions
113    pub directive_definitions: IndexMap<Name, Node<DirectiveDefinition>>,
114
115    /// Definitions and extensions of all types relevant to a schema:
116    ///
117    /// * Explict types in parsed input files or added programatically.
118    ///
119    /// * [Schema-introspection](https://spec.graphql.org/September2025/#sec-Schema-Introspection)
120    ///   types such as `__Schema`, `__Field`, etc.
121    ///
122    /// * When a `Schema` is initially created or parsed,
123    ///   all [Built-in scalars](https://spec.graphql.org/September2025/#sec-Scalars.Built-in-Scalars).
124    ///   After validation, the Rust `types` map in a `Valid<Schema>` only contains
125    ///   built-in scalar definitions for scalars that are used in the schema.
126    ///   We reflect in this Rust API the behavior of `__Schema.types` in GraphQL introspection.
127    pub types: IndexMap<NamedType, ExtendedType>,
128
129    /// Whether to validate default values of input fields and arguments
130    /// against their types. Defaults to `true`.
131    ///
132    /// Set to `false` via [`SchemaBuilder::validate_default_values`]
133    /// to accept schemas with mistyped default values.
134    pub validate_default_values: bool,
135}
136
137/// The [`schema` definition](https://spec.graphql.org/September2025/#sec-Schema) and its extensions,
138/// defining root operations
139#[derive(Debug, Clone, PartialEq, Eq, Default)]
140pub struct SchemaDefinition {
141    pub description: Option<Node<str>>,
142    pub directives: DirectiveList,
143
144    /// Name of the object type for the `query` root operation
145    pub query: Option<ComponentName>,
146
147    /// Name of the object type for the `mutation` root operation
148    pub mutation: Option<ComponentName>,
149
150    /// Name of the object type for the `subscription` root operation
151    pub subscription: Option<ComponentName>,
152}
153
154/// The list of [_Directives_](https://spec.graphql.org/September2025/#Directives)
155/// of a GraphQL type or `schema`, each either from the “main” definition or from an extension.
156///
157/// Like [`ast::DirectiveList`] (a different Rust type with the same name),
158/// except items are [`Component`]s instead of just [`Node`]s in order to track extension origin.
159///
160/// Confusingly, [`ast::DirectiveList`] is also used in other parts of a [`Schema`],
161/// for example for the directives applied to a field definition.
162/// (The field definition as a whole is already a [`Component`] to keep track of its origin.)
163#[derive(Clone, Eq, PartialEq, Hash, Default)]
164pub struct DirectiveList(pub Vec<Component<Directive>>);
165
166/// The definition of a named type, with all information from type extensions folded in.
167///
168/// The source location is that of the "main" definition.
169#[derive(Debug, Clone, PartialEq, Eq)]
170pub enum ExtendedType {
171    Scalar(Node<ScalarType>),
172    Object(Node<ObjectType>),
173    Interface(Node<InterfaceType>),
174    Union(Node<UnionType>),
175    Enum(Node<EnumType>),
176    InputObject(Node<InputObjectType>),
177}
178
179/// The definition of a [scalar type](https://spec.graphql.org/September2025/#sec-Scalars),
180/// with all information from type extensions folded in.
181#[derive(Debug, Clone, PartialEq, Eq, Hash)]
182pub struct ScalarType {
183    pub description: Option<Node<str>>,
184    pub name: Name,
185    pub directives: DirectiveList,
186}
187
188/// The definition of an [object type](https://spec.graphql.org/September2025/#sec-Objects),
189/// with all information from type extensions folded in.
190#[derive(Debug, Clone, PartialEq, Eq)]
191pub struct ObjectType {
192    pub description: Option<Node<str>>,
193    pub name: Name,
194    pub implements_interfaces: IndexSet<ComponentName>,
195    pub directives: DirectiveList,
196
197    /// Explicit field definitions.
198    ///
199    /// When looking up a definition,
200    /// consider using [`Schema::type_field`] instead to include meta-fields.
201    pub fields: IndexMap<Name, Component<FieldDefinition>>,
202}
203
204#[derive(Debug, Clone, PartialEq, Eq)]
205pub struct InterfaceType {
206    pub description: Option<Node<str>>,
207    pub name: Name,
208    pub implements_interfaces: IndexSet<ComponentName>,
209
210    pub directives: DirectiveList,
211
212    /// Explicit field definitions.
213    ///
214    /// When looking up a definition,
215    /// consider using [`Schema::type_field`] instead to include meta-fields.
216    pub fields: IndexMap<Name, Component<FieldDefinition>>,
217}
218
219/// The definition of an [union type](https://spec.graphql.org/September2025/#sec-Unions),
220/// with all information from type extensions folded in.
221#[derive(Debug, Clone, PartialEq, Eq)]
222pub struct UnionType {
223    pub description: Option<Node<str>>,
224    pub name: Name,
225    pub directives: DirectiveList,
226
227    /// * Key: name of a member object type
228    /// * Value: which union type extension defined this implementation,
229    ///   or `None` for the union type definition.
230    pub members: IndexSet<ComponentName>,
231}
232
233/// The definition of an [enum type](https://spec.graphql.org/September2025/#sec-Enums),
234/// with all information from type extensions folded in.
235#[derive(Debug, Clone, PartialEq, Eq)]
236pub struct EnumType {
237    pub description: Option<Node<str>>,
238    pub name: Name,
239    pub directives: DirectiveList,
240    pub values: IndexMap<Name, Component<EnumValueDefinition>>,
241}
242
243/// The definition of an [input object type](https://spec.graphql.org/September2025/#sec-Input-Objects),
244/// with all information from type extensions folded in.
245#[derive(Debug, Clone, PartialEq, Eq)]
246pub struct InputObjectType {
247    pub description: Option<Node<str>>,
248    pub name: Name,
249    pub directives: DirectiveList,
250    pub fields: IndexMap<Name, Component<InputValueDefinition>>,
251}
252
253/// The names of all types that implement a given interface.
254/// Returned by [`Schema::implementers_map`].
255///
256/// Concrete object types and derived interfaces can be accessed separately.
257///
258/// # Examples
259///
260/// ```rust
261/// use apollo_compiler::schema::Implementers;
262/// # let implementers = Implementers::default();
263///
264/// // introspection must return only concrete implementers.
265/// let possible_types = implementers.objects;
266/// ```
267///
268/// ```rust
269/// use apollo_compiler::schema::Implementers;
270/// # let implementers = Implementers::default();
271///
272/// for name in implementers.iter() {
273///     // iterates both concrete objects and interfaces
274///     println!("{name}");
275/// }
276/// ```
277#[derive(Debug, Default, Clone, PartialEq, Eq)]
278pub struct Implementers {
279    /// Names of the concrete object types that implement an interface.
280    pub objects: IndexSet<Name>,
281    /// Names of the interface types that implement an interface.
282    pub interfaces: IndexSet<Name>,
283}
284
285/// AST node that has been skipped during conversion to `Schema`
286#[derive(thiserror::Error, Debug, Clone)]
287pub(crate) enum BuildError {
288    #[error("a schema document must not contain {describe}")]
289    ExecutableDefinition { describe: &'static str },
290
291    #[error("must not have multiple `schema` definitions")]
292    SchemaDefinitionCollision {
293        previous_location: Option<SourceSpan>,
294    },
295
296    #[error("the directive `@{name}` is defined multiple times in the schema")]
297    DirectiveDefinitionCollision {
298        previous_location: Option<SourceSpan>,
299        name: Name,
300    },
301
302    #[error("the type `{name}` is defined multiple times in the schema")]
303    TypeDefinitionCollision {
304        previous_location: Option<SourceSpan>,
305        name: Name,
306    },
307
308    #[error("built-in scalar definitions must be omitted")]
309    BuiltInScalarTypeRedefinition,
310
311    #[error("schema extension without a schema definition")]
312    OrphanSchemaExtension,
313
314    #[error("type extension for undefined type `{name}`")]
315    OrphanTypeExtension { name: Name },
316
317    #[error("adding {describe_ext}, but `{name}` is {describe_def}")]
318    TypeExtensionKindMismatch {
319        name: Name,
320        describe_ext: &'static str,
321        def_location: Option<SourceSpan>,
322        describe_def: &'static str,
323    },
324
325    #[error("duplicate definitions for the `{operation_type}` root operation type")]
326    DuplicateRootOperation {
327        previous_location: Option<SourceSpan>,
328        operation_type: &'static str,
329    },
330
331    #[error(
332        "object type `{type_name}` implements interface `{name_at_previous_location}` \
333         more than once"
334    )]
335    DuplicateImplementsInterfaceInObject {
336        name_at_previous_location: Name,
337        type_name: Name,
338    },
339
340    #[error(
341        "interface type `{type_name}` implements interface `{name_at_previous_location}` \
342         more than once"
343    )]
344    DuplicateImplementsInterfaceInInterface {
345        name_at_previous_location: Name,
346        type_name: Name,
347    },
348
349    #[error(
350        "duplicate definitions for the `{name_at_previous_location}` \
351         field of object type `{type_name}`"
352    )]
353    ObjectFieldNameCollision {
354        name_at_previous_location: Name,
355        type_name: Name,
356    },
357
358    #[error(
359        "duplicate definitions for the `{name_at_previous_location}` \
360         field of interface type `{type_name}`"
361    )]
362    InterfaceFieldNameCollision {
363        name_at_previous_location: Name,
364        type_name: Name,
365    },
366
367    #[error(
368        "duplicate definitions for the `{name_at_previous_location}` \
369         value of enum type `{type_name}`"
370    )]
371    EnumValueNameCollision {
372        name_at_previous_location: Name,
373        type_name: Name,
374    },
375
376    #[error(
377        "duplicate definitions for the `{name_at_previous_location}` \
378         member of union type `{type_name}`"
379    )]
380    UnionMemberNameCollision {
381        name_at_previous_location: Name,
382        type_name: Name,
383    },
384
385    #[error(
386        "duplicate definitions for the `{name_at_previous_location}` \
387         field of input object type `{type_name}`"
388    )]
389    InputFieldNameCollision {
390        name_at_previous_location: Name,
391        type_name: Name,
392    },
393}
394
395/// Error type of [`Schema::type_field`]: could not find the requested field definition
396#[derive(Debug, Clone, PartialEq, Eq)]
397pub enum FieldLookupError<'schema> {
398    NoSuchType,
399    NoSuchField(&'schema NamedType, &'schema ExtendedType),
400}
401
402impl Schema {
403    /// Returns an (almost) empty schema.
404    ///
405    /// It starts with built-in directives, built-in scalars, and introspection types.
406    /// It can then be filled programatically.
407    #[allow(clippy::new_without_default)] // not a great implicit default in generic contexts
408    pub fn new() -> Self {
409        SchemaBuilder::new().build().unwrap()
410    }
411
412    /// Parse a single source file into a schema, with the default parser configuration.
413    ///
414    /// Create a [`Parser`] to use different parser configuration.
415    /// Use [`builder()`][Self::builder] to build a schema from multiple parsed files.
416    #[allow(clippy::result_large_err)] // Typically not called very often
417    pub fn parse(
418        source_text: impl Into<String>,
419        path: impl AsRef<Path>,
420    ) -> Result<Self, WithErrors<Self>> {
421        Parser::default().parse_schema(source_text, path)
422    }
423
424    /// [`parse`][Self::parse] then [`validate`][Self::validate],
425    /// to get a `Valid<Schema>` when mutating it isn’t needed.
426    #[allow(clippy::result_large_err)] // Typically not called very often
427    pub fn parse_and_validate(
428        source_text: impl Into<String>,
429        path: impl AsRef<Path>,
430    ) -> Result<Valid<Self>, WithErrors<Self>> {
431        let mut builder = Schema::builder();
432        Parser::default().parse_into_schema_builder(source_text, path, &mut builder);
433        let (mut schema, mut errors) = builder.build_inner();
434        validation::validate_schema(&mut errors, &mut schema);
435        errors.into_valid_result(schema)
436    }
437
438    /// Returns a new builder for creating a Schema from AST documents,
439    /// initialized with built-in directives, built-in scalars, and introspection types
440    ///
441    /// ```rust
442    /// use apollo_compiler::Schema;
443    ///
444    /// let empty_schema = Schema::builder().build();
445    /// ```
446    pub fn builder() -> SchemaBuilder {
447        SchemaBuilder::new()
448    }
449
450    #[allow(clippy::result_large_err)] // Typically not called very often
451    pub fn validate(mut self) -> Result<Valid<Self>, WithErrors<Self>> {
452        let mut errors = DiagnosticList::new(self.sources.clone());
453        validation::validate_schema(&mut errors, &mut self);
454        errors.into_valid_result(self)
455    }
456
457    /// Returns the type with the given name, if it is a scalar type
458    pub fn get_scalar(&self, name: &str) -> Option<&Node<ScalarType>> {
459        if let Some(ExtendedType::Scalar(ty)) = self.types.get(name) {
460            Some(ty)
461        } else {
462            None
463        }
464    }
465
466    /// Returns the type with the given name, if it is a object type
467    pub fn get_object(&self, name: &str) -> Option<&Node<ObjectType>> {
468        if let Some(ExtendedType::Object(ty)) = self.types.get(name) {
469            Some(ty)
470        } else {
471            None
472        }
473    }
474
475    /// Returns the type with the given name, if it is a interface type
476    pub fn get_interface(&self, name: &str) -> Option<&Node<InterfaceType>> {
477        if let Some(ExtendedType::Interface(ty)) = self.types.get(name) {
478            Some(ty)
479        } else {
480            None
481        }
482    }
483
484    /// Returns the type with the given name, if it is a union type
485    pub fn get_union(&self, name: &str) -> Option<&Node<UnionType>> {
486        if let Some(ExtendedType::Union(ty)) = self.types.get(name) {
487            Some(ty)
488        } else {
489            None
490        }
491    }
492
493    /// Returns the type with the given name, if it is a enum type
494    pub fn get_enum(&self, name: &str) -> Option<&Node<EnumType>> {
495        if let Some(ExtendedType::Enum(ty)) = self.types.get(name) {
496            Some(ty)
497        } else {
498            None
499        }
500    }
501
502    /// Returns the type with the given name, if it is a input object type
503    pub fn get_input_object(&self, name: &str) -> Option<&Node<InputObjectType>> {
504        if let Some(ExtendedType::InputObject(ty)) = self.types.get(name) {
505            Some(ty)
506        } else {
507            None
508        }
509    }
510
511    /// Returns the name of the object type for the root operation with the given operation kind
512    pub fn root_operation(&self, operation_type: ast::OperationType) -> Option<&NamedType> {
513        match operation_type {
514            ast::OperationType::Query => &self.schema_definition.query,
515            ast::OperationType::Mutation => &self.schema_definition.mutation,
516            ast::OperationType::Subscription => &self.schema_definition.subscription,
517        }
518        .as_ref()
519        .map(|component| &component.name)
520    }
521
522    /// Returns the definition of a type’s explicit field or meta-field.
523    pub fn type_field(
524        &self,
525        type_name: &str,
526        field_name: &str,
527    ) -> Result<&Component<FieldDefinition>, FieldLookupError<'_>> {
528        use ExtendedType::*;
529        let (ty_def_name, ty_def) = self
530            .types
531            .get_key_value(type_name)
532            .ok_or(FieldLookupError::NoSuchType)?;
533        let explicit_field = match ty_def {
534            Object(ty) => ty.fields.get(field_name),
535            Interface(ty) => ty.fields.get(field_name),
536            Scalar(_) | Union(_) | Enum(_) | InputObject(_) => None,
537        };
538        if let Some(def) = explicit_field {
539            return Ok(def);
540        }
541        let meta = MetaFieldDefinitions::get();
542        if field_name == "__typename" && matches!(ty_def, Object(_) | Interface(_) | Union(_)) {
543            // .validate() errors for __typename at the root of a subscription operation
544            return Ok(&meta.__typename);
545        }
546        if self
547            .schema_definition
548            .query
549            .as_ref()
550            .is_some_and(|query_type| query_type == type_name)
551        {
552            match field_name {
553                "__schema" => return Ok(&meta.__schema),
554                "__type" => return Ok(&meta.__type),
555                _ => {}
556            }
557        }
558        Err(FieldLookupError::NoSuchField(ty_def_name, ty_def))
559    }
560
561    /// Returns a map of interface names to names of types that implement that interface
562    ///
563    /// `Schema` only stores the inverse relationship
564    /// (in [`ObjectType::implements_interfaces`] and [`InterfaceType::implements_interfaces`]),
565    /// so iterating the implementers of an interface requires a linear scan
566    /// of all types in the schema.
567    /// If that is repeated for multiple interfaces,
568    /// gathering them all at once amorticizes that cost.
569    pub fn implementers_map(&self) -> HashMap<Name, Implementers> {
570        let mut map = HashMap::<Name, Implementers>::default();
571        for (ty_name, ty) in &self.types {
572            match ty {
573                ExtendedType::Object(def) => {
574                    for interface in &def.implements_interfaces {
575                        map.entry(interface.name.clone())
576                            .or_default()
577                            .objects
578                            .insert(ty_name.clone());
579                    }
580                }
581                ExtendedType::Interface(def) => {
582                    for interface in &def.implements_interfaces {
583                        map.entry(interface.name.clone())
584                            .or_default()
585                            .interfaces
586                            .insert(ty_name.clone());
587                    }
588                }
589                ExtendedType::Scalar(_)
590                | ExtendedType::Union(_)
591                | ExtendedType::Enum(_)
592                | ExtendedType::InputObject(_) => (),
593            };
594        }
595        map
596    }
597
598    /// Returns whether `maybe_subtype` is a subtype of `abstract_type`, which means either:
599    ///
600    /// * `maybe_subtype` implements the interface `abstract_type`
601    /// * `maybe_subtype` is a member of the union type `abstract_type`
602    pub fn is_subtype(&self, abstract_type: &str, maybe_subtype: &str) -> bool {
603        self.types.get(abstract_type).is_some_and(|ty| match ty {
604            ExtendedType::Interface(_) => self.types.get(maybe_subtype).is_some_and(|ty2| {
605                match ty2 {
606                    ExtendedType::Object(def) => &def.implements_interfaces,
607                    ExtendedType::Interface(def) => &def.implements_interfaces,
608                    ExtendedType::Scalar(_)
609                    | ExtendedType::Union(_)
610                    | ExtendedType::Enum(_)
611                    | ExtendedType::InputObject(_) => return false,
612                }
613                .contains(abstract_type)
614            }),
615            ExtendedType::Union(def) => def.members.contains(maybe_subtype),
616            ExtendedType::Scalar(_)
617            | ExtendedType::Object(_)
618            | ExtendedType::Enum(_)
619            | ExtendedType::InputObject(_) => false,
620        })
621    }
622
623    /// Returns whether the type `ty` is defined as is an input type
624    ///
625    /// <https://spec.graphql.org/September2025/#sec-Input-and-Output-Types>
626    pub fn is_input_type(&self, ty: &Type) -> bool {
627        match self.types.get(ty.inner_named_type()) {
628            Some(ExtendedType::Scalar(_))
629            | Some(ExtendedType::Enum(_))
630            | Some(ExtendedType::InputObject(_)) => true,
631            Some(ExtendedType::Object(_))
632            | Some(ExtendedType::Interface(_))
633            | Some(ExtendedType::Union(_))
634            | None => false,
635        }
636    }
637
638    /// Returns whether the type `ty` is defined as is an output type
639    ///
640    /// <https://spec.graphql.org/September2025/#sec-Input-and-Output-Types>
641    pub fn is_output_type(&self, ty: &Type) -> bool {
642        match self.types.get(ty.inner_named_type()) {
643            Some(ExtendedType::Scalar(_))
644            | Some(ExtendedType::Object(_))
645            | Some(ExtendedType::Interface(_))
646            | Some(ExtendedType::Union(_))
647            | Some(ExtendedType::Enum(_)) => true,
648            Some(ExtendedType::InputObject(_)) | None => false,
649        }
650    }
651
652    serialize_method!();
653}
654
655impl SchemaDefinition {
656    pub fn iter_root_operations(
657        &self,
658    ) -> impl Iterator<Item = (ast::OperationType, &ComponentName)> {
659        [
660            (ast::OperationType::Query, &self.query),
661            (ast::OperationType::Mutation, &self.mutation),
662            (ast::OperationType::Subscription, &self.subscription),
663        ]
664        .into_iter()
665        .filter_map(|(ty, maybe_op)| maybe_op.as_ref().map(|op| (ty, op)))
666    }
667
668    /// Iterate over the `origins` of all components
669    ///
670    /// The order of the returned set is unspecified but deterministic
671    /// for a given apollo-compiler version.
672    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
673        self.directives
674            .iter()
675            .map(|dir| &dir.origin)
676            .chain(self.query.iter().map(|name| &name.origin))
677            .chain(self.mutation.iter().map(|name| &name.origin))
678            .chain(self.subscription.iter().map(|name| &name.origin))
679    }
680
681    /// Collect `schema` extensions that contribute any component
682    ///
683    /// The order of the returned set is unspecified but deterministic
684    /// for a given apollo-compiler version.
685    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
686        self.iter_origins()
687            .filter_map(|origin| origin.extension_id())
688            .collect()
689    }
690}
691
692impl ExtendedType {
693    pub fn name(&self) -> &Name {
694        match self {
695            Self::Scalar(def) => &def.name,
696            Self::Object(def) => &def.name,
697            Self::Interface(def) => &def.name,
698            Self::Union(def) => &def.name,
699            Self::Enum(def) => &def.name,
700            Self::InputObject(def) => &def.name,
701        }
702    }
703
704    /// Return the source location of the type's base definition.
705    ///
706    /// If the type has extensions, those are not covered by this location.
707    pub fn location(&self) -> Option<SourceSpan> {
708        match self {
709            Self::Scalar(ty) => ty.location(),
710            Self::Object(ty) => ty.location(),
711            Self::Interface(ty) => ty.location(),
712            Self::Union(ty) => ty.location(),
713            Self::Enum(ty) => ty.location(),
714            Self::InputObject(ty) => ty.location(),
715        }
716    }
717
718    pub(crate) fn describe(&self) -> &'static str {
719        match self {
720            Self::Scalar(_) => "a scalar type",
721            Self::Object(_) => "an object type",
722            Self::Interface(_) => "an interface type",
723            Self::Union(_) => "a union type",
724            Self::Enum(_) => "an enum type",
725            Self::InputObject(_) => "an input object type",
726        }
727    }
728
729    pub fn is_scalar(&self) -> bool {
730        matches!(self, Self::Scalar(_))
731    }
732
733    pub fn is_object(&self) -> bool {
734        matches!(self, Self::Object(_))
735    }
736
737    pub fn is_interface(&self) -> bool {
738        matches!(self, Self::Interface(_))
739    }
740
741    pub fn is_union(&self) -> bool {
742        matches!(self, Self::Union(_))
743    }
744
745    pub fn is_enum(&self) -> bool {
746        matches!(self, Self::Enum(_))
747    }
748
749    pub fn is_input_object(&self) -> bool {
750        matches!(self, Self::InputObject(_))
751    }
752
753    pub fn as_scalar(&self) -> Option<&ScalarType> {
754        if let Self::Scalar(def) = self {
755            Some(def)
756        } else {
757            None
758        }
759    }
760
761    pub fn as_object(&self) -> Option<&ObjectType> {
762        if let Self::Object(def) = self {
763            Some(def)
764        } else {
765            None
766        }
767    }
768
769    pub fn as_interface(&self) -> Option<&InterfaceType> {
770        if let Self::Interface(def) = self {
771            Some(def)
772        } else {
773            None
774        }
775    }
776
777    pub fn as_union(&self) -> Option<&UnionType> {
778        if let Self::Union(def) = self {
779            Some(def)
780        } else {
781            None
782        }
783    }
784
785    pub fn as_enum(&self) -> Option<&EnumType> {
786        if let Self::Enum(def) = self {
787            Some(def)
788        } else {
789            None
790        }
791    }
792
793    pub fn as_input_object(&self) -> Option<&InputObjectType> {
794        if let Self::InputObject(def) = self {
795            Some(def)
796        } else {
797            None
798        }
799    }
800
801    /// Returns wether this type is a leaf type: scalar or enum.
802    ///
803    /// Field selections must have sub-selections if and only if
804    /// their inner named type is *not* a leaf field.
805    pub fn is_leaf(&self) -> bool {
806        matches!(self, Self::Scalar(_) | Self::Enum(_))
807    }
808
809    /// Returns true if a value of this type can be used as an input value.
810    ///
811    /// # Spec
812    /// This implements spec function
813    /// [`IsInputType(type)`](https://spec.graphql.org/September2025/#IsInputType())
814    pub fn is_input_type(&self) -> bool {
815        matches!(self, Self::Scalar(_) | Self::Enum(_) | Self::InputObject(_))
816    }
817
818    /// Returns true if a value of this type can be used as an output value.
819    ///
820    /// # Spec
821    /// This implements spec function
822    /// [`IsOutputType(type)`](https://spec.graphql.org/September2025/#IsOutputType())
823    pub fn is_output_type(&self) -> bool {
824        matches!(
825            self,
826            Self::Scalar(_) | Self::Enum(_) | Self::Object(_) | Self::Interface(_) | Self::Union(_)
827        )
828    }
829
830    /// Returns whether this is a built-in scalar or introspection type
831    pub fn is_built_in(&self) -> bool {
832        match self {
833            Self::Scalar(ty) => ty.is_built_in(),
834            Self::Object(ty) => ty.is_built_in(),
835            Self::Interface(ty) => ty.is_built_in(),
836            Self::Union(ty) => ty.is_built_in(),
837            Self::Enum(ty) => ty.is_built_in(),
838            Self::InputObject(ty) => ty.is_built_in(),
839        }
840    }
841
842    pub fn directives(&self) -> &DirectiveList {
843        match self {
844            Self::Scalar(ty) => &ty.directives,
845            Self::Object(ty) => &ty.directives,
846            Self::Interface(ty) => &ty.directives,
847            Self::Union(ty) => &ty.directives,
848            Self::Enum(ty) => &ty.directives,
849            Self::InputObject(ty) => &ty.directives,
850        }
851    }
852
853    pub fn description(&self) -> Option<&Node<str>> {
854        match self {
855            Self::Scalar(ty) => ty.description.as_ref(),
856            Self::Object(ty) => ty.description.as_ref(),
857            Self::Interface(ty) => ty.description.as_ref(),
858            Self::Union(ty) => ty.description.as_ref(),
859            Self::Enum(ty) => ty.description.as_ref(),
860            Self::InputObject(ty) => ty.description.as_ref(),
861        }
862    }
863
864    /// Iterate over the `origins` of all components
865    ///
866    /// The order of the returned set is unspecified but deterministic
867    /// for a given apollo-compiler version.
868    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
869        match self {
870            Self::Scalar(ty) => Box::new(ty.iter_origins()) as Box<dyn Iterator<Item = _>>,
871            Self::Object(ty) => Box::new(ty.iter_origins()),
872            Self::Interface(ty) => Box::new(ty.iter_origins()),
873            Self::Union(ty) => Box::new(ty.iter_origins()),
874            Self::Enum(ty) => Box::new(ty.iter_origins()),
875            Self::InputObject(ty) => Box::new(ty.iter_origins()),
876        }
877    }
878
879    /// Collect `schema` extensions that contribute any component
880    ///
881    /// The order of the returned set is unspecified but deterministic
882    /// for a given apollo-compiler version.
883    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
884        self.iter_origins()
885            .filter_map(|origin| origin.extension_id())
886            .collect()
887    }
888
889    serialize_method!();
890}
891
892impl ScalarType {
893    /// Iterate over the `origins` of all components
894    ///
895    /// The order of the returned set is unspecified but deterministic
896    /// for a given apollo-compiler version.
897    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
898        self.directives.iter().map(|dir| &dir.origin)
899    }
900
901    /// Collect scalar type extensions that contribute any component
902    ///
903    /// The order of the returned set is unspecified but deterministic
904    /// for a given apollo-compiler version.
905    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
906        self.iter_origins()
907            .filter_map(|origin| origin.extension_id())
908            .collect()
909    }
910
911    serialize_method!();
912}
913
914impl ObjectType {
915    /// Iterate over the `origins` of all components
916    ///
917    /// The order of the returned set is unspecified but deterministic
918    /// for a given apollo-compiler version.
919    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
920        self.directives
921            .iter()
922            .map(|dir| &dir.origin)
923            .chain(
924                self.implements_interfaces
925                    .iter()
926                    .map(|component| &component.origin),
927            )
928            .chain(self.fields.values().map(|field| &field.origin))
929    }
930
931    /// Collect object type extensions that contribute any component
932    ///
933    /// The order of the returned set is unspecified but deterministic
934    /// for a given apollo-compiler version.
935    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
936        self.iter_origins()
937            .filter_map(|origin| origin.extension_id())
938            .collect()
939    }
940
941    serialize_method!();
942}
943
944impl InterfaceType {
945    /// Iterate over the `origins` of all components
946    ///
947    /// The order of the returned set is unspecified but deterministic
948    /// for a given apollo-compiler version.
949    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
950        self.directives
951            .iter()
952            .map(|dir| &dir.origin)
953            .chain(
954                self.implements_interfaces
955                    .iter()
956                    .map(|component| &component.origin),
957            )
958            .chain(self.fields.values().map(|field| &field.origin))
959    }
960
961    /// Collect interface type extensions that contribute any component
962    ///
963    /// The order of the returned set is unspecified but deterministic
964    /// for a given apollo-compiler version.
965    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
966        self.iter_origins()
967            .filter_map(|origin| origin.extension_id())
968            .collect()
969    }
970
971    serialize_method!();
972}
973
974impl UnionType {
975    /// Iterate over the `origins` of all components
976    ///
977    /// The order of the returned set is unspecified but deterministic
978    /// for a given apollo-compiler version.
979    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
980        self.directives
981            .iter()
982            .map(|dir| &dir.origin)
983            .chain(self.members.iter().map(|component| &component.origin))
984    }
985
986    /// Collect union type extensions that contribute any component
987    ///
988    /// The order of the returned set is unspecified but deterministic
989    /// for a given apollo-compiler version.
990    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
991        self.iter_origins()
992            .filter_map(|origin| origin.extension_id())
993            .collect()
994    }
995
996    serialize_method!();
997}
998
999impl EnumType {
1000    /// Iterate over the `origins` of all components
1001    ///
1002    /// The order of the returned set is unspecified but deterministic
1003    /// for a given apollo-compiler version.
1004    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
1005        self.directives
1006            .iter()
1007            .map(|dir| &dir.origin)
1008            .chain(self.values.values().map(|value| &value.origin))
1009    }
1010
1011    /// Collect enum type extensions that contribute any component
1012    ///
1013    /// The order of the returned set is unspecified but deterministic
1014    /// for a given apollo-compiler version.
1015    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
1016        self.iter_origins()
1017            .filter_map(|origin| origin.extension_id())
1018            .collect()
1019    }
1020
1021    serialize_method!();
1022}
1023
1024impl InputObjectType {
1025    /// Returns true if this is a OneOf Input Object (has the `@oneOf` directive).
1026    pub fn is_one_of(&self) -> bool {
1027        self.directives.get("oneOf").is_some()
1028    }
1029
1030    /// Iterate over the `origins` of all components
1031    ///
1032    /// The order of the returned set is unspecified but deterministic
1033    /// for a given apollo-compiler version.
1034    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
1035        self.directives
1036            .iter()
1037            .map(|dir| &dir.origin)
1038            .chain(self.fields.values().map(|field| &field.origin))
1039    }
1040
1041    /// Collect input object type extensions that contribute any component
1042    ///
1043    /// The order of the returned set is unspecified but deterministic
1044    /// for a given apollo-compiler version.
1045    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
1046        self.iter_origins()
1047            .filter_map(|origin| origin.extension_id())
1048            .collect()
1049    }
1050
1051    serialize_method!();
1052}
1053
1054impl DirectiveList {
1055    pub const fn new() -> Self {
1056        Self(Vec::new())
1057    }
1058
1059    /// Returns an iterator of directives with the given name.
1060    ///
1061    /// This method is best for repeatable directives.
1062    /// See also [`get`][Self::get] for non-repeatable directives.
1063    pub fn get_all<'def: 'name, 'name>(
1064        &'def self,
1065        name: &'name str,
1066    ) -> impl Iterator<Item = &'def Component<Directive>> + 'name {
1067        self.0.iter().filter(move |dir| dir.name == name)
1068    }
1069
1070    /// Returns the first directive with the given name, if any.
1071    ///
1072    /// This method is best for non-repeatable directives.
1073    /// See also [`get_all`][Self::get_all] for repeatable directives.
1074    pub fn get(&self, name: &str) -> Option<&Component<Directive>> {
1075        self.get_all(name).next()
1076    }
1077
1078    /// Returns whether there is a directive with the given name
1079    pub fn has(&self, name: &str) -> bool {
1080        self.get(name).is_some()
1081    }
1082
1083    pub(crate) fn iter_ast(&self) -> impl Iterator<Item = &Node<ast::Directive>> {
1084        self.0.iter().map(|component| &component.node)
1085    }
1086
1087    /// Accepts either [`Component<Directive>`], [`Node<Directive>`], or [`Directive`].
1088    pub fn push(&mut self, directive: impl Into<Component<Directive>>) {
1089        self.0.push(directive.into());
1090    }
1091
1092    serialize_method!();
1093}
1094
1095impl std::fmt::Debug for DirectiveList {
1096    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1097        self.0.fmt(f)
1098    }
1099}
1100
1101impl std::ops::Deref for DirectiveList {
1102    type Target = Vec<Component<Directive>>;
1103
1104    fn deref(&self) -> &Self::Target {
1105        &self.0
1106    }
1107}
1108
1109impl std::ops::DerefMut for DirectiveList {
1110    fn deref_mut(&mut self) -> &mut Self::Target {
1111        &mut self.0
1112    }
1113}
1114
1115impl IntoIterator for DirectiveList {
1116    type Item = Component<Directive>;
1117
1118    type IntoIter = std::vec::IntoIter<Component<Directive>>;
1119
1120    fn into_iter(self) -> Self::IntoIter {
1121        self.0.into_iter()
1122    }
1123}
1124
1125impl<'a> IntoIterator for &'a DirectiveList {
1126    type Item = &'a Component<Directive>;
1127
1128    type IntoIter = std::slice::Iter<'a, Component<Directive>>;
1129
1130    fn into_iter(self) -> Self::IntoIter {
1131        self.0.iter()
1132    }
1133}
1134
1135impl<'a> IntoIterator for &'a mut DirectiveList {
1136    type Item = &'a mut Component<Directive>;
1137
1138    type IntoIter = std::slice::IterMut<'a, Component<Directive>>;
1139
1140    fn into_iter(self) -> Self::IntoIter {
1141        self.0.iter_mut()
1142    }
1143}
1144
1145impl<D> FromIterator<D> for DirectiveList
1146where
1147    D: Into<Component<Directive>>,
1148{
1149    fn from_iter<T: IntoIterator<Item = D>>(iter: T) -> Self {
1150        Self(iter.into_iter().map(Into::into).collect())
1151    }
1152}
1153
1154impl Eq for Schema {}
1155
1156impl PartialEq for Schema {
1157    fn eq(&self, other: &Self) -> bool {
1158        let Self {
1159            sources: _,                 // ignored
1160            validate_default_values: _, // ignored, config only
1161            schema_definition,
1162            directive_definitions,
1163            types,
1164        } = self;
1165        *schema_definition == other.schema_definition
1166            && *directive_definitions == other.directive_definitions
1167            && *types == other.types
1168    }
1169}
1170
1171impl Implementers {
1172    /// Iterate over all implementers, including objects and interfaces.
1173    ///
1174    /// The iteration order is unspecified.
1175    pub fn iter(&self) -> impl Iterator<Item = &'_ Name> {
1176        self.objects.iter().chain(&self.interfaces)
1177    }
1178}
1179
1180impl From<Node<ScalarType>> for ExtendedType {
1181    fn from(ty: Node<ScalarType>) -> Self {
1182        Self::Scalar(ty)
1183    }
1184}
1185
1186impl From<Node<ObjectType>> for ExtendedType {
1187    fn from(ty: Node<ObjectType>) -> Self {
1188        Self::Object(ty)
1189    }
1190}
1191
1192impl From<Node<InterfaceType>> for ExtendedType {
1193    fn from(ty: Node<InterfaceType>) -> Self {
1194        Self::Interface(ty)
1195    }
1196}
1197
1198impl From<Node<UnionType>> for ExtendedType {
1199    fn from(ty: Node<UnionType>) -> Self {
1200        Self::Union(ty)
1201    }
1202}
1203
1204impl From<Node<EnumType>> for ExtendedType {
1205    fn from(ty: Node<EnumType>) -> Self {
1206        Self::Enum(ty)
1207    }
1208}
1209
1210impl From<Node<InputObjectType>> for ExtendedType {
1211    fn from(ty: Node<InputObjectType>) -> Self {
1212        Self::InputObject(ty)
1213    }
1214}
1215
1216impl From<ScalarType> for ExtendedType {
1217    fn from(ty: ScalarType) -> Self {
1218        Self::Scalar(ty.into())
1219    }
1220}
1221
1222impl From<ObjectType> for ExtendedType {
1223    fn from(ty: ObjectType) -> Self {
1224        Self::Object(ty.into())
1225    }
1226}
1227
1228impl From<InterfaceType> for ExtendedType {
1229    fn from(ty: InterfaceType) -> Self {
1230        Self::Interface(ty.into())
1231    }
1232}
1233
1234impl From<UnionType> for ExtendedType {
1235    fn from(ty: UnionType) -> Self {
1236        Self::Union(ty.into())
1237    }
1238}
1239
1240impl From<EnumType> for ExtendedType {
1241    fn from(ty: EnumType) -> Self {
1242        Self::Enum(ty.into())
1243    }
1244}
1245
1246impl From<InputObjectType> for ExtendedType {
1247    fn from(ty: InputObjectType) -> Self {
1248        Self::InputObject(ty.into())
1249    }
1250}
1251
1252impl std::fmt::Debug for Schema {
1253    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1254        let Self {
1255            sources,
1256            schema_definition,
1257            directive_definitions,
1258            types,
1259            validate_default_values: _,
1260        } = self;
1261        f.debug_struct("Schema")
1262            .field("sources", sources)
1263            .field("schema_definition", schema_definition)
1264            .field(
1265                "directive_definitions",
1266                &DebugDirectiveDefinitions(directive_definitions),
1267            )
1268            .field("types", &DebugTypes(types))
1269            .finish()
1270    }
1271}
1272
1273struct DebugDirectiveDefinitions<'a>(&'a IndexMap<Name, Node<DirectiveDefinition>>);
1274
1275struct DebugTypes<'a>(&'a IndexMap<Name, ExtendedType>);
1276
1277impl std::fmt::Debug for DebugDirectiveDefinitions<'_> {
1278    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1279        let mut map = f.debug_map();
1280        for (name, def) in self.0 {
1281            if !def.is_built_in() {
1282                map.entry(name, def);
1283            } else {
1284                map.entry(name, &format_args!("built_in_directive!({name:?})"));
1285            }
1286        }
1287        map.finish()
1288    }
1289}
1290
1291impl std::fmt::Debug for DebugTypes<'_> {
1292    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1293        let mut map = f.debug_map();
1294        for (name, def) in self.0 {
1295            if !def.is_built_in() {
1296                map.entry(name, def);
1297            } else {
1298                map.entry(name, &format_args!("built_in_type!({name:?})"));
1299            }
1300        }
1301        map.finish()
1302    }
1303}
1304
1305struct MetaFieldDefinitions {
1306    __typename: Component<FieldDefinition>,
1307    __schema: Component<FieldDefinition>,
1308    __type: Component<FieldDefinition>,
1309}
1310
1311impl MetaFieldDefinitions {
1312    fn get() -> &'static Self {
1313        static DEFS: OnceLock<MetaFieldDefinitions> = OnceLock::new();
1314        DEFS.get_or_init(|| Self {
1315            // __typename: String!
1316            __typename: Component::new(FieldDefinition {
1317                description: None,
1318                name: name!("__typename"),
1319                arguments: Vec::new(),
1320                ty: ty!(String!),
1321                directives: ast::DirectiveList::new(),
1322            }),
1323            // __schema: __Schema!
1324            __schema: Component::new(FieldDefinition {
1325                description: None,
1326                name: name!("__schema"),
1327                arguments: Vec::new(),
1328                ty: ty!(__Schema!),
1329                directives: ast::DirectiveList::new(),
1330            }),
1331            // __type(name: String!): __Type
1332            __type: Component::new(FieldDefinition {
1333                description: None,
1334                name: name!("__type"),
1335                arguments: vec![InputValueDefinition {
1336                    description: None,
1337                    name: name!("name"),
1338                    ty: ty!(String!).into(),
1339                    default_value: None,
1340                    directives: ast::DirectiveList::new(),
1341                }
1342                .into()],
1343                ty: ty!(__Type),
1344                directives: ast::DirectiveList::new(),
1345            }),
1346        })
1347    }
1348}