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