async_graphql/validation/rules/
unique_variable_names.rs

1use std::collections::HashSet;
2
3use crate::{
4    Name, Positioned,
5    parser::types::{OperationDefinition, VariableDefinition},
6    validation::visitor::{Visitor, VisitorContext},
7};
8
9#[derive(Default)]
10pub struct UniqueVariableNames<'a> {
11    names: HashSet<&'a str>,
12}
13
14impl<'a> Visitor<'a> for UniqueVariableNames<'a> {
15    fn enter_operation_definition(
16        &mut self,
17        _ctx: &mut VisitorContext<'a>,
18        _name: Option<&'a Name>,
19        _operation_definition: &'a Positioned<OperationDefinition>,
20    ) {
21        self.names.clear();
22    }
23
24    fn enter_variable_definition(
25        &mut self,
26        ctx: &mut VisitorContext<'a>,
27        variable_definition: &'a Positioned<VariableDefinition>,
28    ) {
29        if !self.names.insert(&variable_definition.node.name.node) {
30            ctx.report_error(
31                vec![variable_definition.pos],
32                format!(
33                    "There can only be one variable named \"${}\"",
34                    variable_definition.node.name.node
35                ),
36            );
37        }
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    pub fn factory<'a>() -> UniqueVariableNames<'a> {
46        UniqueVariableNames::default()
47    }
48
49    #[test]
50    fn unique_variable_names() {
51        expect_passes_rule!(
52            factory,
53            r#"
54          query A($x: Int, $y: String) { __typename }
55          query B($x: String, $y: Int) { __typename }
56        "#,
57        );
58    }
59
60    #[test]
61    fn duplicate_variable_names() {
62        expect_fails_rule!(
63            factory,
64            r#"
65          query A($x: Int, $x: Int, $x: String) { __typename }
66          query B($x: String, $x: Int) { __typename }
67          query C($x: Int, $x: Int) { __typename }
68        "#,
69        );
70    }
71}