graphql_tools/validation/rules/
unique_variable_names.rs1use std::collections::hash_map::Entry;
2use std::collections::HashMap;
3
4use crate::parser::Pos;
5
6use super::ValidationRule;
7use crate::ast::{visit_document, OperationVisitor, OperationVisitorContext};
8use crate::static_graphql::query::*;
9use crate::validation::utils::{ValidationError, ValidationErrorContext};
10
11#[derive(Default)]
17pub struct UniqueVariableNames<'a> {
18 found_records: HashMap<&'a str, Pos>,
19}
20
21impl<'a> UniqueVariableNames<'a> {
22 pub fn new() -> Self {
23 UniqueVariableNames {
24 found_records: HashMap::new(),
25 }
26 }
27}
28
29impl<'a> OperationVisitor<'a, ValidationErrorContext> for UniqueVariableNames<'a> {
30 fn enter_operation_definition(
31 &mut self,
32 _: &mut OperationVisitorContext,
33 _: &mut ValidationErrorContext,
34 _operation_definition: &OperationDefinition,
35 ) {
36 self.found_records.clear();
37 }
38
39 fn enter_variable_definition(
40 &mut self,
41 _: &mut OperationVisitorContext,
42 user_context: &mut ValidationErrorContext,
43 variable_definition: &'a VariableDefinition,
44 ) {
45 let error_code = self.error_code();
46 match self.found_records.entry(&variable_definition.name) {
47 Entry::Occupied(entry) => user_context.report_error(ValidationError {
48 error_code,
49 locations: vec![*entry.get(), variable_definition.position],
50 message: format!(
51 "There can only be one variable named \"${}\".",
52 variable_definition.name
53 ),
54 }),
55 Entry::Vacant(entry) => {
56 entry.insert(variable_definition.position);
57 }
58 };
59 }
60}
61
62impl<'v> ValidationRule for UniqueVariableNames<'v> {
63 fn error_code<'a>(&self) -> &'a str {
64 "UniqueVariableNames"
65 }
66
67 fn validate(
68 &self,
69 ctx: &mut OperationVisitorContext,
70 error_collector: &mut ValidationErrorContext,
71 ) {
72 visit_document(
73 &mut UniqueVariableNames::new(),
74 ctx.operation,
75 ctx,
76 error_collector,
77 );
78 }
79}
80
81#[test]
82fn unique_variable_names() {
83 use crate::validation::test_utils::*;
84
85 let mut plan = create_plan_from_rule(Box::new(UniqueVariableNames::new()));
86 let errors = test_operation_with_schema(
87 "query A($x: Int, $y: String) { __typename }
88 query B($x: String, $y: Int) { __typename }",
89 TEST_SCHEMA,
90 &mut plan,
91 );
92
93 assert_eq!(get_messages(&errors).len(), 0);
94}
95
96#[test]
97fn duplicate_variable_names() {
98 use crate::validation::test_utils::*;
99
100 let mut plan = create_plan_from_rule(Box::new(UniqueVariableNames::new()));
101 let errors = test_operation_with_schema(
102 "query A($x: Int, $x: Int, $x: String) { __typename }
103 query B($y: String, $y: Int) { __typename }
104 query C($z: Int, $z: Int) { __typename }",
105 TEST_SCHEMA,
106 &mut plan,
107 );
108
109 let messages = get_messages(&errors);
110
111 assert_eq!(messages.len(), 4);
112 assert!(messages.contains(&&"There can only be one variable named \"$x\".".to_owned()));
113 assert!(messages.contains(&&"There can only be one variable named \"$y\".".to_owned()));
114 assert!(messages.contains(&&"There can only be one variable named \"$z\".".to_owned()));
115}