1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
use crate::static_graphql::query::{
    Definition, Document, Field, FragmentDefinition, FragmentSpread, InlineFragment, Mutation,
    OperationDefinition, Query, Selection, SelectionSet, Subscription, Value, VariableDefinition,
};

use super::DefaultVisitorContext;

pub trait QueryVisitor<T = DefaultVisitorContext> {
    fn visit_document(&self, node: &Document, visitor_context: &mut T) {
        self.enter_document(node, visitor_context);

        for definition in &node.definitions {
            self.enter_definition(definition, visitor_context);

            match definition {
                Definition::Fragment(fragment) => {
                    self.enter_fragment_definition(fragment, visitor_context);
                    self.__visit_selection_set(&fragment.selection_set, visitor_context);
                    self.leave_fragment_definition(fragment, visitor_context);
                }
                Definition::Operation(operation) => {
                    self.enter_operation_definition(operation, visitor_context);

                    match operation {
                        OperationDefinition::Query(query) => {
                            self.enter_query(query, visitor_context);

                            for variable in &query.variable_definitions {
                                self.enter_variable_definition(
                                    variable,
                                    operation,
                                    visitor_context,
                                );
                                self.leave_variable_definition(
                                    variable,
                                    operation,
                                    visitor_context,
                                );
                            }

                            self.__visit_selection_set(&query.selection_set, visitor_context);
                            self.leave_query(query, visitor_context);
                        }
                        OperationDefinition::Mutation(mutation) => {
                            self.enter_mutation(mutation, visitor_context);
                            for variable in &mutation.variable_definitions {
                                self.enter_variable_definition(
                                    variable,
                                    operation,
                                    visitor_context,
                                );
                                self.leave_variable_definition(
                                    variable,
                                    operation,
                                    visitor_context,
                                );
                            }
                            self.__visit_selection_set(&mutation.selection_set, visitor_context);
                            self.leave_mutation(mutation, visitor_context);
                        }
                        OperationDefinition::Subscription(subscription) => {
                            self.enter_subscription(subscription, visitor_context);
                            for variable in &subscription.variable_definitions {
                                self.enter_variable_definition(
                                    variable,
                                    operation,
                                    visitor_context,
                                );
                                self.leave_variable_definition(
                                    variable,
                                    operation,
                                    visitor_context,
                                );
                            }
                            self.__visit_selection_set(
                                &subscription.selection_set,
                                visitor_context,
                            );
                            self.leave_subscription(subscription, visitor_context);
                        }
                        OperationDefinition::SelectionSet(selection_set) => {
                            self.enter_selection_set(selection_set, visitor_context);
                            self.__visit_selection_set(&selection_set, visitor_context);
                            self.leave_selection_set(selection_set, visitor_context);
                        }
                    }

                    self.leave_operation_definition(operation, visitor_context);
                }
            }

            self.leave_definition(definition, visitor_context);
        }

        self.leave_document(node, visitor_context);
    }

    fn __visit_selection_set(&self, _node: &SelectionSet, visitor_context: &mut T) {
        self.enter_selection_set(_node, visitor_context);

        for selection in &_node.items {
            self.enter_selection(selection, visitor_context);

            match selection {
                Selection::Field(field) => {
                    self.enter_field(field, visitor_context);

                    for (name, argument) in &field.arguments {
                        self.enter_field_argument(name, argument, field, visitor_context);
                        self.leave_field_argument(name, argument, field, visitor_context);
                    }

                    self.__visit_selection_set(&field.selection_set, visitor_context);
                    self.leave_field(field, visitor_context);
                }
                Selection::FragmentSpread(fragment_spread) => {
                    self.enter_fragment_spread(fragment_spread, visitor_context);
                    self.leave_fragment_spread(fragment_spread, visitor_context);
                }
                Selection::InlineFragment(inline_fragment) => {
                    self.enter_inline_fragment(inline_fragment, visitor_context);
                    self.__visit_selection_set(&inline_fragment.selection_set, visitor_context);
                    self.leave_inline_fragment(inline_fragment, visitor_context);
                }
            }

            self.leave_selection(selection, visitor_context);
        }

        self.leave_selection_set(_node, visitor_context);
    }

    fn enter_document(&self, _node: &Document, _visitor_context: &mut T) {}
    fn leave_document(&self, _node: &Document, _visitor_context: &mut T) {}

    fn enter_definition(&self, _node: &Definition, _visitor_context: &mut T) {}
    fn leave_definition(&self, _node: &Definition, _visitor_context: &mut T) {}

    fn enter_fragment_definition(&self, _node: &FragmentDefinition, _visitor_context: &mut T) {}
    fn leave_fragment_definition(&self, _node: &FragmentDefinition, _visitor_context: &mut T) {}

    fn enter_operation_definition(&self, _node: &OperationDefinition, _visitor_context: &mut T) {}
    fn leave_operation_definition(&self, _node: &OperationDefinition, _visitor_context: &mut T) {}

    fn enter_query(&self, _node: &Query, _visitor_context: &mut T) {}
    fn leave_query(&self, _node: &Query, _visitor_context: &mut T) {}

    fn enter_mutation(&self, _node: &Mutation, _visitor_context: &mut T) {}
    fn leave_mutation(&self, _node: &Mutation, _visitor_context: &mut T) {}

    fn enter_subscription(&self, _node: &Subscription, _visitor_context: &mut T) {}
    fn leave_subscription(&self, _node: &Subscription, _visitor_context: &mut T) {}

    fn enter_selection_set(&self, _node: &SelectionSet, _visitor_context: &mut T) {}
    fn leave_selection_set(&self, _node: &SelectionSet, _visitor_context: &mut T) {}

    fn enter_variable_definition(
        &self,
        _node: &VariableDefinition,
        _parent_operation: &OperationDefinition,
        _visitor_context: &T,
    ) {
    }
    fn leave_variable_definition(
        &self,
        _node: &VariableDefinition,
        _parent_operation: &OperationDefinition,
        _visitor_context: &T,
    ) {
    }

    fn enter_selection(&self, _node: &Selection, _visitor_context: &mut T) {}
    fn leave_selection(&self, _node: &Selection, _visitor_context: &mut T) {}

    fn enter_field(&self, _node: &Field, _visitor_context: &mut T) {}
    fn leave_field(&self, _node: &Field, _visitor_context: &mut T) {}

    fn enter_field_argument(
        &self,
        _name: &String,
        _value: &Value,
        _parent_field: &Field,
        _visitor_context: &T,
    ) {
    }
    fn leave_field_argument(
        &self,
        _name: &String,
        _value: &Value,
        _parent_field: &Field,
        _visitor_context: &T,
    ) {
    }

    fn enter_fragment_spread(&self, _node: &FragmentSpread, _visitor_context: &mut T) {}
    fn leave_fragment_spread(&self, _node: &FragmentSpread, _visitor_context: &mut T) {}

    fn enter_inline_fragment(&self, _node: &InlineFragment, _visitor_context: &mut T) {}
    fn leave_inline_fragment(&self, _node: &InlineFragment, _visitor_context: &mut T) {}
}

#[test]
fn visit_test_all_nodes() {
    use graphql_parser::query::parse_query;

    let query_ast = parse_query::<String>(
        r#"query someQuery($v: String) {
      hero(v: $v, otherV: 10) {
        name
      }

      test {
        ...SpreadHere

        anotherField {
          nested {
            moreNested
          }
        }
      }

      search(term: "Test") {
        ... on SearchResult {
          result
        }
      }
    }"#,
    )
    .expect("failed to parse query");

    struct TestVisitorCollected {
        collected_queries: Vec<String>,
    }

    struct TestVisitor;

    impl TestVisitor {
        fn collect_visited_info(&self, document: &Document, collector: &mut TestVisitorCollected) {
            self.visit_document(document, collector);
        }
    }

    impl QueryVisitor<TestVisitorCollected> for TestVisitor {
        fn enter_query(&self, _node: &Query, _ctx: &mut TestVisitorCollected) {
            _ctx.collected_queries
                .push(_node.name.as_ref().unwrap().to_string());
        }
    }

    let mut collector = TestVisitorCollected {
        collected_queries: Vec::new(),
    };

    let visitor = TestVisitor {};

    visitor.collect_visited_info(&query_ast, &mut collector);
    assert_eq!(collector.collected_queries, vec!["someQuery"]);
}