graphql_query/validate/rules/
unique_variable_names.rs

1use super::super::{ValidationContext, ValidationRule};
2use crate::{ast::*, visit::*};
3
4/// Validates that no operation the document defines has duplicate variable names in its variable
5/// definitions.
6///
7/// See [`ValidationRule`]
8/// [Reference](https://spec.graphql.org/October2021/#sec-Variable-Uniqueness)
9#[derive(Default)]
10pub struct UniqueVariableNames<'a> {
11    used_variable_names: Vec<&'a str>,
12}
13
14impl<'a> ValidationRule<'a> for UniqueVariableNames<'a> {}
15
16impl<'a> Visitor<'a, ValidationContext<'a>> for UniqueVariableNames<'a> {
17    fn enter_operation(
18        &mut self,
19        _ctx: &mut ValidationContext<'a>,
20        _operation: &'a OperationDefinition<'a>,
21        _info: &VisitInfo,
22    ) -> VisitFlow {
23        self.used_variable_names.clear();
24        VisitFlow::Next
25    }
26
27    fn enter_variable_definition(
28        &mut self,
29        ctx: &mut ValidationContext<'a>,
30        var_def: &'a VariableDefinition<'a>,
31        _info: &VisitInfo,
32    ) -> VisitFlow {
33        if self.used_variable_names.contains(&var_def.variable.name) {
34            ctx.add_error("All defined variables per operation must have unique names");
35            VisitFlow::Break
36        } else {
37            self.used_variable_names.push(var_def.variable.name);
38            VisitFlow::Skip
39        }
40    }
41
42    fn enter_selection_set(
43        &mut self,
44        _ctx: &mut ValidationContext<'a>,
45        _selection_set: &'a SelectionSet,
46        _info: &VisitInfo,
47    ) -> VisitFlow {
48        VisitFlow::Skip
49    }
50
51    fn enter_directive(
52        &mut self,
53        _ctx: &mut ValidationContext<'a>,
54        _fragment: &'a Directive,
55        _info: &VisitInfo,
56    ) -> VisitFlow {
57        VisitFlow::Skip
58    }
59
60    fn enter_fragment(
61        &mut self,
62        _ctx: &mut ValidationContext<'a>,
63        _fragment: &'a FragmentDefinition,
64        _info: &VisitInfo,
65    ) -> VisitFlow {
66        VisitFlow::Skip
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn valid_variables() {
76        let ctx = ASTContext::new();
77        let document = Document::parse(&ctx, "query ($a: Int, $b: Int) { __typename }").unwrap();
78        UniqueVariableNames::validate(&ctx, document).unwrap();
79    }
80
81    #[test]
82    fn overlapping_variables() {
83        let ctx = ASTContext::new();
84        let document = Document::parse(&ctx, "query ($a: Int, $a: Int) { __typename }").unwrap();
85        UniqueVariableNames::validate(&ctx, document).unwrap_err();
86    }
87}