Skip to main content

graphql_query/validate/rules/
unique_variable_names.rs

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