async_graphql/validation/rules/
unique_variable_names.rs1use 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}