1use crate::static_graphql::schema::{
2 Definition, DirectiveDefinition, Document, EnumType, EnumValue, Field, InputObjectType,
3 InputValue, InterfaceType, ObjectType, ScalarType, SchemaDefinition, TypeDefinition, UnionType,
4};
5
6pub trait SchemaVisitor<T = ()> {
8 fn visit_schema_document(&self, document: &Document, _visitor_context: &mut T) {
9 self.enter_document(document, _visitor_context);
10
11 for definition in &document.definitions {
12 match definition {
13 Definition::SchemaDefinition(schema_definition) => {
14 self.enter_schema_definition(schema_definition, _visitor_context);
15 self.leave_schema_definition(schema_definition, _visitor_context);
16 }
17 Definition::TypeDefinition(type_definition) => {
18 self.enter_type_definition(type_definition, _visitor_context);
19
20 match type_definition {
21 TypeDefinition::Object(object) => {
22 self.enter_object_type(object, _visitor_context);
23
24 for field in &object.fields {
25 self.enter_object_type_field(field, object, _visitor_context);
26 self.leave_object_type_field(field, object, _visitor_context);
28 }
29
30 self.leave_object_type(object, _visitor_context);
31 }
32 TypeDefinition::Scalar(scalar) => {
33 self.enter_scalar_type(scalar, _visitor_context);
34 self.leave_scalar_type(scalar, _visitor_context);
35 }
36 TypeDefinition::Enum(enum_) => {
37 self.enter_enum_type(enum_, _visitor_context);
38
39 for value in &enum_.values {
40 self.enter_enum_value(value, enum_, _visitor_context);
41 self.leave_enum_value(value, enum_, _visitor_context);
42 }
43
44 self.leave_enum_type(enum_, _visitor_context);
45 }
46 TypeDefinition::Union(union) => {
47 self.enter_union_type(union, _visitor_context);
48 self.leave_union_type(union, _visitor_context);
49 }
50 TypeDefinition::InputObject(input_object) => {
51 self.enter_input_object_type(input_object, _visitor_context);
52
53 for field in &input_object.fields {
54 self.enter_input_object_type_field(
55 field,
56 input_object,
57 _visitor_context,
58 );
59 self.leave_input_object_type_field(
60 field,
61 input_object,
62 _visitor_context,
63 );
64 }
65
66 self.leave_input_object_type(input_object, _visitor_context);
67 }
68 TypeDefinition::Interface(interface) => {
69 self.enter_interface_type(interface, _visitor_context);
70
71 for field in &interface.fields {
72 self.enter_interface_type_field(field, interface, _visitor_context);
73 self.leave_interface_type_field(field, interface, _visitor_context);
74 }
75
76 self.leave_interface_type(interface, _visitor_context);
77 }
78 }
79
80 self.leave_type_definition(type_definition, _visitor_context);
81 }
82 Definition::DirectiveDefinition(directive_definition) => {
83 self.enter_directive_definition(directive_definition, _visitor_context);
84 self.leave_directive_definition(directive_definition, _visitor_context);
85 }
86 Definition::TypeExtension(_type_extension) => {
87 panic!("TypeExtension not supported at the moment");
89 }
90 }
91 }
92
93 self.leave_document(document, _visitor_context);
94 }
95
96 fn enter_document(&self, _node: &Document, _visitor_context: &mut T) {}
97 fn leave_document(&self, _node: &Document, _visitor_context: &mut T) {}
98
99 fn enter_schema_definition(&self, _node: &SchemaDefinition, _visitor_context: &mut T) {}
100 fn leave_schema_definition(&self, _node: &SchemaDefinition, _visitor_context: &mut T) {}
101
102 fn enter_directive_definition(&self, _node: &DirectiveDefinition, _visitor_context: &mut T) {}
103 fn leave_directive_definition(&self, _node: &DirectiveDefinition, _visitor_context: &mut T) {}
104
105 fn enter_type_definition(&self, _node: &TypeDefinition, _visitor_context: &mut T) {}
106 fn leave_type_definition(&self, _node: &TypeDefinition, _visitor_context: &mut T) {}
107
108 fn enter_interface_type(&self, _node: &InterfaceType, _visitor_context: &mut T) {}
109 fn leave_interface_type(&self, _node: &InterfaceType, _visitor_context: &mut T) {}
110
111 fn enter_interface_type_field(
112 &self,
113 _node: &Field,
114 _type_: &InterfaceType,
115 _visitor_context: &mut T,
116 ) {
117 }
118 fn leave_interface_type_field(
119 &self,
120 _node: &Field,
121 _type_: &InterfaceType,
122 _visitor_context: &mut T,
123 ) {
124 }
125
126 fn enter_object_type(&self, _node: &ObjectType, _visitor_context: &mut T) {}
127 fn leave_object_type(&self, _node: &ObjectType, _visitor_context: &mut T) {}
128
129 fn enter_object_type_field(
130 &self,
131 _node: &Field,
132 _type_: &ObjectType,
133 _visitor_context: &mut T,
134 ) {
135 }
136 fn leave_object_type_field(
137 &self,
138 _node: &Field,
139 _type_: &ObjectType,
140 _visitor_context: &mut T,
141 ) {
142 }
143
144 fn enter_input_object_type(&self, _node: &InputObjectType, _visitor_context: &mut T) {}
145 fn leave_input_object_type(&self, _node: &InputObjectType, _visitor_context: &mut T) {}
146
147 fn enter_input_object_type_field(
148 &self,
149 _node: &InputValue,
150 _input_type: &InputObjectType,
151 _visitor_context: &mut T,
152 ) {
153 }
154 fn leave_input_object_type_field(
155 &self,
156 _node: &InputValue,
157 _input_type: &InputObjectType,
158 _visitor_context: &mut T,
159 ) {
160 }
161
162 fn enter_union_type(&self, _node: &UnionType, _visitor_context: &mut T) {}
163 fn leave_union_type(&self, _node: &UnionType, _visitor_context: &mut T) {}
164
165 fn enter_scalar_type(&self, _node: &ScalarType, _visitor_context: &mut T) {}
166 fn leave_scalar_type(&self, _node: &ScalarType, _visitor_context: &mut T) {}
167
168 fn enter_enum_type(&self, _node: &EnumType, _visitor_context: &mut T) {}
169 fn leave_enum_type(&self, _node: &EnumType, _visitor_context: &mut T) {}
170
171 fn enter_enum_value(&self, _node: &EnumValue, _enum: &EnumType, _visitor_context: &mut T) {}
172 fn leave_enum_value(&self, _node: &EnumValue, _enum: &EnumType, _visitor_context: &mut T) {}
173}
174
175#[test]
176fn visit_schema() {
177 use crate::parser::schema::parse_schema;
178 let schema_ast = parse_schema(
179 r#"
180 scalar Date
181
182 type Query {
183 user(id: ID!): User!
184 users(filter: UsersFilter): [User!]!
185 now: Date
186 }
187
188 input UsersFilter {
189 name: String
190 }
191
192 type User implements Node {
193 id: ID!
194 name: String!
195 role: Role!
196 }
197
198 interface Node {
199 id: ID!
200 }
201
202 type Test {
203 foo: String!
204 }
205
206 enum Role {
207 USER
208 ADMIN
209 }
210
211 union TestUnion = Test | User
212
213 "#,
214 )
215 .expect("Failed to parse schema");
216
217 struct TestVisitorCollected {
218 collected_object_type: Vec<String>,
219 collected_scalar_type: Vec<String>,
220 collected_union_type: Vec<String>,
221 collected_input_type: Vec<String>,
222 collected_enum_type: Vec<String>,
223 collected_enum_value: Vec<String>,
224 collected_interface_type: Vec<String>,
225 collected_object_type_field: Vec<String>,
226 collected_interface_type_field: Vec<String>,
227 collected_input_type_fields: Vec<String>,
228 }
229
230 struct TestVisitor;
231
232 impl TestVisitor {
233 fn collect_visited_info(&self, document: &Document) -> TestVisitorCollected {
234 let mut collected = TestVisitorCollected {
235 collected_object_type: Vec::new(),
236 collected_interface_type: Vec::new(),
237 collected_object_type_field: Vec::new(),
238 collected_interface_type_field: Vec::new(),
239 collected_scalar_type: Vec::new(),
240 collected_union_type: Vec::new(),
241 collected_enum_type: Vec::new(),
242 collected_enum_value: Vec::new(),
243 collected_input_type: Vec::new(),
244 collected_input_type_fields: Vec::new(),
245 };
246 self.visit_schema_document(document, &mut collected);
247
248 collected
249 }
250 }
251
252 impl SchemaVisitor<TestVisitorCollected> for TestVisitor {
253 fn enter_object_type(
254 &self,
255 _node: &ObjectType,
256 _visitor_context: &mut TestVisitorCollected,
257 ) {
258 _visitor_context
259 .collected_object_type
260 .push(_node.name.clone());
261 }
262
263 fn enter_object_type_field(
264 &self,
265 _node: &Field,
266 _type_: &ObjectType,
267 _visitor_context: &mut TestVisitorCollected,
268 ) {
269 let field_id = format!("{}.{}", _type_.name.as_str(), _node.name.as_str());
270 _visitor_context.collected_object_type_field.push(field_id);
271 }
272
273 fn enter_interface_type(
274 &self,
275 _node: &InterfaceType,
276 _visitor_context: &mut TestVisitorCollected,
277 ) {
278 _visitor_context
279 .collected_interface_type
280 .push(_node.name.clone());
281 }
282
283 fn enter_interface_type_field(
284 &self,
285 _node: &Field,
286 _type_: &InterfaceType,
287 _visitor_context: &mut TestVisitorCollected,
288 ) {
289 _visitor_context
290 .collected_interface_type_field
291 .push(_node.name.clone());
292 }
293
294 fn enter_scalar_type(
295 &self,
296 _node: &ScalarType,
297 _visitor_context: &mut TestVisitorCollected,
298 ) {
299 _visitor_context
300 .collected_scalar_type
301 .push(_node.name.clone());
302 }
303
304 fn enter_union_type(&self, _node: &UnionType, _visitor_context: &mut TestVisitorCollected) {
305 _visitor_context
306 .collected_union_type
307 .push(_node.name.clone());
308 }
309
310 fn enter_enum_type(&self, _node: &EnumType, _visitor_context: &mut TestVisitorCollected) {
311 _visitor_context
312 .collected_enum_type
313 .push(_node.name.clone());
314 }
315
316 fn enter_enum_value(
317 &self,
318 _node: &EnumValue,
319 _enum: &EnumType,
320 _visitor_context: &mut TestVisitorCollected,
321 ) {
322 let enum_value_id = format!("{}.{}", _enum.name.as_str(), _node.name.as_str());
323 _visitor_context.collected_enum_value.push(enum_value_id);
324 }
325
326 fn enter_input_object_type(
327 &self,
328 _node: &InputObjectType,
329 _visitor_context: &mut TestVisitorCollected,
330 ) {
331 _visitor_context
332 .collected_input_type
333 .push(_node.name.clone());
334 }
335
336 fn enter_input_object_type_field(
337 &self,
338 _node: &InputValue,
339 _input_type: &InputObjectType,
340 _visitor_context: &mut TestVisitorCollected,
341 ) {
342 let field_id = format!("{}.{}", _input_type.name.as_str(), _node.name.as_str());
343 _visitor_context.collected_input_type_fields.push(field_id);
344 }
345 }
346
347 let visitor = TestVisitor {};
348 let collected = visitor.collect_visited_info(&schema_ast);
349
350 assert_eq!(
351 collected.collected_object_type,
352 vec!["Query", "User", "Test"]
353 );
354 assert_eq!(
355 collected.collected_object_type_field,
356 vec![
357 "Query.user",
358 "Query.users",
359 "Query.now",
360 "User.id",
361 "User.name",
362 "User.role",
363 "Test.foo"
364 ]
365 );
366 assert_eq!(collected.collected_interface_type, vec!["Node"]);
367 assert_eq!(collected.collected_union_type, vec!["TestUnion"]);
368 assert_eq!(collected.collected_scalar_type, vec!["Date"]);
369 assert_eq!(collected.collected_enum_type, vec!["Role"]);
370 assert_eq!(
371 collected.collected_enum_value,
372 vec!["Role.USER", "Role.ADMIN"]
373 );
374 assert_eq!(collected.collected_input_type, vec!["UsersFilter"]);
375 assert_eq!(
376 collected.collected_input_type_fields,
377 vec!["UsersFilter.name"]
378 );
379}