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    /// Collect `schema` extensions that contribute any component
662    ///
663    /// The order of the returned set is unspecified but deterministic
664    /// for a given apollo-compiler version.
665    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
666        self.directives
667            .iter()
668            .flat_map(|dir| dir.origin.extension_id())
669            .chain(
670                self.query
671                    .as_ref()
672                    .and_then(|name| name.origin.extension_id()),
673            )
674            .chain(
675                self.mutation
676                    .as_ref()
677                    .and_then(|name| name.origin.extension_id()),
678            )
679            .chain(
680                self.subscription
681                    .as_ref()
682                    .and_then(|name| name.origin.extension_id()),
683            )
684            .collect()
685    }
686}
687
688impl ExtendedType {
689    pub fn name(&self) -> &Name {
690        match self {
691            Self::Scalar(def) => &def.name,
692            Self::Object(def) => &def.name,
693            Self::Interface(def) => &def.name,
694            Self::Union(def) => &def.name,
695            Self::Enum(def) => &def.name,
696            Self::InputObject(def) => &def.name,
697        }
698    }
699
700    /// Return the source location of the type's base definition.
701    ///
702    /// If the type has extensions, those are not covered by this location.
703    pub fn location(&self) -> Option<SourceSpan> {
704        match self {
705            Self::Scalar(ty) => ty.location(),
706            Self::Object(ty) => ty.location(),
707            Self::Interface(ty) => ty.location(),
708            Self::Union(ty) => ty.location(),
709            Self::Enum(ty) => ty.location(),
710            Self::InputObject(ty) => ty.location(),
711        }
712    }
713
714    pub(crate) fn describe(&self) -> &'static str {
715        match self {
716            Self::Scalar(_) => "a scalar type",
717            Self::Object(_) => "an object type",
718            Self::Interface(_) => "an interface type",
719            Self::Union(_) => "a union type",
720            Self::Enum(_) => "an enum type",
721            Self::InputObject(_) => "an input object type",
722        }
723    }
724
725    pub fn is_scalar(&self) -> bool {
726        matches!(self, Self::Scalar(_))
727    }
728
729    pub fn is_object(&self) -> bool {
730        matches!(self, Self::Object(_))
731    }
732
733    pub fn is_interface(&self) -> bool {
734        matches!(self, Self::Interface(_))
735    }
736
737    pub fn is_union(&self) -> bool {
738        matches!(self, Self::Union(_))
739    }
740
741    pub fn is_enum(&self) -> bool {
742        matches!(self, Self::Enum(_))
743    }
744
745    pub fn is_input_object(&self) -> bool {
746        matches!(self, Self::InputObject(_))
747    }
748
749    pub fn as_scalar(&self) -> Option<&ScalarType> {
750        if let Self::Scalar(def) = self {
751            Some(def)
752        } else {
753            None
754        }
755    }
756
757    pub fn as_object(&self) -> Option<&ObjectType> {
758        if let Self::Object(def) = self {
759            Some(def)
760        } else {
761            None
762        }
763    }
764
765    pub fn as_interface(&self) -> Option<&InterfaceType> {
766        if let Self::Interface(def) = self {
767            Some(def)
768        } else {
769            None
770        }
771    }
772
773    pub fn as_union(&self) -> Option<&UnionType> {
774        if let Self::Union(def) = self {
775            Some(def)
776        } else {
777            None
778        }
779    }
780
781    pub fn as_enum(&self) -> Option<&EnumType> {
782        if let Self::Enum(def) = self {
783            Some(def)
784        } else {
785            None
786        }
787    }
788
789    pub fn as_input_object(&self) -> Option<&InputObjectType> {
790        if let Self::InputObject(def) = self {
791            Some(def)
792        } else {
793            None
794        }
795    }
796
797    /// Returns wether this type is a leaf type: scalar or enum.
798    ///
799    /// Field selections must have sub-selections if and only if
800    /// their inner named type is *not* a leaf field.
801    pub fn is_leaf(&self) -> bool {
802        matches!(self, Self::Scalar(_) | Self::Enum(_))
803    }
804
805    /// Returns true if a value of this type can be used as an input value.
806    ///
807    /// # Spec
808    /// This implements spec function
809    /// [`IsInputType(type)`](https://spec.graphql.org/draft/#IsInputType())
810    pub fn is_input_type(&self) -> bool {
811        matches!(self, Self::Scalar(_) | Self::Enum(_) | Self::InputObject(_))
812    }
813
814    /// Returns true if a value of this type can be used as an output value.
815    ///
816    /// # Spec
817    /// This implements spec function
818    /// [`IsOutputType(type)`](https://spec.graphql.org/draft/#IsOutputType())
819    pub fn is_output_type(&self) -> bool {
820        matches!(
821            self,
822            Self::Scalar(_) | Self::Enum(_) | Self::Object(_) | Self::Interface(_) | Self::Union(_)
823        )
824    }
825
826    /// Returns whether this is a built-in scalar or introspection type
827    pub fn is_built_in(&self) -> bool {
828        match self {
829            Self::Scalar(ty) => ty.is_built_in(),
830            Self::Object(ty) => ty.is_built_in(),
831            Self::Interface(ty) => ty.is_built_in(),
832            Self::Union(ty) => ty.is_built_in(),
833            Self::Enum(ty) => ty.is_built_in(),
834            Self::InputObject(ty) => ty.is_built_in(),
835        }
836    }
837
838    pub fn directives(&self) -> &DirectiveList {
839        match self {
840            Self::Scalar(ty) => &ty.directives,
841            Self::Object(ty) => &ty.directives,
842            Self::Interface(ty) => &ty.directives,
843            Self::Union(ty) => &ty.directives,
844            Self::Enum(ty) => &ty.directives,
845            Self::InputObject(ty) => &ty.directives,
846        }
847    }
848
849    pub fn description(&self) -> Option<&Node<str>> {
850        match self {
851            Self::Scalar(ty) => ty.description.as_ref(),
852            Self::Object(ty) => ty.description.as_ref(),
853            Self::Interface(ty) => ty.description.as_ref(),
854            Self::Union(ty) => ty.description.as_ref(),
855            Self::Enum(ty) => ty.description.as_ref(),
856            Self::InputObject(ty) => ty.description.as_ref(),
857        }
858    }
859
860    serialize_method!();
861}
862
863impl ScalarType {
864    /// Collect scalar type extensions that contribute any component
865    ///
866    /// The order of the returned set is unspecified but deterministic
867    /// for a given apollo-compiler version.
868    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
869        self.directives
870            .iter()
871            .flat_map(|dir| dir.origin.extension_id())
872            .collect()
873    }
874
875    serialize_method!();
876}
877
878impl ObjectType {
879    /// Collect object type 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.directives
885            .iter()
886            .flat_map(|dir| dir.origin.extension_id())
887            .chain(
888                self.implements_interfaces
889                    .iter()
890                    .flat_map(|component| component.origin.extension_id()),
891            )
892            .chain(
893                self.fields
894                    .values()
895                    .flat_map(|field| field.origin.extension_id()),
896            )
897            .collect()
898    }
899
900    serialize_method!();
901}
902
903impl InterfaceType {
904    /// Collect interface type extensions that contribute any component
905    ///
906    /// The order of the returned set is unspecified but deterministic
907    /// for a given apollo-compiler version.
908    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
909        self.directives
910            .iter()
911            .flat_map(|dir| dir.origin.extension_id())
912            .chain(
913                self.implements_interfaces
914                    .iter()
915                    .flat_map(|component| component.origin.extension_id()),
916            )
917            .chain(
918                self.fields
919                    .values()
920                    .flat_map(|field| field.origin.extension_id()),
921            )
922            .collect()
923    }
924
925    serialize_method!();
926}
927
928impl UnionType {
929    /// Collect union type extensions that contribute any component
930    ///
931    /// The order of the returned set is unspecified but deterministic
932    /// for a given apollo-compiler version.
933    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
934        self.directives
935            .iter()
936            .flat_map(|dir| dir.origin.extension_id())
937            .chain(
938                self.members
939                    .iter()
940                    .flat_map(|component| component.origin.extension_id()),
941            )
942            .collect()
943    }
944
945    serialize_method!();
946}
947
948impl EnumType {
949    /// Collect enum type extensions that contribute any component
950    ///
951    /// The order of the returned set is unspecified but deterministic
952    /// for a given apollo-compiler version.
953    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
954        self.directives
955            .iter()
956            .flat_map(|dir| dir.origin.extension_id())
957            .chain(
958                self.values
959                    .values()
960                    .flat_map(|value| value.origin.extension_id()),
961            )
962            .collect()
963    }
964
965    serialize_method!();
966}
967
968impl InputObjectType {
969    /// Collect input object type extensions that contribute any component
970    ///
971    /// The order of the returned set is unspecified but deterministic
972    /// for a given apollo-compiler version.
973    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
974        self.directives
975            .iter()
976            .flat_map(|dir| dir.origin.extension_id())
977            .chain(
978                self.fields
979                    .values()
980                    .flat_map(|field| field.origin.extension_id()),
981            )
982            .collect()
983    }
984
985    serialize_method!();
986}
987
988impl DirectiveList {
989    pub const fn new() -> Self {
990        Self(Vec::new())
991    }
992
993    /// Returns an iterator of directives with the given name.
994    ///
995    /// This method is best for repeatable directives.
996    /// See also [`get`][Self::get] for non-repeatable directives.
997    pub fn get_all<'def: 'name, 'name>(
998        &'def self,
999        name: &'name str,
1000    ) -> impl Iterator<Item = &'def Component<Directive>> + 'name {
1001        self.0.iter().filter(move |dir| dir.name == name)
1002    }
1003
1004    /// Returns the first directive with the given name, if any.
1005    ///
1006    /// This method is best for non-repeatable directives.
1007    /// See also [`get_all`][Self::get_all] for repeatable directives.
1008    pub fn get(&self, name: &str) -> Option<&Component<Directive>> {
1009        self.get_all(name).next()
1010    }
1011
1012    /// Returns whether there is a directive with the given name
1013    pub fn has(&self, name: &str) -> bool {
1014        self.get(name).is_some()
1015    }
1016
1017    pub(crate) fn iter_ast(&self) -> impl Iterator<Item = &Node<ast::Directive>> {
1018        self.0.iter().map(|component| &component.node)
1019    }
1020
1021    /// Accepts either [`Component<Directive>`], [`Node<Directive>`], or [`Directive`].
1022    pub fn push(&mut self, directive: impl Into<Component<Directive>>) {
1023        self.0.push(directive.into());
1024    }
1025
1026    serialize_method!();
1027}
1028
1029impl std::fmt::Debug for DirectiveList {
1030    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1031        self.0.fmt(f)
1032    }
1033}
1034
1035impl std::ops::Deref for DirectiveList {
1036    type Target = Vec<Component<Directive>>;
1037
1038    fn deref(&self) -> &Self::Target {
1039        &self.0
1040    }
1041}
1042
1043impl std::ops::DerefMut for DirectiveList {
1044    fn deref_mut(&mut self) -> &mut Self::Target {
1045        &mut self.0
1046    }
1047}
1048
1049impl IntoIterator for DirectiveList {
1050    type Item = Component<Directive>;
1051
1052    type IntoIter = std::vec::IntoIter<Component<Directive>>;
1053
1054    fn into_iter(self) -> Self::IntoIter {
1055        self.0.into_iter()
1056    }
1057}
1058
1059impl<'a> IntoIterator for &'a DirectiveList {
1060    type Item = &'a Component<Directive>;
1061
1062    type IntoIter = std::slice::Iter<'a, Component<Directive>>;
1063
1064    fn into_iter(self) -> Self::IntoIter {
1065        self.0.iter()
1066    }
1067}
1068
1069impl<'a> IntoIterator for &'a mut DirectiveList {
1070    type Item = &'a mut Component<Directive>;
1071
1072    type IntoIter = std::slice::IterMut<'a, Component<Directive>>;
1073
1074    fn into_iter(self) -> Self::IntoIter {
1075        self.0.iter_mut()
1076    }
1077}
1078
1079impl<D> FromIterator<D> for DirectiveList
1080where
1081    D: Into<Component<Directive>>,
1082{
1083    fn from_iter<T: IntoIterator<Item = D>>(iter: T) -> Self {
1084        Self(iter.into_iter().map(Into::into).collect())
1085    }
1086}
1087
1088impl Eq for Schema {}
1089
1090impl PartialEq for Schema {
1091    fn eq(&self, other: &Self) -> bool {
1092        let Self {
1093            sources: _, // ignored
1094            schema_definition,
1095            directive_definitions,
1096            types,
1097        } = self;
1098        *schema_definition == other.schema_definition
1099            && *directive_definitions == other.directive_definitions
1100            && *types == other.types
1101    }
1102}
1103
1104impl Implementers {
1105    /// Iterate over all implementers, including objects and interfaces.
1106    ///
1107    /// The iteration order is unspecified.
1108    pub fn iter(&self) -> impl Iterator<Item = &'_ Name> {
1109        self.objects.iter().chain(&self.interfaces)
1110    }
1111}
1112
1113impl From<Node<ScalarType>> for ExtendedType {
1114    fn from(ty: Node<ScalarType>) -> Self {
1115        Self::Scalar(ty)
1116    }
1117}
1118
1119impl From<Node<ObjectType>> for ExtendedType {
1120    fn from(ty: Node<ObjectType>) -> Self {
1121        Self::Object(ty)
1122    }
1123}
1124
1125impl From<Node<InterfaceType>> for ExtendedType {
1126    fn from(ty: Node<InterfaceType>) -> Self {
1127        Self::Interface(ty)
1128    }
1129}
1130
1131impl From<Node<UnionType>> for ExtendedType {
1132    fn from(ty: Node<UnionType>) -> Self {
1133        Self::Union(ty)
1134    }
1135}
1136
1137impl From<Node<EnumType>> for ExtendedType {
1138    fn from(ty: Node<EnumType>) -> Self {
1139        Self::Enum(ty)
1140    }
1141}
1142
1143impl From<Node<InputObjectType>> for ExtendedType {
1144    fn from(ty: Node<InputObjectType>) -> Self {
1145        Self::InputObject(ty)
1146    }
1147}
1148
1149impl From<ScalarType> for ExtendedType {
1150    fn from(ty: ScalarType) -> Self {
1151        Self::Scalar(ty.into())
1152    }
1153}
1154
1155impl From<ObjectType> for ExtendedType {
1156    fn from(ty: ObjectType) -> Self {
1157        Self::Object(ty.into())
1158    }
1159}
1160
1161impl From<InterfaceType> for ExtendedType {
1162    fn from(ty: InterfaceType) -> Self {
1163        Self::Interface(ty.into())
1164    }
1165}
1166
1167impl From<UnionType> for ExtendedType {
1168    fn from(ty: UnionType) -> Self {
1169        Self::Union(ty.into())
1170    }
1171}
1172
1173impl From<EnumType> for ExtendedType {
1174    fn from(ty: EnumType) -> Self {
1175        Self::Enum(ty.into())
1176    }
1177}
1178
1179impl From<InputObjectType> for ExtendedType {
1180    fn from(ty: InputObjectType) -> Self {
1181        Self::InputObject(ty.into())
1182    }
1183}
1184
1185impl std::fmt::Debug for Schema {
1186    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1187        let Self {
1188            sources,
1189            schema_definition,
1190            directive_definitions,
1191            types,
1192        } = self;
1193        f.debug_struct("Schema")
1194            .field("sources", sources)
1195            .field("schema_definition", schema_definition)
1196            .field(
1197                "directive_definitions",
1198                &DebugDirectiveDefinitions(directive_definitions),
1199            )
1200            .field("types", &DebugTypes(types))
1201            .finish()
1202    }
1203}
1204
1205struct DebugDirectiveDefinitions<'a>(&'a IndexMap<Name, Node<DirectiveDefinition>>);
1206
1207struct DebugTypes<'a>(&'a IndexMap<Name, ExtendedType>);
1208
1209impl std::fmt::Debug for DebugDirectiveDefinitions<'_> {
1210    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1211        let mut map = f.debug_map();
1212        for (name, def) in self.0 {
1213            if !def.is_built_in() {
1214                map.entry(name, def);
1215            } else {
1216                map.entry(name, &format_args!("built_in_directive!({name:?})"));
1217            }
1218        }
1219        map.finish()
1220    }
1221}
1222
1223impl std::fmt::Debug for DebugTypes<'_> {
1224    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1225        let mut map = f.debug_map();
1226        for (name, def) in self.0 {
1227            if !def.is_built_in() {
1228                map.entry(name, def);
1229            } else {
1230                map.entry(name, &format_args!("built_in_type!({name:?})"));
1231            }
1232        }
1233        map.finish()
1234    }
1235}
1236
1237struct MetaFieldDefinitions {
1238    __typename: Component<FieldDefinition>,
1239    __schema: Component<FieldDefinition>,
1240    __type: Component<FieldDefinition>,
1241}
1242
1243impl MetaFieldDefinitions {
1244    fn get() -> &'static Self {
1245        static DEFS: OnceLock<MetaFieldDefinitions> = OnceLock::new();
1246        DEFS.get_or_init(|| Self {
1247            // __typename: String!
1248            __typename: Component::new(FieldDefinition {
1249                description: None,
1250                name: name!("__typename"),
1251                arguments: Vec::new(),
1252                ty: ty!(String!),
1253                directives: ast::DirectiveList::new(),
1254            }),
1255            // __schema: __Schema!
1256            __schema: Component::new(FieldDefinition {
1257                description: None,
1258                name: name!("__schema"),
1259                arguments: Vec::new(),
1260                ty: ty!(__Schema!),
1261                directives: ast::DirectiveList::new(),
1262            }),
1263            // __type(name: String!): __Type
1264            __type: Component::new(FieldDefinition {
1265                description: None,
1266                name: name!("__type"),
1267                arguments: vec![InputValueDefinition {
1268                    description: None,
1269                    name: name!("name"),
1270                    ty: ty!(String!).into(),
1271                    default_value: None,
1272                    directives: ast::DirectiveList::new(),
1273                }
1274                .into()],
1275                ty: ty!(__Type),
1276                directives: ast::DirectiveList::new(),
1277            }),
1278        })
1279    }
1280}