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}