apollo_parser/cst/
mod.rs

1//! Typed Concrete Syntax Tree module to access nodes in the tree.
2//!
3//! The nodes described here are those also described in the [GraphQL grammar],
4//! with a few exceptions. For example, for easy of querying the CST we do not
5//! separate `Definition` into `ExecutableDefinition` and
6//! `TypeSystemDefinitionOrExtension`. Instead, all possible definitions and
7//! extensions can be accessed with `Definition`.
8//!
9//! Each struct in this module has getter methods to access information that's
10//! part of its node. For example, as per spec a `UnionTypeDefinition` is defined as follows:
11//!
12//! ```ungram
13//! UnionTypeDefinition =
14//!   Description? 'union' Name Directives? UnionMemberTypes?
15//! ```
16//!
17//! It will then have getters for `Description`, union token, `Name`,
18//! `Directives` and `UnionMemberTypes`. Checkout documentation for the Struct
19//! you're working with to find out its exact API.
20//!
21//! ## Example
22//! This example parses a subgraph schema and looks at the various Definition Names.
23//!
24//! ```rust
25//! use apollo_parser::{cst, Parser};
26//!
27//! let schema = r#"
28//! directive @tag(name: String!) repeatable on FIELD_DEFINITION
29//!
30//! type ProductVariation {
31//!   id: ID!
32//! }
33//! scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122")
34//!
35//! union SearchResult = Photo | Person
36//!
37//! extend type Query {
38//!   allProducts: [Product]
39//!   product(id: ID!): Product
40//! }
41//! "#;
42//! let parser = Parser::new(schema);
43//! let cst = parser.parse();
44//!
45//! assert_eq!(0, cst.errors().len());
46//! let document = cst.document();
47//! for definition in document.definitions() {
48//!     match definition {
49//!         cst::Definition::DirectiveDefinition(directive) => {
50//!             assert_eq!(
51//!                 directive
52//!                     .name()
53//!                     .expect("Cannot get directive name.")
54//!                     .text()
55//!                     .as_ref(),
56//!                 "tag"
57//!             )
58//!         }
59//!         cst::Definition::ObjectTypeDefinition(object_type) => {
60//!             assert_eq!(
61//!                 object_type
62//!                     .name()
63//!                     .expect("Cannot get object type definition name.")
64//!                     .text()
65//!                     .as_ref(),
66//!                 "ProductVariation"
67//!             )
68//!         }
69//!         cst::Definition::UnionTypeDefinition(union_type) => {
70//!             assert_eq!(
71//!                 union_type
72//!                     .name()
73//!                     .expect("Cannot get union type definition name.")
74//!                     .text()
75//!                     .as_ref(),
76//!                 "SearchResult"
77//!             )
78//!         }
79//!         cst::Definition::ScalarTypeDefinition(scalar_type) => {
80//!             assert_eq!(
81//!                 scalar_type
82//!                     .name()
83//!                     .expect("Cannot get scalar type definition name.")
84//!                     .text()
85//!                     .as_ref(),
86//!                 "UUID"
87//!             )
88//!         }
89//!         cst::Definition::ObjectTypeExtension(object_type) => {
90//!             assert_eq!(
91//!                 object_type
92//!                     .name()
93//!                     .expect("Cannot get object type extension name.")
94//!                     .text()
95//!                     .as_ref(),
96//!                 "Query"
97//!             )
98//!         }
99//!         _ => unimplemented!(),
100//!     }
101//! }
102//! ```
103//!
104//! [GraphQL grammar]: https://spec.graphql.org/October2021/#sec-Document-Syntax
105mod generated;
106mod node_ext;
107
108pub use crate::parser::SyntaxNodePtr;
109use crate::SyntaxKind;
110pub use crate::SyntaxNode;
111use crate::SyntaxNodeChildren;
112use crate::SyntaxToken;
113pub use generated::nodes::*;
114use std::marker::PhantomData;
115
116/// The main trait to go from untyped `SyntaxNode`  to a typed CST. The
117/// conversion itself has zero runtime cost: CST and syntax nodes have exactly
118/// the same representation: a pointer to the tree root and a pointer to the
119/// node itself.
120pub trait CstNode {
121    fn can_cast(kind: SyntaxKind) -> bool
122    where
123        Self: Sized;
124
125    fn cast(syntax: SyntaxNode) -> Option<Self>
126    where
127        Self: Sized;
128
129    fn syntax(&self) -> &SyntaxNode;
130
131    fn source_string(&self) -> String {
132        self.syntax().to_string()
133    }
134
135    fn clone_for_update(&self) -> Self
136    where
137        Self: Sized,
138    {
139        Self::cast(self.syntax().clone_for_update()).unwrap()
140    }
141
142    fn clone_subtree(&self) -> Self
143    where
144        Self: Sized,
145    {
146        Self::cast(self.syntax().clone_subtree()).unwrap()
147    }
148}
149
150/// Like `CstNode`, but wraps tokens rather than interior nodes.
151pub trait CstToken {
152    fn can_cast(token: SyntaxKind) -> bool
153    where
154        Self: Sized;
155
156    fn cast(syntax: SyntaxToken) -> Option<Self>
157    where
158        Self: Sized;
159
160    fn syntax(&self) -> &SyntaxToken;
161
162    fn text(&self) -> &str {
163        self.syntax().text()
164    }
165}
166
167/// An iterator over `SyntaxNode` children of a particular CST type.
168#[derive(Debug, Clone)]
169pub struct CstChildren<N> {
170    inner: SyntaxNodeChildren,
171    ph: PhantomData<N>,
172}
173
174impl<N> CstChildren<N> {
175    fn new(parent: &SyntaxNode) -> Self {
176        CstChildren {
177            inner: parent.children(),
178            ph: PhantomData,
179        }
180    }
181}
182
183impl<N: CstNode> Iterator for CstChildren<N> {
184    type Item = N;
185    fn next(&mut self) -> Option<N> {
186        self.inner.find_map(N::cast)
187    }
188}
189
190mod support {
191    use super::CstChildren;
192    use super::CstNode;
193    use super::SyntaxKind;
194    use super::SyntaxNode;
195    use super::SyntaxToken;
196
197    pub(super) fn child<N: CstNode>(parent: &SyntaxNode) -> Option<N> {
198        parent.children().find_map(N::cast)
199    }
200
201    pub(super) fn children<N: CstNode>(parent: &SyntaxNode) -> CstChildren<N> {
202        CstChildren::new(parent)
203    }
204
205    pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
206        parent
207            .children_with_tokens()
208            .filter_map(|it| it.into_token())
209            .find(|it| it.kind() == kind)
210    }
211}