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),
                    ],
                ),
            ],
        );
    }
}