Skip to main content

reed_compiler/executable/
mod.rs

1//! High-level representation of an executable document,
2//! which can contain operations and fragments.
3//!
4//! Compared to an [`ast::Document`] which follows closely the structure of GraphQL syntax,
5//! an [`ExecutableDocument`] interpreted in the context of a valid [`Schema`]
6//! and contains typing information.
7//!
8//! In some cases like [`SelectionSet`], this module and the [`ast`] module
9//! define different Rust types with the same names.
10//! In other cases like [`Directive`] there is no data structure difference needed,
11//! so this module reuses and publicly re-exports some Rust types from the [`ast`] module.
12//!
13//! ## “Build” errors
14//!
15//! As a result of how `ExecutableDocument` containing typing information,
16//! not all AST documents (even if filtering out type system definitions) can be fully represented:
17//! creating a `ExecutableDocument` can cause errors (on top of any potential syntax error)
18//! for cases like selecting a field not defined in the schema.
19//!
20//! When such errors (or in [`ExecutableDocument::parse`], syntax errors) happen,
21//! a partial document is returned together with a list of diagnostics.
22//!
23//! ## Structural sharing and mutation
24//!
25//! Like in AST, many parts of a `ExecutableDocument` are reference-counted with [`Node`].
26//! This allows sharing nodes between documents without cloning entire subtrees.
27//! To modify a node, the [`make_mut`][Node::make_mut] method provides copy-on-write semantics.
28//!
29//! ## Validation
30//!
31//! The [Validation] section of the GraphQL specification defines validation rules
32//! beyond syntax errors and errors detected while constructing a `ExecutableDocument`.
33//! The [`validate`][ExecutableDocument::validate] method returns either:
34//!
35//! * An immutable [`Valid<ExecutableDocument>`] type wrapper, or
36//! * The document together with a list of diagnostics
37//!
38//! If there is no mutation needed between parsing and validation,
39//! [`ExecutableDocument::parse_and_validate`] does both in one step.
40//!
41//! [Validation]: https://spec.graphql.org/draft/#sec-Validation
42//!
43//! ## Serialization
44//!
45//! `ExecutableDocument` and other types types implement [`Display`][std::fmt::Display]
46//! and [`ToString`] by serializing to GraphQL syntax with a default configuration.
47//! [`serialize`][ExecutableDocument::serialize] methods return a builder
48//! that has chaining methods for setting serialization configuration,
49//! and also implements `Display` and `ToString`.
50
51use crate::ast;
52use crate::collections::IndexMap;
53use crate::coordinate::FieldArgumentCoordinate;
54use crate::coordinate::TypeAttributeCoordinate;
55use crate::parser::Parser;
56use crate::parser::SourceMap;
57use crate::parser::SourceSpan;
58use crate::schema;
59use crate::validation::DiagnosticList;
60use crate::validation::Valid;
61use crate::validation::WithErrors;
62use crate::Node;
63use crate::Schema;
64use indexmap::map::Entry;
65use std::fmt;
66use std::path::Path;
67use std::sync::Arc;
68
69pub(crate) mod from_ast;
70mod serialize;
71pub(crate) mod validation;
72
73pub use self::from_ast::ExecutableDocumentBuilder;
74pub use crate::ast::Argument;
75use crate::ast::ArgumentByNameError;
76pub use crate::ast::Directive;
77pub use crate::ast::DirectiveList;
78pub use crate::ast::NamedType;
79pub use crate::ast::OperationType;
80pub use crate::ast::Type;
81pub use crate::ast::Value;
82pub use crate::ast::VariableDefinition;
83use crate::collections::HashSet;
84use crate::request::RequestError;
85pub use crate::Name;
86
87/// Executable definitions, annotated with type information
88#[derive(Debug, Clone, Default)]
89pub struct ExecutableDocument {
90    /// If this document was originally parsed from a source file,
91    /// this map contains one entry for that file and its ID.
92    ///
93    /// The document may have been modified since.
94    pub sources: SourceMap,
95
96    pub operations: OperationMap,
97    pub fragments: FragmentMap,
98}
99
100/// Operations definitions for a given executable document
101#[derive(Debug, Clone, Default, PartialEq)]
102pub struct OperationMap {
103    pub anonymous: Option<Node<Operation>>,
104    pub named: IndexMap<Name, Node<Operation>>,
105}
106
107/// Definitions of named fragments for a given executable document
108pub type FragmentMap = IndexMap<Name, Node<Fragment>>;
109
110/// FieldSet information created for FieldSet parsing in `@requires` directive.
111/// Annotated with type information.
112#[derive(Debug, Clone)]
113pub struct FieldSet {
114    /// If this document was originally parsed from a source file,
115    /// this map contains one entry for that file and its ID.
116    ///
117    /// The document may have been modified since.
118    pub sources: SourceMap,
119
120    pub selection_set: SelectionSet,
121}
122
123/// An [_OperationDefinition_](https://spec.graphql.org/draft/#OperationDefinition)
124/// annotated with type information.
125#[derive(Debug, Clone, PartialEq, Eq)]
126pub struct Operation {
127    pub operation_type: OperationType,
128    pub name: Option<Name>,
129    pub variables: Vec<Node<VariableDefinition>>,
130    pub directives: DirectiveList,
131    pub selection_set: SelectionSet,
132}
133
134/// A [_FragmentDefinition_](https://spec.graphql.org/draft/#FragmentDefinition)
135/// annotated with type information.
136#[derive(Debug, Clone, PartialEq, Eq)]
137pub struct Fragment {
138    pub name: Name,
139    pub directives: DirectiveList,
140    pub selection_set: SelectionSet,
141}
142
143/// A [_SelectionSet_](https://spec.graphql.org/draft/#SelectionSet)
144/// annotated with type information.
145#[derive(Debug, Clone, PartialEq, Eq, Hash)]
146pub struct SelectionSet {
147    pub ty: NamedType,
148    pub selections: Vec<Selection>,
149}
150
151/// A [_Selection_](https://spec.graphql.org/draft/#Selection)
152/// annotated with type information.
153#[derive(Debug, Clone, PartialEq, Eq, Hash)]
154pub enum Selection {
155    Field(Node<Field>),
156    FragmentSpread(Node<FragmentSpread>),
157    InlineFragment(Node<InlineFragment>),
158}
159
160/// A [_Field_](https://spec.graphql.org/draft/#Field) selection,
161/// linked to the corresponding field definition in the schema.
162#[derive(Debug, Clone, PartialEq, Eq, Hash)]
163pub struct Field {
164    /// The definition of this field in an object type or interface type definition in the schema
165    pub definition: Node<schema::FieldDefinition>,
166    pub alias: Option<Name>,
167    pub name: Name,
168    pub arguments: Vec<Node<Argument>>,
169    pub directives: DirectiveList,
170    pub selection_set_is_explicit: bool,
171    pub selection_set: SelectionSet,
172}
173
174/// A [_FragmentSpread_](https://spec.graphql.org/draft/#FragmentSpread)
175/// annotated with type information.
176#[derive(Debug, Clone, PartialEq, Eq, Hash)]
177pub struct FragmentSpread {
178    pub fragment_name: Name,
179    pub directives: DirectiveList,
180}
181
182/// A [_InlineFragment_](https://spec.graphql.org/draft/#InlineFragment)
183/// annotated with type information.
184#[derive(Debug, Clone, PartialEq, Eq, Hash)]
185pub struct InlineFragment {
186    pub type_condition: Option<NamedType>,
187    pub directives: DirectiveList,
188    pub selection_set: SelectionSet,
189}
190
191/// Errors that can occur during conversion from AST to executable document or
192/// validation of an executable document.
193#[derive(thiserror::Error, Debug, Clone)]
194pub(crate) enum BuildError {
195    #[error("an executable document must not contain {describe}")]
196    TypeSystemDefinition {
197        name: Option<Name>,
198        describe: &'static str,
199    },
200
201    #[error("anonymous operation cannot be selected when the document contains other operations")]
202    AmbiguousAnonymousOperation,
203
204    #[error(
205        "the operation `{name_at_previous_location}` is defined multiple times in the document"
206    )]
207    OperationNameCollision { name_at_previous_location: Name },
208
209    #[error(
210        "the fragment `{name_at_previous_location}` is defined multiple times in the document"
211    )]
212    FragmentNameCollision { name_at_previous_location: Name },
213
214    #[error("`{operation_type}` root operation type is not defined")]
215    UndefinedRootOperation { operation_type: &'static str },
216
217    #[error(
218        "type condition `{type_name}` of fragment `{fragment_name}` \
219         is not a type defined in the schema"
220    )]
221    UndefinedTypeInNamedFragmentTypeCondition {
222        type_name: NamedType,
223        fragment_name: Name,
224    },
225
226    #[error("type condition `{type_name}` of inline fragment is not a type defined in the schema")]
227    UndefinedTypeInInlineFragmentTypeCondition {
228        type_name: NamedType,
229        path: SelectionPath,
230    },
231
232    #[error("field selection of scalar type `{type_name}` must not have subselections")]
233    SubselectionOnScalarType {
234        type_name: NamedType,
235        path: SelectionPath,
236    },
237
238    #[error("field selection of enum type `{type_name}` must not have subselections")]
239    SubselectionOnEnumType {
240        type_name: NamedType,
241        path: SelectionPath,
242    },
243
244    #[error("type `{type_name}` does not have a field `{field_name}`")]
245    UndefinedField {
246        type_name: NamedType,
247        field_name: Name,
248        path: SelectionPath,
249    },
250
251    // Validation errors
252    #[error(
253        "{} can only have one root field",
254        subscription_name_or_anonymous(name)
255    )]
256    SubscriptionUsesMultipleFields {
257        name: Option<Name>,
258        fields: Vec<Name>,
259    },
260
261    #[error(
262        "{} can not have an introspection field as a root field",
263        subscription_name_or_anonymous(name)
264    )]
265    SubscriptionUsesIntrospection {
266        /// Name of the operation
267        name: Option<Name>,
268        /// Name of the introspection field
269        field: Name,
270    },
271    #[error(
272        "{} can not specify @skip or @include on root fields",
273        subscription_name_or_anonymous(name)
274    )]
275    SubscriptionUsesConditionalSelection {
276        /// Name of the operation
277        name: Option<Name>,
278    },
279
280    #[error("`@defer` label `{label}` is not unique within the document")]
281    DuplicateDeferLabel {
282        label: String,
283        original_location: Option<SourceSpan>,
284    },
285
286    #[error("`@defer` label argument must be a static String, not a variable")]
287    DeferLabelMustNotBeVariable,
288
289    #[error(
290        "`@defer` is not allowed on root selections of {} operations",
291        operation_type.name()
292    )]
293    DeferOnRootMutationOrSubscriptionField { operation_type: OperationType },
294
295    #[error("`@defer` in a subscription operation must be disabled with an `if` argument")]
296    DeferInSubscriptionMustBeConditional,
297
298    #[error("{0}")]
299    ConflictingFieldType(Box<ConflictingFieldType>),
300    #[error("{0}")]
301    ConflictingFieldArgument(Box<ConflictingFieldArgument>),
302    #[error("{0}")]
303    ConflictingFieldName(Box<ConflictingFieldName>),
304}
305
306#[derive(thiserror::Error, Debug, Clone)]
307#[error("operation must not select different types using the same name `{alias}`")]
308pub(crate) struct ConflictingFieldType {
309    /// Name or alias of the non-unique field.
310    pub(crate) alias: Name,
311    pub(crate) original_location: Option<SourceSpan>,
312    pub(crate) original_coordinate: TypeAttributeCoordinate,
313    pub(crate) original_type: Type,
314    pub(crate) conflicting_location: Option<SourceSpan>,
315    pub(crate) conflicting_coordinate: TypeAttributeCoordinate,
316    pub(crate) conflicting_type: Type,
317}
318
319#[derive(thiserror::Error, Debug, Clone)]
320#[error("operation must not provide conflicting field arguments for the same name `{alias}`")]
321pub(crate) struct ConflictingFieldArgument {
322    /// Name or alias of the non-unique field.
323    pub(crate) alias: Name,
324    pub(crate) original_location: Option<SourceSpan>,
325    pub(crate) original_coordinate: FieldArgumentCoordinate,
326    pub(crate) original_value: Option<Value>,
327    pub(crate) conflicting_location: Option<SourceSpan>,
328    pub(crate) conflicting_coordinate: FieldArgumentCoordinate,
329    pub(crate) conflicting_value: Option<Value>,
330}
331
332#[derive(thiserror::Error, Debug, Clone)]
333#[error("cannot select different fields into the same alias `{alias}`")]
334pub(crate) struct ConflictingFieldName {
335    /// Name of the non-unique field.
336    pub(crate) alias: Name,
337    pub(crate) original_location: Option<SourceSpan>,
338    pub(crate) original_selection: TypeAttributeCoordinate,
339    pub(crate) conflicting_location: Option<SourceSpan>,
340    pub(crate) conflicting_selection: TypeAttributeCoordinate,
341}
342
343fn subscription_name_or_anonymous(name: &Option<Name>) -> impl std::fmt::Display + '_ {
344    crate::validation::diagnostics::NameOrAnon {
345        name: name.as_ref(),
346        if_some_prefix: "subscription",
347        if_none: "anonymous subscription",
348    }
349}
350
351#[derive(Debug, Clone, PartialEq, Eq)]
352pub(crate) struct SelectionPath {
353    pub(crate) root: ExecutableDefinitionName,
354    pub(crate) nested_fields: Vec<Name>,
355}
356
357/// Designates by name a top-level definition in an executable document
358#[derive(Debug, Clone, PartialEq, Eq)]
359pub(crate) enum ExecutableDefinitionName {
360    AnonymousOperation(ast::OperationType),
361    NamedOperation(ast::OperationType, Name),
362    Fragment(Name),
363}
364
365impl ExecutableDocument {
366    /// Create an empty document, to be filled programatically
367    pub fn new() -> Self {
368        Self::default()
369    }
370
371    /// Returns a new builder for creating an ExecutableDocument from multiple AST documents.
372    ///
373    /// The builder allows you to parse and combine executable definitions (operations and fragments)
374    /// from multiple source files into a single [`ExecutableDocument`].
375    ///
376    /// # Example
377    ///
378    /// ```rust
379    /// use reed_compiler::{Schema, ExecutableDocument};
380    /// use reed_compiler::parser::Parser;
381    /// use reed_compiler::validation::DiagnosticList;
382    /// # let schema_src = "type Query { user: User, post: Post } type User { id: ID } type Post { title: String }";
383    /// # let schema = Schema::parse_and_validate(schema_src, "schema.graphql").unwrap();
384    ///
385    /// let mut errors = DiagnosticList::new(Default::default());
386    /// let doc = ExecutableDocument::builder(Some(&schema), &mut errors)
387    ///     .parse("query GetUser { user { id } }", "query1.graphql")
388    ///     .parse("query GetMore { user { id } }", "query2.graphql")
389    ///     .build();
390    ///
391    /// assert!(errors.is_empty());
392    /// assert_eq!(doc.operations.named.len(), 2);
393    pub fn builder<'schema, 'errors>(
394        schema: Option<&'schema Valid<Schema>>,
395        errors: &'errors mut DiagnosticList,
396    ) -> from_ast::ExecutableDocumentBuilder<'schema, 'errors> {
397        from_ast::ExecutableDocumentBuilder::new(schema.map(|s| s.as_ref()), errors)
398    }
399
400    /// Parse an executable document with the default configuration.
401    ///
402    /// `path` is the filesystem path (or arbitrary string) used in diagnostics
403    /// to identify this source file to users.
404    ///
405    /// Create a [`Parser`] to use different parser configuration.
406    #[allow(clippy::result_large_err)] // Typically not called very often
407    pub fn parse(
408        schema: &Valid<Schema>,
409        source_text: impl Into<String>,
410        path: impl AsRef<Path>,
411    ) -> Result<Self, WithErrors<Self>> {
412        Parser::new().parse_executable(schema, source_text, path)
413    }
414
415    /// [`parse`][Self::parse] then [`validate`][Self::validate],
416    /// to get a `Valid<ExecutableDocument>` when mutating it isn’t needed.
417    #[allow(clippy::result_large_err)] // Typically not called very often
418    pub fn parse_and_validate(
419        schema: &Valid<Schema>,
420        source_text: impl Into<String>,
421        path: impl AsRef<Path>,
422    ) -> Result<Valid<Self>, WithErrors<Self>> {
423        let (doc, mut errors) = Parser::new().parse_executable_inner(schema, source_text, path);
424        Arc::make_mut(&mut errors.sources)
425            .extend(schema.sources.iter().map(|(k, v)| (*k, v.clone())));
426        validation::validate_executable_document(&mut errors, schema, &doc);
427        errors.into_valid_result(doc)
428    }
429
430    #[allow(clippy::result_large_err)] // Typically not called very often
431    pub fn validate(self, schema: &Valid<Schema>) -> Result<Valid<Self>, WithErrors<Self>> {
432        let mut sources = IndexMap::clone(&schema.sources);
433        sources.extend(self.sources.iter().map(|(k, v)| (*k, v.clone())));
434        let mut errors = DiagnosticList::new(Arc::new(sources));
435        validation::validate_executable_document(&mut errors, schema, &self);
436        errors.into_valid_result(self)
437    }
438
439    serialize_method!();
440}
441
442impl Eq for ExecutableDocument {}
443
444/// `sources` and `build_errors` are ignored for comparison
445impl PartialEq for ExecutableDocument {
446    fn eq(&self, other: &Self) -> bool {
447        let Self {
448            sources: _,
449            operations,
450            fragments,
451        } = self;
452        *operations == other.operations && *fragments == other.fragments
453    }
454}
455
456impl OperationMap {
457    /// Creates a new `OperationMap` containing one operation
458    pub fn from_one(operation: impl Into<Node<Operation>>) -> Self {
459        let mut map = Self::default();
460        map.insert(operation);
461        map
462    }
463
464    pub fn is_empty(&self) -> bool {
465        self.anonymous.is_none() && self.named.is_empty()
466    }
467
468    pub fn len(&self) -> usize {
469        self.anonymous.is_some() as usize + self.named.len()
470    }
471
472    /// Returns an iterator of operations, both anonymous and named
473    pub fn iter(&self) -> impl Iterator<Item = &'_ Node<Operation>> {
474        self.anonymous
475            .as_ref()
476            .into_iter()
477            .chain(self.named.values())
478    }
479
480    /// Return the relevant operation for a request, or a request error
481    ///
482    /// This is the [_GetOperation()_](https://spec.graphql.org/October2021/#GetOperation())
483    /// algorithm in the _Executing Requests_ section of the specification.
484    ///
485    /// A GraphQL request comes with a document (which may contain multiple operations)
486    /// an an optional operation name. When a name is given the request executes the operation
487    /// with that name, which is expected to exist. When it is not given / null / `None`,
488    /// the document is expected to contain a single operation (which may or may not be named)
489    /// to avoid ambiguity.
490    pub fn get(&self, name_request: Option<&str>) -> Result<&Node<Operation>, RequestError> {
491        if let Some(name) = name_request {
492            // Honor the request
493            self.named
494                .get(name)
495                .ok_or_else(|| format!("No operation named '{name}'"))
496        } else {
497            // No name request (`operationName` unspecified or null)
498            if let Some(op) = &self.anonymous {
499                // Return the anonymous operation if it’s the only operation
500                self.named.is_empty().then_some(op)
501            } else {
502                // No anonymous operation, return a named operation if it’s the only one
503                self.named
504                    .values()
505                    .next()
506                    .and_then(|op| (self.named.len() == 1).then_some(op))
507            }
508            .ok_or_else(|| {
509                "Ambiguous request: multiple operations but no specified `operationName`".to_owned()
510            })
511        }
512        .map_err(|message| RequestError {
513            message,
514            location: None,
515            is_suspected_validation_bug: false,
516        })
517    }
518
519    /// Similar to [`get`][Self::get] but returns a mutable reference.
520    pub fn get_mut(&mut self, name_request: Option<&str>) -> Result<&mut Operation, RequestError> {
521        if let Some(name) = name_request {
522            // Honor the request
523            self.named
524                .get_mut(name)
525                .ok_or_else(|| format!("No operation named '{name}'"))
526        } else {
527            // No name request (`operationName` unspecified or null)
528            if let Some(op) = &mut self.anonymous {
529                // Return the anonymous operation if it’s the only operation
530                self.named.is_empty().then_some(op)
531            } else {
532                // No anonymous operation, return a named operation if it’s the only one
533                let len = self.named.len();
534                self.named
535                    .values_mut()
536                    .next()
537                    .and_then(|op| (len == 1).then_some(op))
538            }
539            .ok_or_else(|| {
540                "Ambiguous request: multiple operations but no specified `operationName`".to_owned()
541            })
542        }
543        .map(Node::make_mut)
544        .map_err(|message| RequestError {
545            message,
546            location: None,
547            is_suspected_validation_bug: false,
548        })
549    }
550
551    /// Insert the given operation in either `named_operations` or `anonymous_operation`
552    /// as appropriate, and return the old operation (if any) with that name (or lack thereof).
553    pub fn insert(&mut self, operation: impl Into<Node<Operation>>) -> Option<Node<Operation>> {
554        let operation = operation.into();
555        if let Some(name) = &operation.name {
556            self.named.insert(name.clone(), operation)
557        } else {
558            self.anonymous.replace(operation)
559        }
560    }
561}
562
563impl Operation {
564    /// Returns the name of the schema type this operation selects against.
565    pub fn object_type(&self) -> &NamedType {
566        &self.selection_set.ty
567    }
568
569    /// Returns true if this is a query operation.
570    pub fn is_query(&self) -> bool {
571        self.operation_type == OperationType::Query
572    }
573
574    /// Returns true if this is a mutation operation.
575    pub fn is_mutation(&self) -> bool {
576        self.operation_type == OperationType::Mutation
577    }
578
579    /// Returns true if this is a subscription operation.
580    pub fn is_subscription(&self) -> bool {
581        self.operation_type == OperationType::Subscription
582    }
583
584    /// Return whether this operation is a query that only selects introspection meta-fields:
585    /// `__type`, `__schema`, and `__typename`
586    pub fn is_introspection(&self, document: &ExecutableDocument) -> bool {
587        self.is_query()
588            && self
589                .root_fields(document)
590                .all(|field| matches!(field.name.as_str(), "__type" | "__schema" | "__typename"))
591    }
592
593    /// Returns an iterator of field selections that are at the root of the response.
594    /// That is, inline fragments and fragment spreads at the root are traversed,
595    /// but field sub-selections are not.
596    ///
597    /// See also [`all_fields`][Self::all_fields].
598    ///
599    /// `document` is used to look up fragment definitions.
600    ///
601    /// This does **not** perform [field merging],
602    /// so multiple items in this iterator may have the same response key
603    /// or point to the same field definition.
604    /// Named fragments however are only traversed once even if spread multiple times.
605    ///
606    /// [field merging]: https://spec.graphql.org/draft/#sec-Field-Selection-Merging
607    pub fn root_fields<'doc>(
608        &'doc self,
609        document: &'doc ExecutableDocument,
610    ) -> impl Iterator<Item = &'doc Node<Field>> {
611        self.selection_set.root_fields(document)
612    }
613
614    /// Returns an iterator of all field selections in this operation.
615    ///
616    /// See also [`root_fields`][Self::root_fields].
617    ///
618    /// `document` is used to look up fragment definitions.
619    ///
620    /// This does **not** perform [field merging],
621    /// so multiple items in this iterator may have the same response key
622    /// or point to the same field definition.
623    /// Named fragments however are only traversed once even if spread multiple times.
624    ///
625    /// [field merging]: https://spec.graphql.org/draft/#sec-Field-Selection-Merging
626    pub fn all_fields<'doc>(
627        &'doc self,
628        document: &'doc ExecutableDocument,
629    ) -> impl Iterator<Item = &'doc Node<Field>> {
630        self.selection_set.all_fields(document)
631    }
632
633    serialize_method!();
634}
635
636impl Fragment {
637    pub fn type_condition(&self) -> &NamedType {
638        &self.selection_set.ty
639    }
640
641    serialize_method!();
642}
643
644impl SelectionSet {
645    /// Create a new selection set
646    pub fn new(ty: NamedType) -> Self {
647        Self {
648            ty,
649            selections: Vec::new(),
650        }
651    }
652
653    pub fn is_empty(&self) -> bool {
654        self.selections.is_empty()
655    }
656
657    pub fn push(&mut self, selection: impl Into<Selection>) {
658        self.selections.push(selection.into())
659    }
660
661    pub fn extend(&mut self, selections: impl IntoIterator<Item = impl Into<Selection>>) {
662        self.selections
663            .extend(selections.into_iter().map(|sel| sel.into()))
664    }
665
666    /// Create a new field to be added to this selection set with [`push`][Self::push]
667    ///
668    /// Returns an error if the type of this selection set is not defined
669    /// or does not have a field named `name`.
670    pub fn new_field<'schema>(
671        &self,
672        schema: &'schema Schema,
673        name: Name,
674    ) -> Result<Field, schema::FieldLookupError<'schema>> {
675        let definition = schema.type_field(&self.ty, &name)?.node.clone();
676        Ok(Field::new(name, definition))
677    }
678
679    /// Create a new inline fragment to be added to this selection set with [`push`][Self::push]
680    pub fn new_inline_fragment(&self, opt_type_condition: Option<NamedType>) -> InlineFragment {
681        if let Some(type_condition) = opt_type_condition {
682            InlineFragment::with_type_condition(type_condition)
683        } else {
684            InlineFragment::without_type_condition(self.ty.clone())
685        }
686    }
687
688    /// Create a new fragment spread to be added to this selection set with [`push`][Self::push]
689    pub fn new_fragment_spread(&self, fragment_name: Name) -> FragmentSpread {
690        FragmentSpread::new(fragment_name)
691    }
692
693    /// Returns an iterator of field selections directly in this selection set.
694    ///
695    /// Does not recur into inline fragments or fragment spreads.
696    pub fn fields(&self) -> impl Iterator<Item = &Node<Field>> {
697        self.selections.iter().filter_map(|sel| sel.as_field())
698    }
699
700    /// Returns an iterator of field selections that are at the root of the response.
701    /// That is, inline fragments and fragment spreads at the root are traversed,
702    /// but field sub-selections are not.
703    ///
704    /// See also [`all_fields`][Self::all_fields].
705    ///
706    /// `document` is used to look up fragment definitions.
707    ///
708    /// This does **not** perform [field merging],
709    /// so multiple items in this iterator may have the same response key
710    /// or point to the same field definition.
711    /// Named fragments however are only traversed once even if spread multiple times.
712    ///
713    /// [field merging]: https://spec.graphql.org/draft/#sec-Field-Selection-Merging
714    pub fn root_fields<'doc>(
715        &'doc self,
716        document: &'doc ExecutableDocument,
717    ) -> impl Iterator<Item = &'doc Node<Field>> {
718        let mut stack = vec![self.selections.iter()];
719        let mut fragments_seen = HashSet::default();
720        std::iter::from_fn(move || {
721            while let Some(selection_set_iter) = stack.last_mut() {
722                match selection_set_iter.next() {
723                    Some(Selection::Field(field)) => {
724                        // Yield one item from the `root_fields()` iterator
725                        // but ignore its sub-selections in `field.selection_set`
726                        return Some(field);
727                    }
728                    Some(Selection::InlineFragment(inline)) => {
729                        stack.push(inline.selection_set.selections.iter())
730                    }
731                    Some(Selection::FragmentSpread(spread)) => {
732                        if let Some(def) = document.fragments.get(&spread.fragment_name) {
733                            let new = fragments_seen.insert(&spread.fragment_name);
734                            if new {
735                                stack.push(def.selection_set.selections.iter())
736                            }
737                        } else {
738                            // Undefined fragments are silently ignored.
739                            // They should never happen in a valid document.
740                        }
741                    }
742                    None => {
743                        // Remove an empty iterator from the stack
744                        // and continue with the parent selection set
745                        stack.pop();
746                    }
747                }
748            }
749            None
750        })
751    }
752
753    /// Returns an iterator of all field selections in this operation.
754    ///
755    /// See also [`root_fields`][Self::root_fields].
756    ///
757    /// `document` is used to look up fragment definitions.
758    ///
759    /// This does **not** perform [field merging],
760    /// so multiple items in this iterator may have the same response key
761    /// or point to the same field definition.
762    /// Named fragments however are only traversed once even if spread multiple times.
763    ///
764    /// [field merging]: https://spec.graphql.org/draft/#sec-Field-Selection-Merging
765    pub fn all_fields<'doc>(
766        &'doc self,
767        document: &'doc ExecutableDocument,
768    ) -> impl Iterator<Item = &'doc Node<Field>> {
769        let mut stack = vec![self.selections.iter()];
770        let mut fragments_seen = HashSet::default();
771        std::iter::from_fn(move || {
772            while let Some(selection_set_iter) = stack.last_mut() {
773                match selection_set_iter.next() {
774                    Some(Selection::Field(field)) => {
775                        if !field.selection_set.is_empty() {
776                            // Will be considered for the next call
777                            stack.push(field.selection_set.selections.iter())
778                        }
779                        // Yield one item from the `all_fields()` iterator
780                        return Some(field);
781                    }
782                    Some(Selection::InlineFragment(inline)) => {
783                        stack.push(inline.selection_set.selections.iter())
784                    }
785                    Some(Selection::FragmentSpread(spread)) => {
786                        if let Some(def) = document.fragments.get(&spread.fragment_name) {
787                            let new = fragments_seen.insert(&spread.fragment_name);
788                            if new {
789                                stack.push(def.selection_set.selections.iter())
790                            }
791                        } else {
792                            // Undefined fragments are silently ignored.
793                            // They should never happen in a valid document.
794                        }
795                    }
796                    None => {
797                        // Remove an empty iterator from the stack
798                        // and continue with the parent selection set
799                        stack.pop();
800                    }
801                }
802            }
803            None
804        })
805    }
806
807    serialize_method!();
808}
809
810impl Selection {
811    pub fn directives(&self) -> &DirectiveList {
812        match self {
813            Self::Field(sel) => &sel.directives,
814            Self::FragmentSpread(sel) => &sel.directives,
815            Self::InlineFragment(sel) => &sel.directives,
816        }
817    }
818
819    pub fn as_field(&self) -> Option<&Node<Field>> {
820        if let Self::Field(field) = self {
821            Some(field)
822        } else {
823            None
824        }
825    }
826
827    pub fn as_inline_fragment(&self) -> Option<&Node<InlineFragment>> {
828        if let Self::InlineFragment(inline) = self {
829            Some(inline)
830        } else {
831            None
832        }
833    }
834
835    pub fn as_fragment_spread(&self) -> Option<&Node<FragmentSpread>> {
836        if let Self::FragmentSpread(spread) = self {
837            Some(spread)
838        } else {
839            None
840        }
841    }
842
843    serialize_method!();
844}
845
846impl From<Node<Field>> for Selection {
847    fn from(node: Node<Field>) -> Self {
848        Self::Field(node)
849    }
850}
851
852impl From<Node<InlineFragment>> for Selection {
853    fn from(node: Node<InlineFragment>) -> Self {
854        Self::InlineFragment(node)
855    }
856}
857
858impl From<Node<FragmentSpread>> for Selection {
859    fn from(node: Node<FragmentSpread>) -> Self {
860        Self::FragmentSpread(node)
861    }
862}
863
864impl From<Field> for Selection {
865    fn from(value: Field) -> Self {
866        Self::Field(Node::new(value))
867    }
868}
869
870impl From<InlineFragment> for Selection {
871    fn from(value: InlineFragment) -> Self {
872        Self::InlineFragment(Node::new(value))
873    }
874}
875
876impl From<FragmentSpread> for Selection {
877    fn from(value: FragmentSpread) -> Self {
878        Self::FragmentSpread(Node::new(value))
879    }
880}
881
882impl Field {
883    /// Create a new field with the given name and type.
884    ///
885    /// See [`SelectionSet::new_field`] too look up the type in a schema instead.
886    pub fn new(name: Name, definition: Node<schema::FieldDefinition>) -> Self {
887        let selection_set = SelectionSet::new(definition.ty.inner_named_type().clone());
888        Field {
889            definition,
890            alias: None,
891            name,
892            arguments: Vec::new(),
893            directives: DirectiveList::new(),
894            selection_set_is_explicit: false,
895            selection_set,
896        }
897    }
898
899    pub fn with_alias(mut self, alias: Name) -> Self {
900        self.alias = Some(alias);
901        self
902    }
903
904    pub fn with_opt_alias(mut self, alias: Option<Name>) -> Self {
905        self.alias = alias;
906        self
907    }
908
909    pub fn with_directive(mut self, directive: impl Into<Node<Directive>>) -> Self {
910        self.directives.push(directive.into());
911        self
912    }
913
914    pub fn with_directives(
915        mut self,
916        directives: impl IntoIterator<Item = Node<Directive>>,
917    ) -> Self {
918        self.directives.extend(directives);
919        self
920    }
921
922    pub fn with_argument(mut self, name: Name, value: impl Into<Node<Value>>) -> Self {
923        self.arguments.push((name, value).into());
924        self
925    }
926
927    pub fn with_arguments(mut self, arguments: impl IntoIterator<Item = Node<Argument>>) -> Self {
928        self.arguments.extend(arguments);
929        self
930    }
931
932    pub fn with_selection(mut self, selection: impl Into<Selection>) -> Self {
933        self.selection_set_is_explicit = true;
934        self.selection_set.push(selection);
935        self
936    }
937
938    pub fn with_selections(
939        mut self,
940        selections: impl IntoIterator<Item = impl Into<Selection>>,
941    ) -> Self {
942        self.selection_set_is_explicit = true;
943        self.selection_set.extend(selections);
944        self
945    }
946
947    /// Returns the response key for this field: the alias if there is one, or the name
948    pub fn response_key(&self) -> &Name {
949        self.alias.as_ref().unwrap_or(&self.name)
950    }
951
952    /// The type of this field, from the field definition
953    pub fn ty(&self) -> &Type {
954        &self.definition.ty
955    }
956
957    /// Look up in `schema` the definition of the inner type of this field.
958    ///
959    /// The inner type is [`ty()`][Self::ty] after unwrapping non-null and list markers.
960    pub fn inner_type_def<'a>(&self, schema: &'a Schema) -> Option<&'a schema::ExtendedType> {
961        schema.types.get(self.ty().inner_named_type())
962    }
963
964    /// Returns the value of the argument named `name`, accounting for nullability
965    /// and for the default value in `schema`’s directive definition.
966    pub fn argument_by_name(&self, name: &str) -> Result<&Node<Value>, ArgumentByNameError> {
967        Argument::argument_by_name(&self.arguments, name, || {
968            self.definition
969                .argument_by_name(name)
970                .ok_or(ArgumentByNameError::NoSuchArgument)
971        })
972    }
973
974    /// Returns the value of the argument named `name`, as specified in the field selection.
975    ///
976    /// Returns `None` if the field selection does not specify this argument.
977    ///
978    /// If the field definition makes this argument nullable or defines a default value,
979    /// consider using [`argument_by_name`][Self::argument_by_name] instead.
980    pub fn specified_argument_by_name(&self, name: &str) -> Option<&Node<Value>> {
981        Argument::specified_argument_by_name(&self.arguments, name)
982    }
983
984    serialize_method!();
985}
986
987impl InlineFragment {
988    pub fn with_type_condition(type_condition: NamedType) -> Self {
989        let selection_set = SelectionSet::new(type_condition.clone());
990        Self {
991            type_condition: Some(type_condition),
992            directives: DirectiveList::new(),
993            selection_set,
994        }
995    }
996
997    pub fn without_type_condition(parent_selection_set_type: NamedType) -> Self {
998        Self {
999            type_condition: None,
1000            directives: DirectiveList::new(),
1001            selection_set: SelectionSet::new(parent_selection_set_type),
1002        }
1003    }
1004
1005    pub fn with_directive(mut self, directive: impl Into<Node<Directive>>) -> Self {
1006        self.directives.push(directive.into());
1007        self
1008    }
1009
1010    pub fn with_directives(
1011        mut self,
1012        directives: impl IntoIterator<Item = Node<Directive>>,
1013    ) -> Self {
1014        self.directives.extend(directives);
1015        self
1016    }
1017
1018    pub fn with_selection(mut self, selection: impl Into<Selection>) -> Self {
1019        self.selection_set.push(selection);
1020        self
1021    }
1022
1023    pub fn with_selections(
1024        mut self,
1025        selections: impl IntoIterator<Item = impl Into<Selection>>,
1026    ) -> Self {
1027        self.selection_set.extend(selections);
1028        self
1029    }
1030
1031    serialize_method!();
1032}
1033
1034impl FragmentSpread {
1035    pub fn new(fragment_name: Name) -> Self {
1036        Self {
1037            fragment_name,
1038            directives: DirectiveList::new(),
1039        }
1040    }
1041
1042    pub fn with_directive(mut self, directive: impl Into<Node<Directive>>) -> Self {
1043        self.directives.push(directive.into());
1044        self
1045    }
1046
1047    pub fn with_directives(
1048        mut self,
1049        directives: impl IntoIterator<Item = Node<Directive>>,
1050    ) -> Self {
1051        self.directives.extend(directives);
1052        self
1053    }
1054
1055    pub fn fragment_def<'a>(&self, document: &'a ExecutableDocument) -> Option<&'a Node<Fragment>> {
1056        document.fragments.get(&self.fragment_name)
1057    }
1058
1059    serialize_method!();
1060}
1061
1062impl FieldSet {
1063    /// Parse the given source a selection set with optional outer brackets.
1064    ///
1065    /// `path` is the filesystem path (or arbitrary string) used in diagnostics
1066    /// to identify this source file to users.
1067    ///
1068    /// Create a [`Parser`] to use different parser configuration.
1069    pub fn parse(
1070        schema: &Valid<Schema>,
1071        type_name: NamedType,
1072        source_text: impl Into<String>,
1073        path: impl AsRef<Path>,
1074    ) -> Result<FieldSet, WithErrors<FieldSet>> {
1075        Parser::new().parse_field_set(schema, type_name, source_text, path)
1076    }
1077
1078    /// [`parse`][Self::parse] then [`validate`][Self::validate],
1079    /// to get a `Valid<ExecutableDocument>` when mutating it isn’t needed.
1080    pub fn parse_and_validate(
1081        schema: &Valid<Schema>,
1082        type_name: NamedType,
1083        source_text: impl Into<String>,
1084        path: impl AsRef<Path>,
1085    ) -> Result<Valid<Self>, WithErrors<Self>> {
1086        let (field_set, mut errors) =
1087            Parser::new().parse_field_set_inner(schema, type_name, source_text, path);
1088        validation::validate_field_set(&mut errors, schema, &field_set);
1089        errors.into_valid_result(field_set)
1090    }
1091
1092    pub fn validate(&self, schema: &Valid<Schema>) -> Result<(), DiagnosticList> {
1093        let mut sources = IndexMap::clone(&schema.sources);
1094        sources.extend(self.sources.iter().map(|(k, v)| (*k, v.clone())));
1095        let mut errors = DiagnosticList::new(Arc::new(sources));
1096        validation::validate_field_set(&mut errors, schema, self);
1097        errors.into_result()
1098    }
1099
1100    serialize_method!();
1101}
1102
1103impl fmt::Display for SelectionPath {
1104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1105        match &self.root {
1106            ExecutableDefinitionName::AnonymousOperation(operation_type) => {
1107                write!(f, "{operation_type}")?
1108            }
1109            ExecutableDefinitionName::NamedOperation(operation_type, name) => {
1110                write!(f, "{operation_type} {name}")?
1111            }
1112            ExecutableDefinitionName::Fragment(name) => write!(f, "fragment {name}")?,
1113        }
1114        for name in &self.nested_fields {
1115            write!(f, " → {name}")?
1116        }
1117        Ok(())
1118    }
1119}