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}