Skip to main content

graphql_query/validate/rules/
unique_argument_names.rs

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