Skip to main content

apollo_compiler/ast/
mod.rs

1//! *Abstract Syntax Tree* for GraphQL documents.
2//! An AST [`Document`] is more permissive but lower-level than [`Schema`][crate::Schema]
3//! or [`ExecutableDocument`][crate::ExecutableDocument].
4//!
5//! This AST aims to faithfully represent documents
6//! that conform to the GraphQL [syntactic grammar],
7//! except that [ignored tokens] such as whitespace are not preserved.
8//! These documents may or may not be [valid].
9//!
10//! Parsing an input that does not conform to the grammar results in parse errors
11//! together with a partial AST.
12//!
13//! [ignored tokens]: https://spec.graphql.org/September2025/#Ignored
14//! [syntactic grammar]: https://spec.graphql.org/September2025/#sec-Language
15//! [valid]: https://spec.graphql.org/September2025/#sec-Validation
16//!
17//! ## Parsing
18//!
19//! Start with [`Document::parse`], or [`Parser`][crate::parser::Parser]
20//! to change the parser configuration.
21//!
22//! ## Structural sharing and mutation
23//!
24//! Nodes inside documents are wrapped in [`Node`], a reference-counted smart pointer.
25//! This allows sharing nodes between documents without cloning entire subtrees.
26//! To modify a node, the [`make_mut`][Node::make_mut] method provides copy-on-write semantics.
27//!
28//! ## Serialization
29//!
30//! [`Document`] and its node types implement [`Display`][std::fmt::Display]
31//! and [`ToString`] by serializing to GraphQL syntax with a default configuration.
32//! [`serialize`][Document::serialize] methods return a builder
33//! that has chaining methods for setting serialization configuration,
34//! and also implements `Display` and `ToString`.
35//!
36//! ## Example
37//!
38//! ```
39//! use apollo_compiler::{ast, name};
40//!
41//! let source = "{field}";
42//! let mut doc = ast::Document::parse(source, "example.graphql").unwrap();
43//! for def in &mut doc.definitions {
44//!     if let ast::Definition::OperationDefinition(op) = def {
45//!         // `op` has type `&mut Node<ast::OperationDefinition>`
46//!         // `Node` implements `Deref` but not `DeferMut`
47//!         // `make_mut()` clones if necessary and returns `&mut ast::OperationDefinition`
48//!         op.make_mut().directives.push(ast::Directive::new(name!(dir)));
49//!     }
50//! }
51//! assert_eq!(doc.serialize().no_indent().to_string(), "query @dir { field }")
52//! ```
53
54use crate::collections::eq_unique_by_name;
55use crate::collections::hash_unordered;
56use crate::collections::IndexSet;
57use crate::parser::SourceMap;
58use crate::Name;
59use crate::Node;
60use std::hash::Hash;
61use std::hash::Hasher;
62
63pub(crate) mod from_cst;
64pub(crate) mod impls;
65pub(crate) mod serialize;
66
67pub use self::serialize::Serialize;
68
69/// AST for a GraphQL [_Document_](https://spec.graphql.org/September2025/#Document)
70/// that can contain executable definitions, type system (schema) definitions, or both.
71///
72/// It is typically parsed from one `&str` input “file” but can be also be synthesized
73/// programatically.
74#[derive(Clone)]
75pub struct Document {
76    /// If this document was originally parsed from a source file,
77    /// this map contains one entry for that file and its ID.
78    ///
79    /// The document is [mutable][crate::ast#structural-sharing-and-mutation]
80    /// so it may have been modified since.
81    pub sources: SourceMap,
82
83    pub definitions: Vec<Definition>,
84}
85
86const _: () = {
87    const fn assert_send<T: Send>() {}
88    const fn assert_sync<T: Sync>() {}
89    assert_send::<Document>();
90    assert_sync::<Document>();
91};
92
93/// A [_NamedType_](https://spec.graphql.org/September2025/#NamedType)
94/// references by name a GraphQL type defined elsewhere.
95pub type NamedType = Name;
96
97/// AST for a top-level [_Definition_](https://spec.graphql.org/September2025/#Definition) of any kind:
98/// executable, type system, or type system extension.
99#[derive(Clone, Eq, PartialEq, Hash)]
100pub enum Definition {
101    OperationDefinition(Node<OperationDefinition>),
102    FragmentDefinition(Node<FragmentDefinition>),
103    DirectiveDefinition(Node<DirectiveDefinition>),
104    SchemaDefinition(Node<SchemaDefinition>),
105    ScalarTypeDefinition(Node<ScalarTypeDefinition>),
106    ObjectTypeDefinition(Node<ObjectTypeDefinition>),
107    InterfaceTypeDefinition(Node<InterfaceTypeDefinition>),
108    UnionTypeDefinition(Node<UnionTypeDefinition>),
109    EnumTypeDefinition(Node<EnumTypeDefinition>),
110    InputObjectTypeDefinition(Node<InputObjectTypeDefinition>),
111    SchemaExtension(Node<SchemaExtension>),
112    ScalarTypeExtension(Node<ScalarTypeExtension>),
113    ObjectTypeExtension(Node<ObjectTypeExtension>),
114    InterfaceTypeExtension(Node<InterfaceTypeExtension>),
115    UnionTypeExtension(Node<UnionTypeExtension>),
116    EnumTypeExtension(Node<EnumTypeExtension>),
117    InputObjectTypeExtension(Node<InputObjectTypeExtension>),
118}
119
120/// Executable AST for an
121/// [_OperationDefinition_](https://spec.graphql.org/September2025/#OperationDefinition).
122#[derive(Clone, Debug, Eq, PartialEq, Hash)]
123pub struct OperationDefinition {
124    pub description: Option<Node<str>>,
125    pub operation_type: OperationType,
126    pub name: Option<Name>,
127    pub variables: Vec<Node<VariableDefinition>>,
128    pub directives: DirectiveList,
129    pub selection_set: Vec<Selection>,
130}
131
132/// Executable AST for a
133/// [_FragmentDefinition_](https://spec.graphql.org/September2025/#FragmentDefinition).
134#[derive(Clone, Debug, Eq, PartialEq, Hash)]
135pub struct FragmentDefinition {
136    pub description: Option<Node<str>>,
137    pub name: Name,
138    pub type_condition: NamedType,
139    pub directives: DirectiveList,
140    pub selection_set: Vec<Selection>,
141}
142
143/// Type system AST for a `directive @foo`
144/// [_DirectiveDefinition_](https://spec.graphql.org/September2025/#DirectiveDefinition).
145#[derive(Clone, Debug, Eq)]
146pub struct DirectiveDefinition {
147    pub description: Option<Node<str>>,
148    pub name: Name,
149    pub arguments: Vec<Node<InputValueDefinition>>,
150    pub repeatable: bool,
151    pub locations: IndexSet<DirectiveLocation>,
152}
153
154impl PartialEq for DirectiveDefinition {
155    fn eq(&self, other: &Self) -> bool {
156        self.description == other.description
157            && self.name == other.name
158            && eq_unique_by_name(&self.arguments, &other.arguments, |a| &a.name)
159            && self.repeatable == other.repeatable
160            && self.locations == other.locations
161    }
162}
163
164impl Hash for DirectiveDefinition {
165    fn hash<H: Hasher>(&self, state: &mut H) {
166        self.description.hash(state);
167        self.name.hash(state);
168        hash_unordered(self.arguments.iter(), state, self.arguments.len());
169        self.repeatable.hash(state);
170        hash_unordered(self.locations.iter(), state, self.locations.len());
171    }
172}
173
174/// Type system AST for a `schema`
175/// [_SchemaDefinition_](https://spec.graphql.org/September2025/#SchemaDefinition).
176#[derive(Clone, Debug, Eq, PartialEq, Hash)]
177pub struct SchemaDefinition {
178    pub description: Option<Node<str>>,
179    pub directives: DirectiveList,
180    pub root_operations: Vec<Node<(OperationType, NamedType)>>,
181}
182
183/// Type system AST for a `scalar FooS`
184/// [_ScalarTypeDefinition_](https://spec.graphql.org/September2025/#ScalarTypeDefinition).
185#[derive(Clone, Debug, Eq, PartialEq, Hash)]
186pub struct ScalarTypeDefinition {
187    pub description: Option<Node<str>>,
188    pub name: Name,
189    pub directives: DirectiveList,
190}
191
192/// Type system AST for a `type FooO`
193/// [_ObjectTypeDefinition_](https://spec.graphql.org/September2025/#ObjectTypeDefinition).
194#[derive(Clone, Debug, Eq, PartialEq, Hash)]
195pub struct ObjectTypeDefinition {
196    pub description: Option<Node<str>>,
197    pub name: Name,
198    pub implements_interfaces: Vec<Name>,
199    pub directives: DirectiveList,
200    pub fields: Vec<Node<FieldDefinition>>,
201}
202
203/// Type system AST for an `interface FooI`
204/// [_InterfaceTypeDefinition_](https://spec.graphql.org/September2025/#InterfaceTypeDefinition).
205#[derive(Clone, Debug, Eq, PartialEq, Hash)]
206pub struct InterfaceTypeDefinition {
207    pub description: Option<Node<str>>,
208    pub name: Name,
209    pub implements_interfaces: Vec<Name>,
210    pub directives: DirectiveList,
211    pub fields: Vec<Node<FieldDefinition>>,
212}
213
214/// Type system AST for a `union FooU`
215/// [_UnionTypeDefinition_](https://spec.graphql.org/September2025/#UnionTypeDefinition).
216#[derive(Clone, Debug, Eq, PartialEq, Hash)]
217pub struct UnionTypeDefinition {
218    pub description: Option<Node<str>>,
219    pub name: Name,
220    pub directives: DirectiveList,
221    pub members: Vec<NamedType>,
222}
223
224/// Type system AST for an `enum FooE`
225/// [_EnumTypeDefinition_](https://spec.graphql.org/September2025/#EnumTypeDefinition).
226#[derive(Clone, Debug, Eq, PartialEq, Hash)]
227pub struct EnumTypeDefinition {
228    pub description: Option<Node<str>>,
229    pub name: Name,
230    pub directives: DirectiveList,
231    pub values: Vec<Node<EnumValueDefinition>>,
232}
233
234/// Type system AST for an `input FooIn`
235/// [_InputObjectTypeDefinition_](https://spec.graphql.org/September2025/#InputObjectTypeDefinition).
236#[derive(Clone, Debug, Eq, PartialEq, Hash)]
237pub struct InputObjectTypeDefinition {
238    pub description: Option<Node<str>>,
239    pub name: Name,
240    pub directives: DirectiveList,
241    pub fields: Vec<Node<InputValueDefinition>>,
242}
243
244/// Type system AST for an `extend schema`
245/// [_SchemaExtension_](https://spec.graphql.org/September2025/#SchemaExtension).
246#[derive(Clone, Debug, Eq, PartialEq, Hash)]
247pub struct SchemaExtension {
248    pub directives: DirectiveList,
249    pub root_operations: Vec<Node<(OperationType, NamedType)>>,
250}
251
252/// Type system AST for an `extend scalar FooS`
253/// [_ScalarTypeExtension_](https://spec.graphql.org/September2025/#ScalarTypeExtension).
254#[derive(Clone, Debug, Eq, PartialEq, Hash)]
255pub struct ScalarTypeExtension {
256    pub name: Name,
257    pub directives: DirectiveList,
258}
259
260/// Type system AST for an `extend type FooO`
261/// [_ObjectTypeExtension_](https://spec.graphql.org/September2025/#ObjectTypeExtension).
262#[derive(Clone, Debug, Eq, PartialEq, Hash)]
263pub struct ObjectTypeExtension {
264    pub name: Name,
265    pub implements_interfaces: Vec<Name>,
266    pub directives: DirectiveList,
267    pub fields: Vec<Node<FieldDefinition>>,
268}
269
270/// Type system AST for an `extend interface FooI`
271/// [_InterfaceTypeExtension_](https://spec.graphql.org/September2025/#InterfaceTypeExtension).
272#[derive(Clone, Debug, Eq, PartialEq, Hash)]
273pub struct InterfaceTypeExtension {
274    pub name: Name,
275    pub implements_interfaces: Vec<Name>,
276    pub directives: DirectiveList,
277    pub fields: Vec<Node<FieldDefinition>>,
278}
279
280/// Type system AST for an `extend union FooU`
281/// [_UnionTypeExtension_](https://spec.graphql.org/September2025/#UnionTypeExtension).
282#[derive(Clone, Debug, Eq, PartialEq, Hash)]
283pub struct UnionTypeExtension {
284    pub name: Name,
285    pub directives: DirectiveList,
286    pub members: Vec<NamedType>,
287}
288
289/// Type system AST for an `extend enum FooE`
290/// [_EnumTypeExtension_](https://spec.graphql.org/September2025/#EnumTypeExtension).
291#[derive(Clone, Debug, Eq, PartialEq, Hash)]
292pub struct EnumTypeExtension {
293    pub name: Name,
294    pub directives: DirectiveList,
295    pub values: Vec<Node<EnumValueDefinition>>,
296}
297
298/// Type system AST for an `extend input FooIn`
299/// [_InputObjectTypeExtension_](https://spec.graphql.org/September2025/#InputObjectTypeExtension).
300#[derive(Clone, Debug, Eq, PartialEq, Hash)]
301pub struct InputObjectTypeExtension {
302    pub name: Name,
303    pub directives: DirectiveList,
304    pub fields: Vec<Node<InputValueDefinition>>,
305}
306
307/// AST for an [_Argument_](https://spec.graphql.org/September2025/#Argument)
308/// of a [`Field`] selection or [`Directive`] application.
309#[derive(Clone, Debug, Eq, PartialEq, Hash)]
310pub struct Argument {
311    pub name: Name,
312    pub value: Node<Value>,
313}
314
315/// The list of [_Directives_](https://spec.graphql.org/September2025/#Directives)
316/// applied to some context.
317///
318/// This type is used in both AST and high-level [`Schema`][crate::Schema]
319/// representations. In a schema, each directive [`Node`] tracks whether it
320/// comes from the “main” definition or from an extension
321/// through its [`origin`][Node::origin].
322#[derive(Clone, Eq, PartialEq, Hash, Default)]
323pub struct DirectiveList(pub Vec<Node<Directive>>);
324
325/// AST for a [_Directive_](https://spec.graphql.org/September2025/#Directive) application.
326#[derive(Clone, Debug, Eq)]
327pub struct Directive {
328    pub name: Name,
329    pub arguments: Vec<Node<Argument>>,
330}
331
332impl PartialEq for Directive {
333    fn eq(&self, other: &Self) -> bool {
334        self.name == other.name && eq_unique_by_name(&self.arguments, &other.arguments, |a| &a.name)
335    }
336}
337
338impl Hash for Directive {
339    fn hash<H: Hasher>(&self, state: &mut H) {
340        self.name.hash(state);
341        hash_unordered(self.arguments.iter(), state, self.arguments.len());
342    }
343}
344
345/// AST for the [_OperationType_](https://spec.graphql.org/September2025/#OperationType)
346/// of an [`OperationDefinition`] or [`RootOperationDefinition`][SchemaDefinition::root_operations].
347#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
348pub enum OperationType {
349    Query,
350    Mutation,
351    Subscription,
352}
353
354/// AST for a [_DirectiveLocation_](https://spec.graphql.org/September2025/#DirectiveLocation)
355/// of a [`DirectiveDefinition`].
356#[derive(Copy, Clone, Hash, PartialEq, Eq)]
357pub enum DirectiveLocation {
358    Query,
359    Mutation,
360    Subscription,
361    Field,
362    FragmentDefinition,
363    FragmentSpread,
364    InlineFragment,
365    VariableDefinition,
366    Schema,
367    Scalar,
368    Object,
369    FieldDefinition,
370    ArgumentDefinition,
371    Interface,
372    Union,
373    Enum,
374    EnumValue,
375    InputObject,
376    InputFieldDefinition,
377}
378
379/// Executable AST for a [_VariableDefinition_](https://spec.graphql.org/September2025/#VariableDefinition)
380/// in an [`OperationDefinition`].
381#[derive(Clone, Debug, Eq, PartialEq, Hash)]
382pub struct VariableDefinition {
383    pub description: Option<Node<str>>,
384    pub name: Name,
385    pub ty: Node<Type>,
386    pub default_value: Option<Node<Value>>,
387    pub directives: DirectiveList,
388}
389
390/// Type system AST for a reference to a GraphQL [_Type_](https://spec.graphql.org/September2025/#Type)
391#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
392pub enum Type {
393    /// A `Foo` reference to nullable named type
394    Named(NamedType),
395
396    /// A `Foo!` reference to non-null named type
397    NonNullNamed(NamedType),
398
399    /// A `[…]` reference to nullable list type.
400    /// (The inner item type may or may not be nullable, or a nested list.)
401    List(Box<Type>),
402
403    /// A `[…]!` reference to non-null list type.
404    /// (The inner item type may or may not be nullable, or a nested list.)
405    NonNullList(Box<Type>),
406}
407
408/// Type system AST for a [_FieldDefinition_](https://spec.graphql.org/September2025/#FieldDefinition)
409/// in an object type or interface type defintion or extension.
410#[derive(Clone, Debug, Eq)]
411pub struct FieldDefinition {
412    pub description: Option<Node<str>>,
413    pub name: Name,
414    pub arguments: Vec<Node<InputValueDefinition>>,
415    pub ty: Type,
416    pub directives: DirectiveList,
417}
418
419impl PartialEq for FieldDefinition {
420    fn eq(&self, other: &Self) -> bool {
421        self.description == other.description
422            && self.name == other.name
423            && eq_unique_by_name(&self.arguments, &other.arguments, |a| &a.name)
424            && self.ty == other.ty
425            && self.directives == other.directives
426    }
427}
428
429impl Hash for FieldDefinition {
430    fn hash<H: Hasher>(&self, state: &mut H) {
431        self.description.hash(state);
432        self.name.hash(state);
433        hash_unordered(self.arguments.iter(), state, self.arguments.len());
434        self.ty.hash(state);
435        self.directives.hash(state);
436    }
437}
438
439/// Type system AST for an
440/// [_InputValueDefinition_](https://spec.graphql.org/September2025/#InputValueDefinition),
441/// a input type field definition or an argument definition.
442#[derive(Clone, Debug, Eq, PartialEq, Hash)]
443pub struct InputValueDefinition {
444    pub description: Option<Node<str>>,
445    pub name: Name,
446    pub ty: Node<Type>,
447    pub default_value: Option<Node<Value>>,
448    pub directives: DirectiveList,
449}
450
451/// Type system AST for an
452/// [_EnumValueDefinition_](https://spec.graphql.org/September2025/#EnumValueDefinition)
453/// in an enum type definition or extension.
454#[derive(Clone, Debug, Eq, PartialEq, Hash)]
455pub struct EnumValueDefinition {
456    pub description: Option<Node<str>>,
457    pub value: Name,
458    pub directives: DirectiveList,
459}
460
461/// Executable AST for a [_Selection_](https://spec.graphql.org/September2025/#Selection)
462/// in a selection set.
463#[derive(Clone, Debug, Eq, PartialEq, Hash)]
464pub enum Selection {
465    Field(Node<Field>),
466    FragmentSpread(Node<FragmentSpread>),
467    InlineFragment(Node<InlineFragment>),
468}
469
470/// Executable AST for a [_Field_](https://spec.graphql.org/September2025/#Field) selection
471/// in a selection set.
472#[derive(Clone, Debug, Eq)]
473pub struct Field {
474    pub alias: Option<Name>,
475    pub name: Name,
476    pub arguments: Vec<Node<Argument>>,
477    pub directives: DirectiveList,
478    pub selection_set: Vec<Selection>,
479}
480
481impl PartialEq for Field {
482    fn eq(&self, other: &Self) -> bool {
483        self.alias == other.alias
484            && self.name == other.name
485            && eq_unique_by_name(&self.arguments, &other.arguments, |a| &a.name)
486            && self.directives == other.directives
487            && self.selection_set == other.selection_set
488    }
489}
490
491impl Hash for Field {
492    fn hash<H: Hasher>(&self, state: &mut H) {
493        self.alias.hash(state);
494        self.name.hash(state);
495        hash_unordered(self.arguments.iter(), state, self.arguments.len());
496        self.directives.hash(state);
497        self.selection_set.hash(state);
498    }
499}
500
501/// Executable AST for a
502/// [_FragmentSpread_](https://spec.graphql.org/September2025/#FragmentSpread) selection
503/// in a selection set.
504#[derive(Clone, Debug, Eq, PartialEq, Hash)]
505pub struct FragmentSpread {
506    pub fragment_name: Name,
507    pub directives: DirectiveList,
508}
509
510/// Executable AST for an
511/// [_InlineFragment_](https://spec.graphql.org/September2025/#InlineFragment) selection
512/// in a selection set.
513#[derive(Clone, Debug, Eq, PartialEq, Hash)]
514pub struct InlineFragment {
515    pub type_condition: Option<NamedType>,
516    pub directives: DirectiveList,
517    pub selection_set: Vec<Selection>,
518}
519
520/// Executable AST for a literal GraphQL [_Value_](https://spec.graphql.org/September2025/#Value).
521#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
522pub enum Value {
523    /// A [_NullValue_](https://spec.graphql.org/September2025/#NullValue)
524    Null,
525
526    /// An [_EnumValue_](https://spec.graphql.org/September2025/#EnumValue)
527    Enum(Name),
528
529    /// A [_Variable_](https://spec.graphql.org/September2025/#Variable)
530    Variable(Name),
531
532    /// A [_StringValue_](https://spec.graphql.org/September2025/#StringValue)
533    String(
534        /// The [semantic Unicode text](https://spec.graphql.org/September2025/#sec-String-Value.Static-Semantics)
535        /// that this value represents.
536        String,
537    ),
538
539    /// A [_FloatValue_](https://spec.graphql.org/September2025/#FloatValue)
540    Float(FloatValue),
541
542    /// An [_IntValue_](https://spec.graphql.org/September2025/#IntValue)
543    Int(IntValue),
544
545    /// A [_BooleanValue_](https://spec.graphql.org/September2025/#BooleanValue)
546    Boolean(bool),
547
548    /// A [_ListValue_](https://spec.graphql.org/September2025/#ListValue)
549    List(Vec<Node<Value>>),
550
551    /// An [_ObjectValue_](https://spec.graphql.org/September2025/#ObjectValue)
552    Object(Vec<(Name, Node<Value>)>),
553}
554
555/// An [_IntValue_](https://spec.graphql.org/September2025/#IntValue),
556/// represented as a string in order not to lose range or precision.
557#[derive(Clone, Eq, PartialEq, Hash)]
558pub struct IntValue(String);
559
560/// An [_FloatValue_](https://spec.graphql.org/September2025/#FloatValue),
561/// represented as a string in order not to lose range or precision.
562#[derive(Clone, Eq, PartialEq, Hash)]
563pub struct FloatValue(String);
564
565/// Error type of [`IntValue::try_to_f64`] an  [`FloatValue::try_to_f64`]
566/// for conversions that overflow `f64` and would be “rounded” to infinity.
567#[derive(Clone, Eq, PartialEq)]
568#[non_exhaustive]
569pub struct FloatOverflowError {}
570
571/// Error type of [`Directive::argument_by_name`] and
572/// [`Field::argument_by_name`][crate::executable::Field::argument_by_name]
573#[derive(Debug, Clone, PartialEq, Eq)]
574pub enum ArgumentByNameError {
575    /// The directive is not definied in the schema
576    UndefinedDirective,
577    /// The directive or field definition does not define an argument with the requested name
578    NoSuchArgument,
579    /// The argument is required (does not define a default value and has non-null type)
580    /// but not specified
581    RequiredArgumentNotSpecified,
582}