Skip to main content

reed_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 reed_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 reed_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("directive extension for undefined directive `@{name}`")]
311    OrphanDirectiveExtension { name: Name },
312
313    #[error("adding {describe_ext}, but `{name}` is {describe_def}")]
314    TypeExtensionKindMismatch {
315        name: Name,
316        describe_ext: &'static str,
317        def_location: Option<SourceSpan>,
318        describe_def: &'static str,
319    },
320
321    #[error("duplicate definitions for the `{operation_type}` root operation type")]
322    DuplicateRootOperation {
323        previous_location: Option<SourceSpan>,
324        operation_type: &'static str,
325    },
326
327    #[error(
328        "object type `{type_name}` implements interface `{name_at_previous_location}` \
329         more than once"
330    )]
331    DuplicateImplementsInterfaceInObject {
332        name_at_previous_location: Name,
333        type_name: Name,
334    },
335
336    #[error(
337        "interface type `{type_name}` implements interface `{name_at_previous_location}` \
338         more than once"
339    )]
340    DuplicateImplementsInterfaceInInterface {
341        name_at_previous_location: Name,
342        type_name: Name,
343    },
344
345    #[error(
346        "duplicate definitions for the `{name_at_previous_location}` \
347         field of object type `{type_name}`"
348    )]
349    ObjectFieldNameCollision {
350        name_at_previous_location: Name,
351        type_name: Name,
352    },
353
354    #[error(
355        "duplicate definitions for the `{name_at_previous_location}` \
356         field of interface type `{type_name}`"
357    )]
358    InterfaceFieldNameCollision {
359        name_at_previous_location: Name,
360        type_name: Name,
361    },
362
363    #[error(
364        "duplicate definitions for the `{name_at_previous_location}` \
365         value of enum type `{type_name}`"
366    )]
367    EnumValueNameCollision {
368        name_at_previous_location: Name,
369        type_name: Name,
370    },
371
372    #[error(
373        "duplicate definitions for the `{name_at_previous_location}` \
374         member of union type `{type_name}`"
375    )]
376    UnionMemberNameCollision {
377        name_at_previous_location: Name,
378        type_name: Name,
379    },
380
381    #[error(
382        "duplicate definitions for the `{name_at_previous_location}` \
383         field of input object type `{type_name}`"
384    )]
385    InputFieldNameCollision {
386        name_at_previous_location: Name,
387        type_name: Name,
388    },
389}
390
391/// Error type of [`Schema::type_field`]: could not find the requested field definition
392#[derive(Debug, Clone, PartialEq, Eq)]
393pub enum FieldLookupError<'schema> {
394    NoSuchType,
395    NoSuchField(&'schema NamedType, &'schema ExtendedType),
396}
397
398impl Schema {
399    /// Returns an (almost) empty schema.
400    ///
401    /// It starts with built-in directives, built-in scalars, and introspection types.
402    /// It can then be filled programatically.
403    #[allow(clippy::new_without_default)] // not a great implicit default in generic contexts
404    pub fn new() -> Self {
405        SchemaBuilder::new().build().unwrap()
406    }
407
408    /// Parse a single source file into a schema, with the default parser configuration.
409    ///
410    /// Create a [`Parser`] to use different parser configuration.
411    /// Use [`builder()`][Self::builder] to build a schema from multiple parsed files.
412    #[allow(clippy::result_large_err)] // Typically not called very often
413    pub fn parse(
414        source_text: impl Into<String>,
415        path: impl AsRef<Path>,
416    ) -> Result<Self, WithErrors<Self>> {
417        Parser::default().parse_schema(source_text, path)
418    }
419
420    /// [`parse`][Self::parse] then [`validate`][Self::validate],
421    /// to get a `Valid<Schema>` when mutating it isn’t needed.
422    #[allow(clippy::result_large_err)] // Typically not called very often
423    pub fn parse_and_validate(
424        source_text: impl Into<String>,
425        path: impl AsRef<Path>,
426    ) -> Result<Valid<Self>, WithErrors<Self>> {
427        let mut builder = Schema::builder();
428        Parser::default().parse_into_schema_builder(source_text, path, &mut builder);
429        let (mut schema, mut errors) = builder.build_inner();
430        validation::validate_schema(&mut errors, &mut schema);
431        errors.into_valid_result(schema)
432    }
433
434    /// Returns a new builder for creating a Schema from AST documents,
435    /// initialized with built-in directives, built-in scalars, and introspection types
436    ///
437    /// ```rust
438    /// use reed_compiler::Schema;
439    ///
440    /// let empty_schema = Schema::builder().build();
441    /// ```
442    pub fn builder() -> SchemaBuilder {
443        SchemaBuilder::new()
444    }
445
446    #[allow(clippy::result_large_err)] // Typically not called very often
447    pub fn validate(mut self) -> Result<Valid<Self>, WithErrors<Self>> {
448        let mut errors = DiagnosticList::new(self.sources.clone());
449        validation::validate_schema(&mut errors, &mut self);
450        errors.into_valid_result(self)
451    }
452
453    /// Returns the type with the given name, if it is a scalar type
454    pub fn get_scalar(&self, name: &str) -> Option<&Node<ScalarType>> {
455        if let Some(ExtendedType::Scalar(ty)) = self.types.get(name) {
456            Some(ty)
457        } else {
458            None
459        }
460    }
461
462    /// Returns the type with the given name, if it is a object type
463    pub fn get_object(&self, name: &str) -> Option<&Node<ObjectType>> {
464        if let Some(ExtendedType::Object(ty)) = self.types.get(name) {
465            Some(ty)
466        } else {
467            None
468        }
469    }
470
471    /// Returns the type with the given name, if it is a interface type
472    pub fn get_interface(&self, name: &str) -> Option<&Node<InterfaceType>> {
473        if let Some(ExtendedType::Interface(ty)) = self.types.get(name) {
474            Some(ty)
475        } else {
476            None
477        }
478    }
479
480    /// Returns the type with the given name, if it is a union type
481    pub fn get_union(&self, name: &str) -> Option<&Node<UnionType>> {
482        if let Some(ExtendedType::Union(ty)) = self.types.get(name) {
483            Some(ty)
484        } else {
485            None
486        }
487    }
488
489    /// Returns the type with the given name, if it is a enum type
490    pub fn get_enum(&self, name: &str) -> Option<&Node<EnumType>> {
491        if let Some(ExtendedType::Enum(ty)) = self.types.get(name) {
492            Some(ty)
493        } else {
494            None
495        }
496    }
497
498    /// Returns the type with the given name, if it is a input object type
499    pub fn get_input_object(&self, name: &str) -> Option<&Node<InputObjectType>> {
500        if let Some(ExtendedType::InputObject(ty)) = self.types.get(name) {
501            Some(ty)
502        } else {
503            None
504        }
505    }
506
507    /// Returns the name of the object type for the root operation with the given operation kind
508    pub fn root_operation(&self, operation_type: ast::OperationType) -> Option<&NamedType> {
509        match operation_type {
510            ast::OperationType::Query => &self.schema_definition.query,
511            ast::OperationType::Mutation => &self.schema_definition.mutation,
512            ast::OperationType::Subscription => &self.schema_definition.subscription,
513        }
514        .as_ref()
515        .map(|component| &component.name)
516    }
517
518    /// Returns the definition of a type’s explicit field or meta-field.
519    pub fn type_field(
520        &self,
521        type_name: &str,
522        field_name: &str,
523    ) -> Result<&Component<FieldDefinition>, FieldLookupError<'_>> {
524        use ExtendedType::*;
525        let (ty_def_name, ty_def) = self
526            .types
527            .get_key_value(type_name)
528            .ok_or(FieldLookupError::NoSuchType)?;
529        let explicit_field = match ty_def {
530            Object(ty) => ty.fields.get(field_name),
531            Interface(ty) => ty.fields.get(field_name),
532            Scalar(_) | Union(_) | Enum(_) | InputObject(_) => None,
533        };
534        if let Some(def) = explicit_field {
535            return Ok(def);
536        }
537        let meta = MetaFieldDefinitions::get();
538        if field_name == "__typename" && matches!(ty_def, Object(_) | Interface(_) | Union(_)) {
539            // .validate() errors for __typename at the root of a subscription operation
540            return Ok(&meta.__typename);
541        }
542        if self
543            .schema_definition
544            .query
545            .as_ref()
546            .is_some_and(|query_type| query_type == type_name)
547        {
548            match field_name {
549                "__schema" => return Ok(&meta.__schema),
550                "__type" => return Ok(&meta.__type),
551                _ => {}
552            }
553        }
554        Err(FieldLookupError::NoSuchField(ty_def_name, ty_def))
555    }
556
557    /// Returns a map of interface names to names of types that implement that interface
558    ///
559    /// `Schema` only stores the inverse relationship
560    /// (in [`ObjectType::implements_interfaces`] and [`InterfaceType::implements_interfaces`]),
561    /// so iterating the implementers of an interface requires a linear scan
562    /// of all types in the schema.
563    /// If that is repeated for multiple interfaces,
564    /// gathering them all at once amorticizes that cost.
565    pub fn implementers_map(&self) -> HashMap<Name, Implementers> {
566        let mut map = HashMap::<Name, Implementers>::default();
567        for (ty_name, ty) in &self.types {
568            match ty {
569                ExtendedType::Object(def) => {
570                    for interface in &def.implements_interfaces {
571                        map.entry(interface.name.clone())
572                            .or_default()
573                            .objects
574                            .insert(ty_name.clone());
575                    }
576                }
577                ExtendedType::Interface(def) => {
578                    for interface in &def.implements_interfaces {
579                        map.entry(interface.name.clone())
580                            .or_default()
581                            .interfaces
582                            .insert(ty_name.clone());
583                    }
584                }
585                ExtendedType::Scalar(_)
586                | ExtendedType::Union(_)
587                | ExtendedType::Enum(_)
588                | ExtendedType::InputObject(_) => (),
589            };
590        }
591        map
592    }
593
594    /// Returns whether `maybe_subtype` is a subtype of `abstract_type`, which means either:
595    ///
596    /// * `maybe_subtype` implements the interface `abstract_type`
597    /// * `maybe_subtype` is a member of the union type `abstract_type`
598    pub fn is_subtype(&self, abstract_type: &str, maybe_subtype: &str) -> bool {
599        self.types.get(abstract_type).is_some_and(|ty| match ty {
600            ExtendedType::Interface(_) => self.types.get(maybe_subtype).is_some_and(|ty2| {
601                match ty2 {
602                    ExtendedType::Object(def) => &def.implements_interfaces,
603                    ExtendedType::Interface(def) => &def.implements_interfaces,
604                    ExtendedType::Scalar(_)
605                    | ExtendedType::Union(_)
606                    | ExtendedType::Enum(_)
607                    | ExtendedType::InputObject(_) => return false,
608                }
609                .contains(abstract_type)
610            }),
611            ExtendedType::Union(def) => def.members.contains(maybe_subtype),
612            ExtendedType::Scalar(_)
613            | ExtendedType::Object(_)
614            | ExtendedType::Enum(_)
615            | ExtendedType::InputObject(_) => false,
616        })
617    }
618
619    /// Returns whether the type `ty` is defined as is an input type
620    ///
621    /// <https://spec.graphql.org/October2021/#sec-Input-and-Output-Types>
622    pub fn is_input_type(&self, ty: &Type) -> bool {
623        match self.types.get(ty.inner_named_type()) {
624            Some(ExtendedType::Scalar(_))
625            | Some(ExtendedType::Enum(_))
626            | Some(ExtendedType::InputObject(_)) => true,
627            Some(ExtendedType::Object(_))
628            | Some(ExtendedType::Interface(_))
629            | Some(ExtendedType::Union(_))
630            | None => false,
631        }
632    }
633
634    /// Returns whether the type `ty` is defined as is an output type
635    ///
636    /// <https://spec.graphql.org/October2021/#sec-Input-and-Output-Types>
637    pub fn is_output_type(&self, ty: &Type) -> bool {
638        match self.types.get(ty.inner_named_type()) {
639            Some(ExtendedType::Scalar(_))
640            | Some(ExtendedType::Object(_))
641            | Some(ExtendedType::Interface(_))
642            | Some(ExtendedType::Union(_))
643            | Some(ExtendedType::Enum(_)) => true,
644            Some(ExtendedType::InputObject(_)) | None => false,
645        }
646    }
647
648    serialize_method!();
649}
650
651impl SchemaDefinition {
652    pub fn iter_root_operations(
653        &self,
654    ) -> impl Iterator<Item = (ast::OperationType, &ComponentName)> {
655        [
656            (ast::OperationType::Query, &self.query),
657            (ast::OperationType::Mutation, &self.mutation),
658            (ast::OperationType::Subscription, &self.subscription),
659        ]
660        .into_iter()
661        .filter_map(|(ty, maybe_op)| maybe_op.as_ref().map(|op| (ty, op)))
662    }
663
664    /// Iterate over the `origins` of all components
665    ///
666    /// The order of the returned set is unspecified but deterministic
667    /// for a given reed-compiler version.
668    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
669        self.directives
670            .iter()
671            .map(|dir| &dir.origin)
672            .chain(self.query.iter().map(|name| &name.origin))
673            .chain(self.mutation.iter().map(|name| &name.origin))
674            .chain(self.subscription.iter().map(|name| &name.origin))
675    }
676
677    /// Collect `schema` extensions that contribute any component
678    ///
679    /// The order of the returned set is unspecified but deterministic
680    /// for a given reed-compiler version.
681    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
682        self.iter_origins()
683            .filter_map(|origin| origin.extension_id())
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    /// Iterate over the `origins` of all components
861    ///
862    /// The order of the returned set is unspecified but deterministic
863    /// for a given reed-compiler version.
864    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
865        match self {
866            Self::Scalar(ty) => Box::new(ty.iter_origins()) as Box<dyn Iterator<Item = _>>,
867            Self::Object(ty) => Box::new(ty.iter_origins()),
868            Self::Interface(ty) => Box::new(ty.iter_origins()),
869            Self::Union(ty) => Box::new(ty.iter_origins()),
870            Self::Enum(ty) => Box::new(ty.iter_origins()),
871            Self::InputObject(ty) => Box::new(ty.iter_origins()),
872        }
873    }
874
875    /// Collect `schema` extensions that contribute any component
876    ///
877    /// The order of the returned set is unspecified but deterministic
878    /// for a given reed-compiler version.
879    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
880        self.iter_origins()
881            .filter_map(|origin| origin.extension_id())
882            .collect()
883    }
884
885    serialize_method!();
886}
887
888impl ScalarType {
889    /// Iterate over the `origins` of all components
890    ///
891    /// The order of the returned set is unspecified but deterministic
892    /// for a given reed-compiler version.
893    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
894        self.directives.iter().map(|dir| &dir.origin)
895    }
896
897    /// Collect scalar type extensions that contribute any component
898    ///
899    /// The order of the returned set is unspecified but deterministic
900    /// for a given reed-compiler version.
901    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
902        self.iter_origins()
903            .filter_map(|origin| origin.extension_id())
904            .collect()
905    }
906
907    serialize_method!();
908}
909
910impl ObjectType {
911    /// Iterate over the `origins` of all components
912    ///
913    /// The order of the returned set is unspecified but deterministic
914    /// for a given reed-compiler version.
915    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
916        self.directives
917            .iter()
918            .map(|dir| &dir.origin)
919            .chain(
920                self.implements_interfaces
921                    .iter()
922                    .map(|component| &component.origin),
923            )
924            .chain(self.fields.values().map(|field| &field.origin))
925    }
926
927    /// Collect object type extensions that contribute any component
928    ///
929    /// The order of the returned set is unspecified but deterministic
930    /// for a given reed-compiler version.
931    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
932        self.iter_origins()
933            .filter_map(|origin| origin.extension_id())
934            .collect()
935    }
936
937    serialize_method!();
938}
939
940impl InterfaceType {
941    /// Iterate over the `origins` of all components
942    ///
943    /// The order of the returned set is unspecified but deterministic
944    /// for a given reed-compiler version.
945    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
946        self.directives
947            .iter()
948            .map(|dir| &dir.origin)
949            .chain(
950                self.implements_interfaces
951                    .iter()
952                    .map(|component| &component.origin),
953            )
954            .chain(self.fields.values().map(|field| &field.origin))
955    }
956
957    /// Collect interface type extensions that contribute any component
958    ///
959    /// The order of the returned set is unspecified but deterministic
960    /// for a given reed-compiler version.
961    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
962        self.iter_origins()
963            .filter_map(|origin| origin.extension_id())
964            .collect()
965    }
966
967    serialize_method!();
968}
969
970impl UnionType {
971    /// Iterate over the `origins` of all components
972    ///
973    /// The order of the returned set is unspecified but deterministic
974    /// for a given reed-compiler version.
975    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
976        self.directives
977            .iter()
978            .map(|dir| &dir.origin)
979            .chain(self.members.iter().map(|component| &component.origin))
980    }
981
982    /// Collect union type extensions that contribute any component
983    ///
984    /// The order of the returned set is unspecified but deterministic
985    /// for a given reed-compiler version.
986    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
987        self.iter_origins()
988            .filter_map(|origin| origin.extension_id())
989            .collect()
990    }
991
992    serialize_method!();
993}
994
995impl EnumType {
996    /// Iterate over the `origins` of all components
997    ///
998    /// The order of the returned set is unspecified but deterministic
999    /// for a given reed-compiler version.
1000    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
1001        self.directives
1002            .iter()
1003            .map(|dir| &dir.origin)
1004            .chain(self.values.values().map(|value| &value.origin))
1005    }
1006
1007    /// Collect enum type extensions that contribute any component
1008    ///
1009    /// The order of the returned set is unspecified but deterministic
1010    /// for a given reed-compiler version.
1011    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
1012        self.iter_origins()
1013            .filter_map(|origin| origin.extension_id())
1014            .collect()
1015    }
1016
1017    serialize_method!();
1018}
1019
1020impl InputObjectType {
1021    /// Iterate over the `origins` of all components
1022    ///
1023    /// The order of the returned set is unspecified but deterministic
1024    /// for a given reed-compiler version.
1025    pub fn iter_origins(&self) -> impl Iterator<Item = &ComponentOrigin> {
1026        self.directives
1027            .iter()
1028            .map(|dir| &dir.origin)
1029            .chain(self.fields.values().map(|field| &field.origin))
1030    }
1031
1032    /// Collect input object type extensions that contribute any component
1033    ///
1034    /// The order of the returned set is unspecified but deterministic
1035    /// for a given reed-compiler version.
1036    pub fn extensions(&self) -> IndexSet<&ExtensionId> {
1037        self.iter_origins()
1038            .filter_map(|origin| origin.extension_id())
1039            .collect()
1040    }
1041
1042    serialize_method!();
1043}
1044
1045impl DirectiveList {
1046    pub const fn new() -> Self {
1047        Self(Vec::new())
1048    }
1049
1050    /// Returns an iterator of directives with the given name.
1051    ///
1052    /// This method is best for repeatable directives.
1053    /// See also [`get`][Self::get] for non-repeatable directives.
1054    pub fn get_all<'def: 'name, 'name>(
1055        &'def self,
1056        name: &'name str,
1057    ) -> impl Iterator<Item = &'def Component<Directive>> + 'name {
1058        self.0.iter().filter(move |dir| dir.name == name)
1059    }
1060
1061    /// Returns the first directive with the given name, if any.
1062    ///
1063    /// This method is best for non-repeatable directives.
1064    /// See also [`get_all`][Self::get_all] for repeatable directives.
1065    pub fn get(&self, name: &str) -> Option<&Component<Directive>> {
1066        self.get_all(name).next()
1067    }
1068
1069    /// Returns whether there is a directive with the given name
1070    pub fn has(&self, name: &str) -> bool {
1071        self.get(name).is_some()
1072    }
1073
1074    pub(crate) fn iter_ast(&self) -> impl Iterator<Item = &Node<ast::Directive>> {
1075        self.0.iter().map(|component| &component.node)
1076    }
1077
1078    /// Accepts either [`Component<Directive>`], [`Node<Directive>`], or [`Directive`].
1079    pub fn push(&mut self, directive: impl Into<Component<Directive>>) {
1080        self.0.push(directive.into());
1081    }
1082
1083    serialize_method!();
1084}
1085
1086impl std::fmt::Debug for DirectiveList {
1087    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1088        self.0.fmt(f)
1089    }
1090}
1091
1092impl std::ops::Deref for DirectiveList {
1093    type Target = Vec<Component<Directive>>;
1094
1095    fn deref(&self) -> &Self::Target {
1096        &self.0
1097    }
1098}
1099
1100impl std::ops::DerefMut for DirectiveList {
1101    fn deref_mut(&mut self) -> &mut Self::Target {
1102        &mut self.0
1103    }
1104}
1105
1106impl IntoIterator for DirectiveList {
1107    type Item = Component<Directive>;
1108
1109    type IntoIter = std::vec::IntoIter<Component<Directive>>;
1110
1111    fn into_iter(self) -> Self::IntoIter {
1112        self.0.into_iter()
1113    }
1114}
1115
1116impl<'a> IntoIterator for &'a DirectiveList {
1117    type Item = &'a Component<Directive>;
1118
1119    type IntoIter = std::slice::Iter<'a, Component<Directive>>;
1120
1121    fn into_iter(self) -> Self::IntoIter {
1122        self.0.iter()
1123    }
1124}
1125
1126impl<'a> IntoIterator for &'a mut DirectiveList {
1127    type Item = &'a mut Component<Directive>;
1128
1129    type IntoIter = std::slice::IterMut<'a, Component<Directive>>;
1130
1131    fn into_iter(self) -> Self::IntoIter {
1132        self.0.iter_mut()
1133    }
1134}
1135
1136impl<D> FromIterator<D> for DirectiveList
1137where
1138    D: Into<Component<Directive>>,
1139{
1140    fn from_iter<T: IntoIterator<Item = D>>(iter: T) -> Self {
1141        Self(iter.into_iter().map(Into::into).collect())
1142    }
1143}
1144
1145impl Eq for Schema {}
1146
1147impl PartialEq for Schema {
1148    fn eq(&self, other: &Self) -> bool {
1149        let Self {
1150            sources: _, // ignored
1151            schema_definition,
1152            directive_definitions,
1153            types,
1154        } = self;
1155        *schema_definition == other.schema_definition
1156            && *directive_definitions == other.directive_definitions
1157            && *types == other.types
1158    }
1159}
1160
1161impl Implementers {
1162    /// Iterate over all implementers, including objects and interfaces.
1163    ///
1164    /// The iteration order is unspecified.
1165    pub fn iter(&self) -> impl Iterator<Item = &'_ Name> {
1166        self.objects.iter().chain(&self.interfaces)
1167    }
1168}
1169
1170impl From<Node<ScalarType>> for ExtendedType {
1171    fn from(ty: Node<ScalarType>) -> Self {
1172        Self::Scalar(ty)
1173    }
1174}
1175
1176impl From<Node<ObjectType>> for ExtendedType {
1177    fn from(ty: Node<ObjectType>) -> Self {
1178        Self::Object(ty)
1179    }
1180}
1181
1182impl From<Node<InterfaceType>> for ExtendedType {
1183    fn from(ty: Node<InterfaceType>) -> Self {
1184        Self::Interface(ty)
1185    }
1186}
1187
1188impl From<Node<UnionType>> for ExtendedType {
1189    fn from(ty: Node<UnionType>) -> Self {
1190        Self::Union(ty)
1191    }
1192}
1193
1194impl From<Node<EnumType>> for ExtendedType {
1195    fn from(ty: Node<EnumType>) -> Self {
1196        Self::Enum(ty)
1197    }
1198}
1199
1200impl From<Node<InputObjectType>> for ExtendedType {
1201    fn from(ty: Node<InputObjectType>) -> Self {
1202        Self::InputObject(ty)
1203    }
1204}
1205
1206impl From<ScalarType> for ExtendedType {
1207    fn from(ty: ScalarType) -> Self {
1208        Self::Scalar(ty.into())
1209    }
1210}
1211
1212impl From<ObjectType> for ExtendedType {
1213    fn from(ty: ObjectType) -> Self {
1214        Self::Object(ty.into())
1215    }
1216}
1217
1218impl From<InterfaceType> for ExtendedType {
1219    fn from(ty: InterfaceType) -> Self {
1220        Self::Interface(ty.into())
1221    }
1222}
1223
1224impl From<UnionType> for ExtendedType {
1225    fn from(ty: UnionType) -> Self {
1226        Self::Union(ty.into())
1227    }
1228}
1229
1230impl From<EnumType> for ExtendedType {
1231    fn from(ty: EnumType) -> Self {
1232        Self::Enum(ty.into())
1233    }
1234}
1235
1236impl From<InputObjectType> for ExtendedType {
1237    fn from(ty: InputObjectType) -> Self {
1238        Self::InputObject(ty.into())
1239    }
1240}
1241
1242impl std::fmt::Debug for Schema {
1243    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1244        let Self {
1245            sources,
1246            schema_definition,
1247            directive_definitions,
1248            types,
1249        } = self;
1250        f.debug_struct("Schema")
1251            .field("sources", sources)
1252            .field("schema_definition", schema_definition)
1253            .field(
1254                "directive_definitions",
1255                &DebugDirectiveDefinitions(directive_definitions),
1256            )
1257            .field("types", &DebugTypes(types))
1258            .finish()
1259    }
1260}
1261
1262struct DebugDirectiveDefinitions<'a>(&'a IndexMap<Name, Node<DirectiveDefinition>>);
1263
1264struct DebugTypes<'a>(&'a IndexMap<Name, ExtendedType>);
1265
1266impl std::fmt::Debug for DebugDirectiveDefinitions<'_> {
1267    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1268        let mut map = f.debug_map();
1269        for (name, def) in self.0 {
1270            if !def.is_built_in() {
1271                map.entry(name, def);
1272            } else {
1273                map.entry(name, &format_args!("built_in_directive!({name:?})"));
1274            }
1275        }
1276        map.finish()
1277    }
1278}
1279
1280impl std::fmt::Debug for DebugTypes<'_> {
1281    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1282        let mut map = f.debug_map();
1283        for (name, def) in self.0 {
1284            if !def.is_built_in() {
1285                map.entry(name, def);
1286            } else {
1287                map.entry(name, &format_args!("built_in_type!({name:?})"));
1288            }
1289        }
1290        map.finish()
1291    }
1292}
1293
1294struct MetaFieldDefinitions {
1295    __typename: Component<FieldDefinition>,
1296    __schema: Component<FieldDefinition>,
1297    __type: Component<FieldDefinition>,
1298}
1299
1300impl MetaFieldDefinitions {
1301    fn get() -> &'static Self {
1302        static DEFS: OnceLock<MetaFieldDefinitions> = OnceLock::new();
1303        DEFS.get_or_init(|| Self {
1304            // __typename: String!
1305            __typename: Component::new(FieldDefinition {
1306                description: None,
1307                name: name!("__typename"),
1308                arguments: Vec::new(),
1309                ty: ty!(String!),
1310                directives: ast::DirectiveList::new(),
1311            }),
1312            // __schema: __Schema!
1313            __schema: Component::new(FieldDefinition {
1314                description: None,
1315                name: name!("__schema"),
1316                arguments: Vec::new(),
1317                ty: ty!(__Schema!),
1318                directives: ast::DirectiveList::new(),
1319            }),
1320            // __type(name: String!): __Type
1321            __type: Component::new(FieldDefinition {
1322                description: None,
1323                name: name!("__type"),
1324                arguments: vec![InputValueDefinition {
1325                    description: None,
1326                    name: name!("name"),
1327                    ty: ty!(String!).into(),
1328                    default_value: None,
1329                    directives: ast::DirectiveList::new(),
1330                }
1331                .into()],
1332                ty: ty!(__Type),
1333                directives: ast::DirectiveList::new(),
1334            }),
1335        })
1336    }
1337}