Skip to main content

libgraphql_parser/ast/
selection.rs

1use crate::ast::AstNode;
2use crate::ast::DirectiveAnnotation;
3use crate::ast::FieldSelection;
4use crate::ast::FragmentSpread;
5use crate::ast::InlineFragment;
6use crate::ast::Name;
7use crate::ByteSpan;
8use crate::SourceMap;
9use crate::SourceSpan;
10use inherent::inherent;
11
12/// A single selection within a selection set.
13///
14/// See
15/// [Selection Sets](https://spec.graphql.org/September2025/#sec-Selection-Sets)
16/// in the spec.
17#[derive(Clone, Debug, PartialEq)]
18pub enum Selection<'src> {
19    Field(FieldSelection<'src>),
20    FragmentSpread(FragmentSpread<'src>),
21    InlineFragment(InlineFragment<'src>),
22}
23
24impl<'src> Selection<'src> {
25    /// Returns the directive annotations applied to this selection.
26    pub fn directive_annotations(&self) -> &[DirectiveAnnotation<'src>] {
27        match self {
28            Self::Field(s) => &s.directives,
29            Self::FragmentSpread(s) => &s.directives,
30            Self::InlineFragment(s) => &s.directives,
31        }
32    }
33
34    /// Returns the name of this selection, or [`None`] for
35    /// inline fragments (which have no name).
36    pub fn name(&self) -> Option<&Name<'src>> {
37        match self {
38            Self::Field(s) => Some(&s.name),
39            Self::FragmentSpread(s) => Some(&s.name),
40            Self::InlineFragment(_) => None,
41        }
42    }
43
44    /// Returns the name of this selection as a string slice,
45    /// or [`None`] for inline fragments.
46    ///
47    /// Convenience accessor for `self.name().value`.
48    pub fn name_value(&self) -> Option<&str> {
49        self.name().map(|n| n.value.as_ref())
50    }
51}
52
53#[inherent]
54impl AstNode for Selection<'_> {
55    /// See [`AstNode::append_source()`](crate::ast::AstNode::append_source).
56    pub fn append_source(
57        &self,
58        sink: &mut String,
59        source: Option<&str>,
60    ) {
61        match self {
62            Selection::Field(s) => {
63                s.append_source(sink, source)
64            },
65            Selection::FragmentSpread(s) => {
66                s.append_source(sink, source)
67            },
68            Selection::InlineFragment(s) => {
69                s.append_source(sink, source)
70            },
71        }
72    }
73
74    /// Returns this selection's byte-offset span within the
75    /// source text.
76    ///
77    /// The returned [`ByteSpan`] can be resolved to line/column
78    /// positions via [`source_span()`](Self::source_span) or
79    /// [`ByteSpan::resolve()`].
80    pub fn byte_span(&self) -> ByteSpan {
81        match self {
82            Self::Field(s) => s.span,
83            Self::FragmentSpread(s) => s.span,
84            Self::InlineFragment(s) => s.span,
85        }
86    }
87
88    /// Resolves this selection's position to line/column
89    /// coordinates using the given [`SourceMap`].
90    ///
91    /// Returns [`None`] if the byte offsets cannot be resolved
92    /// (e.g. the span was synthetically constructed without
93    /// valid position data).
94    pub fn source_span(
95        &self,
96        source_map: &SourceMap,
97    ) -> Option<SourceSpan> {
98        self.byte_span().resolve(source_map)
99    }
100}