Skip to main content

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