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}