graphql_tools/ast/
operation_transformer.rs

1use crate::parser::query::*;
2
3#[derive(Clone, Debug)]
4pub enum Transformed<T> {
5    Keep,
6    Replace(T),
7}
8
9#[derive(Clone, Debug)]
10pub enum TransformedValue<T> {
11    Keep,
12    Replace(T),
13}
14
15impl<T> TransformedValue<T> {
16    pub fn should_keep(&self) -> bool {
17        match self {
18            TransformedValue::Keep => true,
19            TransformedValue::Replace(_) => false,
20        }
21    }
22
23    pub fn replace_or_else<F>(self, f: F) -> T
24    where
25        F: FnOnce() -> T,
26    {
27        match self {
28            TransformedValue::Keep => f(),
29            TransformedValue::Replace(next_value) => next_value,
30        }
31    }
32}
33
34impl<T> From<TransformedValue<T>> for Transformed<T> {
35    fn from(val: TransformedValue<T>) -> Self {
36        match val {
37            TransformedValue::Keep => Transformed::Keep,
38            TransformedValue::Replace(replacement) => Transformed::Replace(replacement),
39        }
40    }
41}
42
43pub trait OperationTransformer<'a, T: Text<'a> + Clone> {
44    fn transform_document(
45        &mut self,
46        document: &Document<'a, T>,
47    ) -> TransformedValue<Document<'a, T>> {
48        self.default_transform_document(document)
49    }
50
51    fn default_transform_document(
52        &mut self,
53        document: &Document<'a, T>,
54    ) -> TransformedValue<Document<'a, T>> {
55        let mut next_document = Document {
56            definitions: Vec::new(),
57        };
58        let mut has_changes = false;
59        for definition in document.definitions.clone() {
60            match self.transform_definition(&definition) {
61                Transformed::Keep => next_document.definitions.push(definition),
62                Transformed::Replace(replacement) => {
63                    has_changes = true;
64                    next_document.definitions.push(replacement)
65                }
66            }
67        }
68        if has_changes {
69            TransformedValue::Replace(next_document)
70        } else {
71            TransformedValue::Keep
72        }
73    }
74
75    fn transform_definition(
76        &mut self,
77        definition: &Definition<'a, T>,
78    ) -> Transformed<Definition<'a, T>> {
79        self.default_transform_definition(definition)
80    }
81
82    fn default_transform_definition(
83        &mut self,
84        definition: &Definition<'a, T>,
85    ) -> Transformed<Definition<'a, T>> {
86        match definition {
87            Definition::Operation(operation) => match self.transform_operation(operation) {
88                Transformed::Keep => Transformed::Keep,
89                Transformed::Replace(replacement) => {
90                    Transformed::Replace(Definition::Operation(replacement))
91                }
92            },
93            Definition::Fragment(fragment) => match self.transform_fragment(fragment) {
94                Transformed::Keep => Transformed::Keep,
95                Transformed::Replace(replacement) => {
96                    Transformed::Replace(Definition::Fragment(replacement))
97                }
98            },
99        }
100    }
101
102    fn transform_operation(
103        &mut self,
104        operation: &OperationDefinition<'a, T>,
105    ) -> Transformed<OperationDefinition<'a, T>> {
106        self.default_transform_operation(operation)
107    }
108
109    fn default_transform_operation(
110        &mut self,
111        operation: &OperationDefinition<'a, T>,
112    ) -> Transformed<OperationDefinition<'a, T>> {
113        match operation {
114            OperationDefinition::Query(query) => match self.transform_query(query) {
115                Transformed::Keep => Transformed::Keep,
116                Transformed::Replace(replacement) => {
117                    Transformed::Replace(OperationDefinition::Query(replacement))
118                }
119            },
120            OperationDefinition::Mutation(mutation) => match self.transform_mutation(mutation) {
121                Transformed::Keep => Transformed::Keep,
122                Transformed::Replace(replacement) => {
123                    Transformed::Replace(OperationDefinition::Mutation(replacement))
124                }
125            },
126            OperationDefinition::Subscription(subscription) => {
127                match self.transform_subscription(subscription) {
128                    Transformed::Keep => Transformed::Keep,
129                    Transformed::Replace(replacement) => {
130                        Transformed::Replace(OperationDefinition::Subscription(replacement))
131                    }
132                }
133            }
134            OperationDefinition::SelectionSet(selection_set) => {
135                let items = self.transform_selection_set(selection_set);
136
137                if items.should_keep() {
138                    return Transformed::Keep;
139                }
140
141                Transformed::Replace(OperationDefinition::SelectionSet(SelectionSet {
142                    items: items.replace_or_else(|| selection_set.items.clone()),
143                    span: selection_set.span,
144                }))
145            }
146        }
147    }
148
149    fn transform_query(&mut self, node: &Query<'a, T>) -> Transformed<Query<'a, T>> {
150        self.default_transform_query(node)
151    }
152
153    fn default_transform_query(&mut self, node: &Query<'a, T>) -> Transformed<Query<'a, T>> {
154        let selections = self.transform_selection_set(&node.selection_set);
155        let directives = self.transform_directives(&node.directives);
156        let variable_definitions = self.transform_variable_definitions(&node.variable_definitions);
157
158        if selections.should_keep()
159            && directives.should_keep()
160            && variable_definitions.should_keep()
161        {
162            return Transformed::Keep;
163        }
164
165        Transformed::Replace(Query {
166            directives: directives.replace_or_else(|| node.directives.clone()),
167            selection_set: SelectionSet {
168                items: selections.replace_or_else(|| node.selection_set.items.clone()),
169                span: node.selection_set.span,
170            },
171            variable_definitions: variable_definitions
172                .replace_or_else(|| node.variable_definitions.clone()),
173            position: node.position,
174            name: node.name.clone(),
175        })
176    }
177
178    fn transform_mutation(&mut self, node: &Mutation<'a, T>) -> Transformed<Mutation<'a, T>> {
179        self.default_transform_mutation(node)
180    }
181
182    fn default_transform_mutation(
183        &mut self,
184        node: &Mutation<'a, T>,
185    ) -> Transformed<Mutation<'a, T>> {
186        let selections = self.transform_selection_set(&node.selection_set);
187        let directives = self.transform_directives(&node.directives);
188        let variable_definitions = self.transform_variable_definitions(&node.variable_definitions);
189
190        if selections.should_keep()
191            && directives.should_keep()
192            && variable_definitions.should_keep()
193        {
194            return Transformed::Keep;
195        }
196
197        Transformed::Replace(Mutation {
198            directives: directives.replace_or_else(|| node.directives.clone()),
199            selection_set: SelectionSet {
200                items: selections.replace_or_else(|| node.selection_set.items.clone()),
201                span: node.selection_set.span,
202            },
203            variable_definitions: variable_definitions
204                .replace_or_else(|| node.variable_definitions.clone()),
205            position: node.position,
206            name: node.name.clone(),
207        })
208    }
209
210    fn transform_subscription(
211        &mut self,
212        node: &Subscription<'a, T>,
213    ) -> Transformed<Subscription<'a, T>> {
214        self.default_transform_subscription(node)
215    }
216
217    fn default_transform_subscription(
218        &mut self,
219        node: &Subscription<'a, T>,
220    ) -> Transformed<Subscription<'a, T>> {
221        let selections = self.transform_selection_set(&node.selection_set);
222        let directives = self.transform_directives(&node.directives);
223        let variable_definitions = self.transform_variable_definitions(&node.variable_definitions);
224
225        if selections.should_keep()
226            && directives.should_keep()
227            && variable_definitions.should_keep()
228        {
229            return Transformed::Keep;
230        }
231
232        Transformed::Replace(Subscription {
233            directives: directives.replace_or_else(|| node.directives.clone()),
234            selection_set: SelectionSet {
235                items: selections.replace_or_else(|| node.selection_set.items.clone()),
236                span: node.selection_set.span,
237            },
238            variable_definitions: variable_definitions
239                .replace_or_else(|| node.variable_definitions.clone()),
240            position: node.position,
241            name: node.name.clone(),
242        })
243    }
244
245    fn transform_fragment(
246        &mut self,
247        fragment: &FragmentDefinition<'a, T>,
248    ) -> Transformed<FragmentDefinition<'a, T>> {
249        self.default_transform_fragment(fragment)
250    }
251
252    fn default_transform_fragment(
253        &mut self,
254        fragment: &FragmentDefinition<'a, T>,
255    ) -> Transformed<FragmentDefinition<'a, T>> {
256        let selections = self.transform_selection_set(&fragment.selection_set);
257        let directives = self.transform_directives(&fragment.directives);
258
259        if selections.should_keep() && directives.should_keep() {
260            return Transformed::Keep;
261        }
262
263        Transformed::Replace(FragmentDefinition {
264            directives: directives.replace_or_else(|| fragment.directives.clone()),
265            selection_set: SelectionSet {
266                items: selections.replace_or_else(|| fragment.selection_set.items.clone()),
267                span: fragment.selection_set.span,
268            },
269            position: fragment.position,
270            name: fragment.name.clone(),
271            type_condition: fragment.type_condition.clone(),
272        })
273    }
274
275    fn transform_selection_set(
276        &mut self,
277        selections: &SelectionSet<'a, T>,
278    ) -> TransformedValue<Vec<Selection<'a, T>>> {
279        self.transform_list(&selections.items, Self::transform_selection)
280    }
281
282    fn transform_selection(
283        &mut self,
284        selection: &Selection<'a, T>,
285    ) -> Transformed<Selection<'a, T>> {
286        self.default_transform_selection(selection)
287    }
288
289    fn default_transform_selection(
290        &mut self,
291        selection: &Selection<'a, T>,
292    ) -> Transformed<Selection<'a, T>> {
293        match selection {
294            Selection::FragmentSpread(selection) => self.transform_fragment_spread(selection),
295            Selection::InlineFragment(selection) => self.transform_inline_fragment(selection),
296            Selection::Field(field) => self.transform_field(field),
297        }
298    }
299
300    fn transform_field(&mut self, field: &Field<'a, T>) -> Transformed<Selection<'a, T>> {
301        self.default_transform_field(field)
302    }
303
304    fn default_transform_field(&mut self, field: &Field<'a, T>) -> Transformed<Selection<'a, T>> {
305        let selection_set = self.transform_selection_set(&field.selection_set);
306        let arguments = self.transform_arguments(&field.arguments);
307        let directives = self.transform_directives(&field.directives);
308        if selection_set.should_keep() && arguments.should_keep() && directives.should_keep() {
309            return Transformed::Keep;
310        }
311        Transformed::Replace(Selection::Field(Field {
312            arguments: arguments.replace_or_else(|| field.arguments.clone()),
313            directives: directives.replace_or_else(|| field.directives.clone()),
314            selection_set: SelectionSet {
315                items: selection_set.replace_or_else(|| field.selection_set.items.clone()),
316                span: field.selection_set.span,
317            },
318            position: field.position,
319            alias: field.alias.clone(),
320            name: field.name.clone(),
321        }))
322    }
323
324    fn transform_fragment_spread(
325        &mut self,
326        spread: &FragmentSpread<'a, T>,
327    ) -> Transformed<Selection<'a, T>> {
328        self.default_transform_fragment_spread(spread)
329    }
330    fn default_transform_fragment_spread(
331        &mut self,
332        spread: &FragmentSpread<'a, T>,
333    ) -> Transformed<Selection<'a, T>> {
334        let directives = self.transform_directives(&spread.directives);
335        Transformed::Replace(Selection::FragmentSpread(FragmentSpread {
336            directives: directives.replace_or_else(|| spread.directives.clone()),
337            position: spread.position,
338            fragment_name: spread.fragment_name.clone(),
339        }))
340    }
341
342    fn transform_inline_fragment(
343        &mut self,
344        fragment: &InlineFragment<'a, T>,
345    ) -> Transformed<Selection<'a, T>> {
346        self.default_transform_inline_fragment(fragment)
347    }
348
349    fn default_transform_inline_fragment(
350        &mut self,
351        fragment: &InlineFragment<'a, T>,
352    ) -> Transformed<Selection<'a, T>> {
353        let selections = self.transform_selection_set(&fragment.selection_set);
354        let directives = self.transform_directives(&fragment.directives);
355
356        if selections.should_keep() && directives.should_keep() {
357            return Transformed::Keep;
358        }
359
360        Transformed::Replace(Selection::InlineFragment(InlineFragment {
361            position: fragment.position,
362            type_condition: fragment.type_condition.clone(),
363            directives: directives.replace_or_else(|| fragment.directives.clone()),
364            selection_set: SelectionSet {
365                span: fragment.selection_set.span,
366                items: selections.replace_or_else(|| fragment.selection_set.items.clone()),
367            },
368        }))
369    }
370
371    fn transform_directives(
372        &mut self,
373        directives: &[Directive<'a, T>],
374    ) -> TransformedValue<Vec<Directive<'a, T>>> {
375        self.transform_list(directives, Self::transform_directive)
376    }
377
378    fn transform_directive(
379        &mut self,
380        directive: &Directive<'a, T>,
381    ) -> Transformed<Directive<'a, T>> {
382        self.default_transform_directive(directive)
383    }
384
385    fn default_transform_directive(
386        &mut self,
387        directive: &Directive<'a, T>,
388    ) -> Transformed<Directive<'a, T>> {
389        let arguments = self.transform_arguments(&directive.arguments);
390        match arguments {
391            TransformedValue::Keep => Transformed::Keep,
392            TransformedValue::Replace(replacement) => Transformed::Replace(Directive {
393                position: directive.position,
394                name: directive.name.clone(),
395                arguments: replacement,
396            }),
397        }
398    }
399
400    fn transform_arguments(
401        &mut self,
402        arguments: &[(T::Value, Value<'a, T>)],
403    ) -> TransformedValue<Vec<(T::Value, Value<'a, T>)>> {
404        self.transform_list(arguments, Self::transform_argument)
405    }
406
407    fn transform_argument(
408        &mut self,
409        argument: &(T::Value, Value<'a, T>),
410    ) -> Transformed<(T::Value, Value<'a, T>)> {
411        self.default_transform_argument(argument)
412    }
413
414    fn default_transform_argument(
415        &mut self,
416        argument: &(T::Value, Value<'a, T>),
417    ) -> Transformed<(T::Value, Value<'a, T>)> {
418        let (name, value) = argument;
419
420        match self.transform_value(value) {
421            TransformedValue::Keep => Transformed::Keep,
422            TransformedValue::Replace(replacement) => {
423                Transformed::Replace((name.clone(), replacement))
424            }
425        }
426    }
427
428    fn transform_value(&mut self, value: &Value<'a, T>) -> TransformedValue<Value<'a, T>> {
429        self.default_transform_value(value)
430    }
431
432    fn default_transform_value(&mut self, value: &Value<'a, T>) -> TransformedValue<Value<'a, T>> {
433        match value {
434            Value::Variable(_) => TransformedValue::Keep,
435            Value::List(_) => TransformedValue::Keep,
436            Value::Object(_) => TransformedValue::Keep,
437            Value::Null => TransformedValue::Keep,
438            Value::Boolean(_) => TransformedValue::Keep,
439            Value::Enum(_) => TransformedValue::Keep,
440            Value::Int(_) => TransformedValue::Keep,
441            Value::Float(_) => TransformedValue::Keep,
442            Value::String(_) => TransformedValue::Keep,
443        }
444    }
445
446    fn transform_variable_definitions(
447        &mut self,
448        variable_definitions: &Vec<VariableDefinition<'a, T>>,
449    ) -> TransformedValue<Vec<VariableDefinition<'a, T>>> {
450        self.default_transform_variable_definitions(variable_definitions)
451    }
452
453    fn default_transform_variable_definitions(
454        &mut self,
455        variable_definitions: &[VariableDefinition<'a, T>],
456    ) -> TransformedValue<Vec<VariableDefinition<'a, T>>> {
457        self.transform_list(
458            variable_definitions,
459            Self::default_transform_variable_definition,
460        )
461    }
462
463    fn transform_variable_definition(
464        &mut self,
465        variable_definition: &VariableDefinition<'a, T>,
466    ) -> TransformedValue<VariableDefinition<'a, T>> {
467        self.default_transform_variable_definition(variable_definition)
468    }
469
470    fn default_transform_variable_definition(
471        &mut self,
472        variable_definition: &VariableDefinition<'a, T>,
473    ) -> TransformedValue<VariableDefinition<'a, T>> {
474        if let Some(value) = variable_definition.default_value.clone() {
475            let transformed_default_value = self.transform_value(&value);
476
477            if transformed_default_value.should_keep() {
478                return TransformedValue::Keep;
479            } else {
480                return TransformedValue::Replace(VariableDefinition {
481                    position: variable_definition.position,
482                    name: variable_definition.name.clone(),
483                    var_type: variable_definition.var_type.clone(),
484                    default_value: Some(transformed_default_value.replace_or_else(|| value)),
485                });
486            }
487        }
488
489        TransformedValue::Keep
490    }
491
492    fn transform_list<I, F, R>(&mut self, list: &[I], f: F) -> TransformedValue<Vec<I>>
493    where
494        I: Clone,
495        F: Fn(&mut Self, &I) -> R,
496        R: Into<Transformed<I>>,
497    {
498        let mut result = Vec::new();
499        let mut has_changes = false;
500        for (index, prev_item) in list.iter().enumerate() {
501            let next_item: Transformed<_> = f(self, prev_item).into();
502            match next_item {
503                Transformed::Keep => {
504                    if has_changes {
505                        result.push(prev_item.clone());
506                    }
507                }
508                Transformed::Replace(next_item) => {
509                    if !has_changes {
510                        debug_assert!(result.capacity() == 0);
511                        result.reserve(list.len());
512                        result.extend(list.iter().take(index).cloned());
513                    }
514                    result.push(next_item);
515                    has_changes = true;
516                }
517            }
518        }
519        if has_changes {
520            TransformedValue::Replace(result)
521        } else {
522            TransformedValue::Keep
523        }
524    }
525}
526
527#[cfg(test)]
528mod tests {
529    #[test]
530    fn transform_fields_and_values() {
531        use super::{
532            parse_query, Field, Number, OperationTransformer, Selection, SelectionSet, Text,
533            Transformed, TransformedValue, Value,
534        };
535
536        let raw = parse_query(
537            r#"
538            query example {
539                foo(bar: "baz")
540            }
541        "#,
542        )
543        .expect("Failed to parse query")
544        .into_static();
545
546        struct RemoveLiteralsTransformer {}
547
548        impl<'a, T: Text<'a> + Clone> OperationTransformer<'a, T> for RemoveLiteralsTransformer {
549            fn transform_value(&mut self, node: &Value<'a, T>) -> TransformedValue<Value<'a, T>> {
550                match node {
551                    Value::Float(_) => TransformedValue::Replace(Value::Float(0.0)),
552                    Value::Int(_) => TransformedValue::Replace(Value::Int(Number::from(0))),
553                    Value::String(_) => TransformedValue::Replace(Value::String(String::from(""))),
554                    Value::Variable(_) => TransformedValue::Keep,
555                    Value::Boolean(_) => TransformedValue::Keep,
556                    Value::Null => TransformedValue::Keep,
557                    Value::Enum(_) => TransformedValue::Keep,
558                    Value::List(val) => {
559                        let items: Vec<Value<'a, T>> = val
560                            .iter()
561                            .map(|item| self.transform_value(item).replace_or_else(|| item.clone()))
562                            .collect();
563
564                        TransformedValue::Replace(Value::List(items))
565                    }
566                    Value::Object(fields) => {
567                        let fields: std::collections::BTreeMap<T::Value, Value<'a, T>> = fields
568                            .iter()
569                            .map(|field| {
570                                let (name, value) = field;
571                                let new_value = self
572                                    .transform_value(value)
573                                    .replace_or_else(|| value.clone());
574                                (name.clone(), new_value)
575                            })
576                            .collect();
577
578                        TransformedValue::Replace(Value::Object(fields))
579                    }
580                }
581            }
582
583            fn transform_field(
584                &mut self,
585                field: &crate::parser::query::Field<'a, T>,
586            ) -> Transformed<crate::parser::query::Selection<'a, T>> {
587                let selection_set = self.transform_selection_set(&field.selection_set);
588                let arguments = self.transform_arguments(&field.arguments);
589                let directives = self.transform_directives(&field.directives);
590
591                Transformed::Replace(Selection::Field(Field {
592                    arguments: arguments.replace_or_else(|| field.arguments.clone()),
593                    directives: directives.replace_or_else(|| field.directives.clone()),
594                    selection_set: SelectionSet {
595                        items: selection_set.replace_or_else(|| field.selection_set.items.clone()),
596                        span: field.selection_set.span,
597                    },
598                    position: field.position,
599                    alias: None,
600                    name: field.name.clone(),
601                }))
602            }
603        }
604
605        let mut transformer = RemoveLiteralsTransformer {};
606        let transformed = transformer
607            .transform_document(&raw)
608            .replace_or_else(|| raw.clone());
609
610        assert_eq!(
611            format!("{transformed}"),
612            "query example {\n  foo(bar: \"\")\n}\n".to_string()
613        );
614    }
615}