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::{OperationVisitor, OperationVisitorContext};
8use crate::static_graphql::query::*;
9use crate::validation::utils::{ValidationError, ValidationErrorContext};
10
11#[derive(Default)]
17pub struct UniqueVariableNames<'doc> {
18 found_records: HashMap<&'doc str, Pos>,
19}
20
21impl UniqueVariableNames<'_> {
22 pub fn new() -> Self {
23 UniqueVariableNames {
24 found_records: HashMap::new(),
25 }
26 }
27}
28
29impl<'doc> OperationVisitor<'doc, ValidationErrorContext> for UniqueVariableNames<'doc> {
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: &'doc 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 ValidationRule for UniqueVariableNames<'_> {
63 fn error_code(&self) -> &'static str {
64 "UniqueVariableNames"
65 }
66
67 fn visitor<'doc>(&self) -> super::ValidationVisitor<'doc> {
68 Box::new(UniqueVariableNames::new())
69 }
70}
71
72#[test]
73fn unique_variable_names() {
74 use crate::validation::test_utils::*;
75
76 let mut plan = create_plan_from_rule(Box::new(UniqueVariableNames::new()));
77 let errors = test_operation_with_schema(
78 "query A($x: Int, $y: String) { __typename }
79 query B($x: String, $y: Int) { __typename }",
80 TEST_SCHEMA,
81 &mut plan,
82 );
83
84 assert_eq!(get_messages(&errors).len(), 0);
85}
86
87#[test]
88fn duplicate_variable_names() {
89 use crate::validation::test_utils::*;
90
91 let mut plan = create_plan_from_rule(Box::new(UniqueVariableNames::new()));
92 let errors = test_operation_with_schema(
93 "query A($x: Int, $x: Int, $x: String) { __typename }
94 query B($y: String, $y: Int) { __typename }
95 query C($z: Int, $z: Int) { __typename }",
96 TEST_SCHEMA,
97 &mut plan,
98 );
99
100 let messages = get_messages(&errors);
101
102 assert_eq!(messages.len(), 4);
103 assert!(messages.contains(&&"There can only be one variable named \"$x\".".to_owned()));
104 assert!(messages.contains(&&"There can only be one variable named \"$y\".".to_owned()));
105 assert!(messages.contains(&&"There can only be one variable named \"$z\".".to_owned()));
106}