graphql_query/validate/rules/
unique_argument_names.rs

1use super::super::{ValidationContext, ValidationRule};
2use crate::{ast::*, visit::*};
3
4/// Validates that no arguments anywhere contain duplicate names.
5///
6/// See [`ValidationRule`]
7/// [Reference](https://spec.graphql.org/October2021/#sec-Argument-Uniqueness)
8#[derive(Default)]
9pub struct UniqueArgumentNames<'a> {
10    used_argument_names: Vec<&'a str>,
11}
12
13impl<'a> ValidationRule<'a> for UniqueArgumentNames<'a> {}
14
15impl<'a> Visitor<'a, ValidationContext<'a>> for UniqueArgumentNames<'a> {
16    fn enter_field(
17        &mut self,
18        _ctx: &mut ValidationContext<'a>,
19        _field: &'a Field<'a>,
20        _info: &VisitInfo,
21    ) -> VisitFlow {
22        self.used_argument_names.clear();
23        VisitFlow::Next
24    }
25
26    fn enter_directive(
27        &mut self,
28        _ctx: &mut ValidationContext<'a>,
29        _directive: &'a Directive<'a>,
30        _info: &VisitInfo,
31    ) -> VisitFlow {
32        self.used_argument_names.clear();
33        VisitFlow::Next
34    }
35
36    fn enter_argument(
37        &mut self,
38        ctx: &mut ValidationContext<'a>,
39        argument: &'a Argument<'a>,
40        _info: &VisitInfo,
41    ) -> VisitFlow {
42        if self.used_argument_names.contains(&argument.name) {
43            ctx.add_error("All passed arguments per field must only be passed once");
44            VisitFlow::Break
45        } else {
46            self.used_argument_names.push(argument.name);
47            VisitFlow::Skip
48        }
49    }
50
51    fn enter_variable_definition(
52        &mut self,
53        _ctx: &mut ValidationContext<'a>,
54        _var_def: &'a VariableDefinition,
55        _info: &VisitInfo,
56    ) -> VisitFlow {
57        VisitFlow::Skip
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn valid_args() {
67        let ctx = ASTContext::new();
68        let document = Document::parse(&ctx, "{ test(field: 1) }").unwrap();
69        UniqueArgumentNames::validate(&ctx, document).unwrap();
70    }
71
72    #[test]
73    fn overlapping_args() {
74        let ctx = ASTContext::new();
75        let document = Document::parse(&ctx, "{ test(field: 1, field: 2) }").unwrap();
76        UniqueArgumentNames::validate(&ctx, document).unwrap_err();
77    }
78}