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