1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
use std::collections::hash_map::{Entry, HashMap}; use crate::{ ast::{Operation, VariableDefinition}, parser::{SourcePosition, Spanning}, validation::{ValidatorContext, Visitor}, value::ScalarValue, }; pub struct UniqueVariableNames<'a> { names: HashMap<&'a str, SourcePosition>, } pub fn factory<'a>() -> UniqueVariableNames<'a> { UniqueVariableNames { names: HashMap::new(), } } impl<'a, S> Visitor<'a, S> for UniqueVariableNames<'a> where S: ScalarValue, { fn enter_operation_definition( &mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning<Operation<S>>, ) { self.names = HashMap::new(); } fn enter_variable_definition( &mut self, ctx: &mut ValidatorContext<'a, S>, &(ref var_name, _): &'a (Spanning<&'a str>, VariableDefinition<S>), ) { match self.names.entry(var_name.item) { Entry::Occupied(e) => { ctx.report_error(&error_message(var_name.item), &[*e.get(), var_name.start]); } Entry::Vacant(e) => { e.insert(var_name.start); } } } } fn error_message(var_name: &str) -> String { format!("There can only be one variable named {}", var_name) } #[cfg(test)] mod tests { use super::{error_message, factory}; use crate::{ parser::SourcePosition, validation::{expect_fails_rule, expect_passes_rule, RuleError}, value::DefaultScalarValue, }; #[test] fn unique_variable_names() { expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" query A($x: Int, $y: String) { __typename } query B($x: String, $y: Int) { __typename } "#, ); } #[test] fn duplicate_variable_names() { expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" query A($x: Int, $x: Int, $x: String) { __typename } query B($x: String, $x: Int) { __typename } query C($x: Int, $x: Int) { __typename } "#, &[ RuleError::new( &error_message("x"), &[ SourcePosition::new(19, 1, 18), SourcePosition::new(28, 1, 27), ], ), RuleError::new( &error_message("x"), &[ SourcePosition::new(19, 1, 18), SourcePosition::new(37, 1, 36), ], ), RuleError::new( &error_message("x"), &[ SourcePosition::new(82, 2, 18), SourcePosition::new(94, 2, 30), ], ), RuleError::new( &error_message("x"), &[ SourcePosition::new(136, 3, 18), SourcePosition::new(145, 3, 27), ], ), ], ); } }