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/October2021/#Ignored
14//! [syntactic grammar]: https://spec.graphql.org/October2021/#sec-Language
15//! [valid]: https://spec.graphql.org/October2021/#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::parser::SourceMap;
55use crate::Name;
56use crate::Node;
57
58pub(crate) mod from_cst;
59pub(crate) mod impls;
60pub(crate) mod serialize;
61
62pub use self::serialize::Serialize;
63
64/// AST for a GraphQL [_Document_](https://spec.graphql.org/draft/#Document)
65/// that can contain executable definitions, type system (schema) definitions, or both.
66///
67/// It is typically parsed from one `&str` input “file” but can be also be synthesized
68/// programatically.
69#[derive(Clone)]
70pub struct Document {
71    /// If this document was originally parsed from a source file,
72    /// this map contains one entry for that file and its ID.
73    ///
74    /// The document is [mutable][crate::ast#structural-sharing-and-mutation]
75    /// so it may have been modified since.
76    pub sources: SourceMap,
77
78    pub definitions: Vec<Definition>,
79}
80
81const _: () = {
82    const fn assert_send<T: Send>() {}
83    const fn assert_sync<T: Sync>() {}
84    assert_send::<Document>();
85    assert_sync::<Document>();
86};
87
88/// A [_NamedType_](https://spec.graphql.org/draft/#NamedType)
89/// references by name a GraphQL type defined elsewhere.
90pub type NamedType = Name;
91
92/// AST for a top-level [_Definition_](https://spec.graphql.org/draft/#Definition) of any kind:
93/// executable, type system, or type system extension.
94#[derive(Clone, Eq, PartialEq, Hash)]
95pub enum Definition {
96    OperationDefinition(Node<OperationDefinition>),
97    FragmentDefinition(Node<FragmentDefinition>),
98    DirectiveDefinition(Node<DirectiveDefinition>),
99    SchemaDefinition(Node<SchemaDefinition>),
100    ScalarTypeDefinition(Node<ScalarTypeDefinition>),
101    ObjectTypeDefinition(Node<ObjectTypeDefinition>),
102    InterfaceTypeDefinition(Node<InterfaceTypeDefinition>),
103    UnionTypeDefinition(Node<UnionTypeDefinition>),
104    EnumTypeDefinition(Node<EnumTypeDefinition>),
105    InputObjectTypeDefinition(Node<InputObjectTypeDefinition>),
106    SchemaExtension(Node<SchemaExtension>),
107    ScalarTypeExtension(Node<ScalarTypeExtension>),
108    ObjectTypeExtension(Node<ObjectTypeExtension>),
109    InterfaceTypeExtension(Node<InterfaceTypeExtension>),
110    UnionTypeExtension(Node<UnionTypeExtension>),
111    EnumTypeExtension(Node<EnumTypeExtension>),
112    InputObjectTypeExtension(Node<InputObjectTypeExtension>),
113}
114
115/// Executable AST for an
116/// [_OperationDefinition_](https://spec.graphql.org/draft/#OperationDefinition).
117#[derive(Clone, Debug, Eq, PartialEq, Hash)]
118pub struct OperationDefinition {
119    pub operation_type: OperationType,
120    pub name: Option<Name>,
121    pub variables: Vec<Node<VariableDefinition>>,
122    pub directives: DirectiveList,
123    pub selection_set: Vec<Selection>,
124}
125
126/// Executable AST for a
127/// [_FragmentDefinition_](https://spec.graphql.org/draft/#FragmentDefinition).
128#[derive(Clone, Debug, Eq, PartialEq, Hash)]
129pub struct FragmentDefinition {
130    pub name: Name,
131    pub type_condition: NamedType,
132    pub directives: DirectiveList,
133    pub selection_set: Vec<Selection>,
134}
135
136/// Type system AST for a `directive @foo`
137/// [_DirectiveDefinition_](https://spec.graphql.org/draft/#DirectiveDefinition).
138#[derive(Clone, Debug, Eq, PartialEq, Hash)]
139pub struct DirectiveDefinition {
140    pub description: Option<Node<str>>,
141    pub name: Name,
142    pub arguments: Vec<Node<InputValueDefinition>>,
143    pub repeatable: bool,
144    pub locations: Vec<DirectiveLocation>,
145}
146
147/// Type system AST for a `schema`
148/// [_SchemaDefinition_](https://spec.graphql.org/draft/#SchemaDefinition).
149#[derive(Clone, Debug, Eq, PartialEq, Hash)]
150pub struct SchemaDefinition {
151    pub description: Option<Node<str>>,
152    pub directives: DirectiveList,
153    pub root_operations: Vec<Node<(OperationType, NamedType)>>,
154}
155
156/// Type system AST for a `scalar FooS`
157/// [_ScalarTypeDefinition_](https://spec.graphql.org/draft/#ScalarTypeDefinition).
158#[derive(Clone, Debug, Eq, PartialEq, Hash)]
159pub struct ScalarTypeDefinition {
160    pub description: Option<Node<str>>,
161    pub name: Name,
162    pub directives: DirectiveList,
163}
164
165/// Type system AST for a `type FooO`
166/// [_ObjectTypeDefinition_](https://spec.graphql.org/draft/#ObjectTypeDefinition).
167#[derive(Clone, Debug, Eq, PartialEq, Hash)]
168pub struct ObjectTypeDefinition {
169    pub description: Option<Node<str>>,
170    pub name: Name,
171    pub implements_interfaces: Vec<Name>,
172    pub directives: DirectiveList,
173    pub fields: Vec<Node<FieldDefinition>>,
174}
175
176/// Type system AST for an `interface FooI`
177/// [_InterfaceTypeDefinition_](https://spec.graphql.org/draft/#InterfaceTypeDefinition).
178#[derive(Clone, Debug, Eq, PartialEq, Hash)]
179pub struct InterfaceTypeDefinition {
180    pub description: Option<Node<str>>,
181    pub name: Name,
182    pub implements_interfaces: Vec<Name>,
183    pub directives: DirectiveList,
184    pub fields: Vec<Node<FieldDefinition>>,
185}
186
187/// Type system AST for a `union FooU`
188/// [_UnionTypeDefinition_](https://spec.graphql.org/draft/#UnionTypeDefinition).
189#[derive(Clone, Debug, Eq, PartialEq, Hash)]
190pub struct UnionTypeDefinition {
191    pub description: Option<Node<str>>,
192    pub name: Name,
193    pub directives: DirectiveList,
194    pub members: Vec<NamedType>,
195}
196
197/// Type system AST for an `enum FooE`
198/// [_EnumTypeDefinition_](https://spec.graphql.org/draft/#EnumTypeDefinition).
199#[derive(Clone, Debug, Eq, PartialEq, Hash)]
200pub struct EnumTypeDefinition {
201    pub description: Option<Node<str>>,
202    pub name: Name,
203    pub directives: DirectiveList,
204    pub values: Vec<Node<EnumValueDefinition>>,
205}
206
207/// Type system AST for an `input FooIn`
208/// [_InputObjectTypeDefinition_](https://spec.graphql.org/draft/#InputObjectTypeDefinition).
209#[derive(Clone, Debug, Eq, PartialEq, Hash)]
210pub struct InputObjectTypeDefinition {
211    pub description: Option<Node<str>>,
212    pub name: Name,
213    pub directives: DirectiveList,
214    pub fields: Vec<Node<InputValueDefinition>>,
215}
216
217/// Type system AST for an `extend schema`
218/// [_SchemaExtension_](https://spec.graphql.org/draft/#SchemaExtension).
219#[derive(Clone, Debug, Eq, PartialEq, Hash)]
220pub struct SchemaExtension {
221    pub directives: DirectiveList,
222    pub root_operations: Vec<Node<(OperationType, NamedType)>>,
223}
224
225/// Type system AST for an `extend scalar FooS`
226/// [_ScalarTypeExtension_](https://spec.graphql.org/draft/#ScalarTypeExtension).
227#[derive(Clone, Debug, Eq, PartialEq, Hash)]
228pub struct ScalarTypeExtension {
229    pub name: Name,
230    pub directives: DirectiveList,
231}
232
233/// Type system AST for an `extend type FooO`
234/// [_ObjectTypeExtension_](https://spec.graphql.org/draft/#ObjectTypeExtension).
235#[derive(Clone, Debug, Eq, PartialEq, Hash)]
236pub struct ObjectTypeExtension {
237    pub name: Name,
238    pub implements_interfaces: Vec<Name>,
239    pub directives: DirectiveList,
240    pub fields: Vec<Node<FieldDefinition>>,
241}
242
243/// Type system AST for an `extend interface FooI`
244/// [_InterfaceTypeExtension_](https://spec.graphql.org/draft/#InterfaceTypeExtension).
245#[derive(Clone, Debug, Eq, PartialEq, Hash)]
246pub struct InterfaceTypeExtension {
247    pub name: Name,
248    pub implements_interfaces: Vec<Name>,
249    pub directives: DirectiveList,
250    pub fields: Vec<Node<FieldDefinition>>,
251}
252
253/// Type system AST for an `extend union FooU`
254/// [_UnionTypeExtension_](https://spec.graphql.org/draft/#UnionTypeExtension).
255#[derive(Clone, Debug, Eq, PartialEq, Hash)]
256pub struct UnionTypeExtension {
257    pub name: Name,
258    pub directives: DirectiveList,
259    pub members: Vec<NamedType>,
260}
261
262/// Type system AST for an `extend enum FooE`
263/// [_EnumTypeExtension_](https://spec.graphql.org/draft/#EnumTypeExtension).
264#[derive(Clone, Debug, Eq, PartialEq, Hash)]
265pub struct EnumTypeExtension {
266    pub name: Name,
267    pub directives: DirectiveList,
268    pub values: Vec<Node<EnumValueDefinition>>,
269}
270
271/// Type system AST for an `extend input FooIn`
272/// [_InputObjectTypeExtension_](https://spec.graphql.org/draft/#InputObjectTypeExtension).
273#[derive(Clone, Debug, Eq, PartialEq, Hash)]
274pub struct InputObjectTypeExtension {
275    pub name: Name,
276    pub directives: DirectiveList,
277    pub fields: Vec<Node<InputValueDefinition>>,
278}
279
280/// AST for an [_Argument_](https://spec.graphql.org/draft/#Argument)
281/// of a [`Field`] selection or [`Directive`] application.
282#[derive(Clone, Debug, Eq, PartialEq, Hash)]
283pub struct Argument {
284    pub name: Name,
285    pub value: Node<Value>,
286}
287
288/// AST for the list of [_Directives_](https://spec.graphql.org/draft/#Directives)
289/// applied to some context.
290#[derive(Clone, Eq, PartialEq, Hash, Default)]
291pub struct DirectiveList(pub Vec<Node<Directive>>);
292
293/// AST for a [_Directive_](https://spec.graphql.org/draft/#Directive) application.
294#[derive(Clone, Debug, Eq, PartialEq, Hash)]
295pub struct Directive {
296    pub name: Name,
297    pub arguments: Vec<Node<Argument>>,
298}
299
300/// AST for the [_OperationType_](https://spec.graphql.org/draft/#OperationType)
301/// of an [`OperationDefinition`] or [`RootOperationDefinition`][SchemaDefinition::root_operations].
302#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
303pub enum OperationType {
304    Query,
305    Mutation,
306    Subscription,
307}
308
309/// AST for a [_DirectiveLocation_](https://spec.graphql.org/draft/#DirectiveLocation)
310/// of a [`DirectiveDefinition`].
311#[derive(Copy, Clone, Hash, PartialEq, Eq)]
312pub enum DirectiveLocation {
313    Query,
314    Mutation,
315    Subscription,
316    Field,
317    FragmentDefinition,
318    FragmentSpread,
319    InlineFragment,
320    VariableDefinition,
321    Schema,
322    Scalar,
323    Object,
324    FieldDefinition,
325    ArgumentDefinition,
326    Interface,
327    Union,
328    Enum,
329    EnumValue,
330    InputObject,
331    InputFieldDefinition,
332}
333
334/// Executable AST for a [_VariableDefinition_](https://spec.graphql.org/draft/#VariableDefinition)
335/// in an [`OperationDefinition`].
336#[derive(Clone, Debug, Eq, PartialEq, Hash)]
337pub struct VariableDefinition {
338    pub name: Name,
339    pub ty: Node<Type>,
340    pub default_value: Option<Node<Value>>,
341    pub directives: DirectiveList,
342}
343
344/// Type system AST for a reference to a GraphQL [_Type_](https://spec.graphql.org/draft/#Type)
345#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
346pub enum Type {
347    /// A `Foo` reference to nullable named type
348    Named(NamedType),
349
350    /// A `Foo!` reference to non-null named type
351    NonNullNamed(NamedType),
352
353    /// A `[…]` reference to nullable list type.
354    /// (The inner item type may or may not be nullable, or a nested list.)
355    List(Box<Type>),
356
357    /// A `[…]!` reference to non-null list type.
358    /// (The inner item type may or may not be nullable, or a nested list.)
359    NonNullList(Box<Type>),
360}
361
362/// Type system AST for a [_FieldDefinition_](https://spec.graphql.org/draft/#FieldDefinition)
363/// in an object type or interface type defintion or extension.
364#[derive(Clone, Debug, Eq, PartialEq, Hash)]
365pub struct FieldDefinition {
366    pub description: Option<Node<str>>,
367    pub name: Name,
368    pub arguments: Vec<Node<InputValueDefinition>>,
369    pub ty: Type,
370    pub directives: DirectiveList,
371}
372
373/// Type system AST for an
374/// [_InputValueDefinition_](https://spec.graphql.org/draft/#InputValueDefinition),
375/// a input type field definition or an argument definition.
376#[derive(Clone, Debug, Eq, PartialEq, Hash)]
377pub struct InputValueDefinition {
378    pub description: Option<Node<str>>,
379    pub name: Name,
380    pub ty: Node<Type>,
381    pub default_value: Option<Node<Value>>,
382    pub directives: DirectiveList,
383}
384
385/// Type system AST for an
386/// [_EnumValueDefinition_](https://spec.graphql.org/draft/#EnumValueDefinition)
387/// in an enum type definition or extension.
388#[derive(Clone, Debug, Eq, PartialEq, Hash)]
389pub struct EnumValueDefinition {
390    pub description: Option<Node<str>>,
391    pub value: Name,
392    pub directives: DirectiveList,
393}
394
395/// Executable AST for a [_Selection_](https://spec.graphql.org/draft/#Selection)
396/// in a selection set.
397#[derive(Clone, Debug, Eq, PartialEq, Hash)]
398pub enum Selection {
399    Field(Node<Field>),
400    FragmentSpread(Node<FragmentSpread>),
401    InlineFragment(Node<InlineFragment>),
402}
403
404/// Executable AST for a [_Field_](https://spec.graphql.org/draft/#Field) selection
405/// in a selection set.
406#[derive(Clone, Debug, Eq, PartialEq, Hash)]
407pub struct Field {
408    pub alias: Option<Name>,
409    pub name: Name,
410    pub arguments: Vec<Node<Argument>>,
411    pub directives: DirectiveList,
412    pub selection_set: Vec<Selection>,
413}
414
415/// Executable AST for a
416/// [_FragmentSpread_](https://spec.graphql.org/draft/#FragmentSpread) selection
417/// in a selection set.
418#[derive(Clone, Debug, Eq, PartialEq, Hash)]
419pub struct FragmentSpread {
420    pub fragment_name: Name,
421    pub directives: DirectiveList,
422}
423
424/// Executable AST for an
425/// [_InlineFragment_](https://spec.graphql.org/draft/#InlineFragment) selection
426/// in a selection set.
427#[derive(Clone, Debug, Eq, PartialEq, Hash)]
428pub struct InlineFragment {
429    pub type_condition: Option<NamedType>,
430    pub directives: DirectiveList,
431    pub selection_set: Vec<Selection>,
432}
433
434/// Executable AST for a literal GraphQL [_Value_](https://spec.graphql.org/draft/#Value).
435#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
436pub enum Value {
437    /// A [_NullValue_](https://spec.graphql.org/draft/#NullValue)
438    Null,
439
440    /// An [_EnumValue_](https://spec.graphql.org/draft/#EnumValue)
441    Enum(Name),
442
443    /// A [_Variable_](https://spec.graphql.org/draft/#Variable)
444    Variable(Name),
445
446    /// A [_StringValue_](https://spec.graphql.org/draft/#StringValue)
447    String(
448        /// The [semantic Unicode text](https://spec.graphql.org/draft/#sec-String-Value.Static-Semantics)
449        /// that this value represents.
450        String,
451    ),
452
453    /// A [_FloatValue_](https://spec.graphql.org/draft/#FloatValue)
454    Float(FloatValue),
455
456    /// An [_IntValue_](https://spec.graphql.org/draft/#IntValue)
457    Int(IntValue),
458
459    /// A [_BooleanValue_](https://spec.graphql.org/draft/#BooleanValue)
460    Boolean(bool),
461
462    /// A [_ListValue_](https://spec.graphql.org/draft/#ListValue)
463    List(Vec<Node<Value>>),
464
465    /// An [_ObjectValue_](https://spec.graphql.org/draft/#ObjectValue)
466    Object(Vec<(Name, Node<Value>)>),
467}
468
469/// An [_IntValue_](https://spec.graphql.org/draft/#IntValue),
470/// represented as a string in order not to lose range or precision.
471#[derive(Clone, Eq, PartialEq, Hash)]
472pub struct IntValue(String);
473
474/// An [_FloatValue_](https://spec.graphql.org/draft/#FloatValue),
475/// represented as a string in order not to lose range or precision.
476#[derive(Clone, Eq, PartialEq, Hash)]
477pub struct FloatValue(String);
478
479/// Error type of [`IntValue::try_to_f64`] an  [`FloatValue::try_to_f64`]
480/// for conversions that overflow `f64` and would be “rounded” to infinity.
481#[derive(Clone, Eq, PartialEq)]
482#[non_exhaustive]
483pub struct FloatOverflowError {}
484
485/// Error type of [`Directive::argument_by_name`] and
486/// [`Field::argument_by_name`][crate::executable::Field::argument_by_name]
487#[derive(Debug, Clone, PartialEq, Eq)]
488pub enum ArgumentByNameError {
489    /// The directive is not definied in the schema
490    UndefinedDirective,
491    /// The directive or field definition does not define an argument with the requested name
492    NoSuchArgument,
493    /// The argument is required (does not define a default value and has non-null type)
494    /// but not specified
495    RequiredArgumentNotSpecified,
496}