Skip to content

Commit 158b5e7

Browse files
committed
Add QueryVisitor trait and functions for walking a query ast
1 parent eca72ce commit 158b5e7

File tree

2 files changed

+226
-0
lines changed

2 files changed

+226
-0
lines changed

src/query/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod ast;
44
mod error;
55
mod format;
66
mod grammar;
7+
pub mod query_visitor;
78

89

910
pub use self::grammar::parse_query;

src/query/query_visitor.rs

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
//! Query syntax tree traversal.
2+
//!
3+
//! Each method of [`QueryVisitor`] is a hook that can be overridden to customize the behavior when
4+
//! visiting the corresponding type of node. By default, the methods don't do anything. The actual
5+
//! walking of the ast is done by the `walk_*` functions. So to run a visitor over the whole
6+
//! document you should use [`walk_document`].
7+
//!
8+
//! Example:
9+
//!
10+
//! ```
11+
//! use graphql_parser::query::{
12+
//! Field,
13+
//! parse_query,
14+
//! query_visitor::{QueryVisitor, walk_document},
15+
//! };
16+
//!
17+
//! struct FieldsCounter {
18+
//! count: usize,
19+
//! }
20+
//!
21+
//! impl FieldsCounter {
22+
//! fn new() -> Self {
23+
//! Self { count: 0 }
24+
//! }
25+
//! }
26+
//!
27+
//! impl<'ast> QueryVisitor<'ast> for FieldsCounter {
28+
//! fn visit_field(&mut self, node: &'ast Field) {
29+
//! self.count += 1
30+
//! }
31+
//! }
32+
//!
33+
//! fn main() {
34+
//! let mut number_of_type = FieldsCounter::new();
35+
//!
36+
//! let doc = parse_query(r#"
37+
//! query TestQuery {
38+
//! users {
39+
//! id
40+
//! country {
41+
//! id
42+
//! }
43+
//! }
44+
//! }
45+
//! "#).expect("Failed to parse query");
46+
//!
47+
//! walk_document(&mut number_of_type, &doc);
48+
//!
49+
//! assert_eq!(number_of_type.count, 2);
50+
//! }
51+
//! ```
52+
//!
53+
//! [`QueryVisitor`]: /graphql_parser/query/query_visitor/trait.QueryVisitor.html
54+
//! [`walk_document`]: /graphql_parser/query/query_visitor/fn.walk_document.html
55+
56+
#![allow(unused_variables)]
57+
58+
use super::ast::*;
59+
60+
/// Trait for easy query syntax tree traversal.
61+
///
62+
/// See [module docs](/graphql_parser/query/query_visitor/index.html) for more info.
63+
pub trait QueryVisitor<'ast> {
64+
fn visit_document(&mut self, node: &'ast Document) {}
65+
66+
fn visit_definition(&mut self, node: &'ast Definition) {}
67+
68+
fn visit_fragment_definition(&mut self, node: &'ast FragmentDefinition) {}
69+
70+
fn visit_operation_definition(&mut self, node: &'ast OperationDefinition) {}
71+
72+
fn visit_query(&mut self, node: &'ast Query) {}
73+
74+
fn visit_mutation(&mut self, node: &'ast Mutation) {}
75+
76+
fn visit_subscription(&mut self, node: &'ast Subscription) {}
77+
78+
fn visit_selection_set(&mut self, node: &'ast SelectionSet) {}
79+
80+
fn visit_variable_definition(&mut self, node: &'ast VariableDefinition) {}
81+
82+
fn visit_selection(&mut self, node: &'ast Selection) {}
83+
84+
fn visit_field(&mut self, node: &'ast Field) {}
85+
86+
fn visit_fragment_spread(&mut self, node: &'ast FragmentSpread) {}
87+
88+
fn visit_inline_fragment(&mut self, node: &'ast InlineFragment) {}
89+
}
90+
91+
92+
/// Walk a query syntax tree and call the visitor methods for each type of node.
93+
///
94+
/// This function is how you should initiate a visitor.
95+
pub fn walk_document<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Document) {
96+
visitor.visit_document(node);
97+
for def in &node.definitions {
98+
walk_definition(visitor, def);
99+
}
100+
}
101+
102+
fn walk_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Definition) {
103+
use super::ast::Definition::*;
104+
105+
visitor.visit_definition(node);
106+
match node {
107+
Operation(inner) => {
108+
visitor.visit_operation_definition(inner);
109+
walk_operation_definition(visitor, inner);
110+
},
111+
Fragment(inner) => {
112+
visitor.visit_fragment_definition(inner);
113+
walk_fragment_definition(visitor, inner);
114+
},
115+
}
116+
}
117+
118+
fn walk_fragment_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast FragmentDefinition) {
119+
visitor.visit_fragment_definition(node);
120+
}
121+
122+
fn walk_operation_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast OperationDefinition) {
123+
use super::ast::OperationDefinition::*;
124+
125+
visitor.visit_operation_definition(node);
126+
match node {
127+
SelectionSet(inner) => {
128+
visitor.visit_selection_set(inner);
129+
walk_selection_set(visitor, inner);
130+
}
131+
Query(inner) => {
132+
visitor.visit_query(inner);
133+
walk_query(visitor, inner);
134+
}
135+
Mutation(inner) => {
136+
visitor.visit_mutation(inner);
137+
walk_mutation(visitor, inner);
138+
}
139+
Subscription(inner) => {
140+
visitor.visit_subscription(inner);
141+
walk_subscription(visitor, inner);
142+
}
143+
}
144+
}
145+
146+
fn walk_query<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Query) {
147+
visitor.visit_query(node);
148+
149+
for var_def in &node.variable_definitions {
150+
visitor.visit_variable_definition(var_def);
151+
walk_variable_definition(visitor, var_def);
152+
}
153+
154+
visitor.visit_selection_set(&node.selection_set);
155+
walk_selection_set(visitor, &node.selection_set);
156+
}
157+
158+
fn walk_mutation<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Mutation) {
159+
visitor.visit_mutation(node);
160+
161+
for var_def in &node.variable_definitions {
162+
visitor.visit_variable_definition(var_def);
163+
walk_variable_definition(visitor, var_def);
164+
}
165+
166+
visitor.visit_selection_set(&node.selection_set);
167+
walk_selection_set(visitor, &node.selection_set);
168+
}
169+
170+
fn walk_subscription<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Subscription) {
171+
visitor.visit_subscription(node);
172+
173+
for var_def in &node.variable_definitions {
174+
visitor.visit_variable_definition(var_def);
175+
walk_variable_definition(visitor, var_def);
176+
}
177+
178+
visitor.visit_selection_set(&node.selection_set);
179+
walk_selection_set(visitor, &node.selection_set);
180+
}
181+
182+
fn walk_selection_set<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast SelectionSet) {
183+
visitor.visit_selection_set(node);
184+
185+
for selection in &node.items {
186+
visitor.visit_selection(selection);
187+
walk_selection(visitor, selection);
188+
}
189+
}
190+
191+
fn walk_variable_definition<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast VariableDefinition) {
192+
visitor.visit_variable_definition(node)
193+
}
194+
195+
fn walk_selection<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Selection) {
196+
use super::ast::Selection::*;
197+
198+
visitor.visit_selection(node);
199+
match node {
200+
Field(inner) => {
201+
visitor.visit_field(inner);
202+
walk_field(visitor, inner);
203+
}
204+
FragmentSpread(inner) => {
205+
visitor.visit_fragment_spread(inner);
206+
walk_fragment_spread(visitor, inner);
207+
}
208+
InlineFragment(inner) => {
209+
visitor.visit_inline_fragment(inner);
210+
walk_inline_fragment(visitor, inner);
211+
}
212+
}
213+
}
214+
215+
fn walk_field<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast Field) {
216+
visitor.visit_field(node)
217+
}
218+
219+
fn walk_fragment_spread<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast FragmentSpread) {
220+
visitor.visit_fragment_spread(node)
221+
}
222+
223+
fn walk_inline_fragment<'ast, V: QueryVisitor<'ast>>(visitor: &mut V, node: &'ast InlineFragment) {
224+
visitor.visit_inline_fragment(node);
225+
}

0 commit comments

Comments
 (0)