Skip to main content

graphql_query/validate/rules/
unique_operation_names.rs

1use bumpalo::collections::Vec;
2
3use super::super::{ValidationContext, ValidationRule};
4use crate::{ast::*, visit::*};
5
6/// Validates that no operation the document defines have duplicate names.
7/// Note: Operations and Fragments are allowed to share names.
8///
9/// See [`ValidationRule`]
10/// [Reference](https://spec.graphql.org/October2021/#sec-Operation-Name-Uniqueness)
11pub struct UniqueOperationNames<'a> {
12    used_operation_names: Vec<'a, &'a str>,
13}
14
15impl<'a> DefaultIn<'a> for UniqueOperationNames<'a> {
16    fn default_in(arena: &'a bumpalo::Bump) -> Self {
17        Self {
18            used_operation_names: Vec::new_in(arena),
19        }
20    }
21}
22
23impl<'a> ValidationRule<'a> for UniqueOperationNames<'a> {}
24
25impl<'a> Visitor<'a, ValidationContext<'a>> for UniqueOperationNames<'a> {
26    fn enter_operation(
27        &mut self,
28        ctx: &mut ValidationContext<'a>,
29        operation: &'a OperationDefinition<'a>,
30        _info: &VisitInfo,
31    ) -> VisitFlow {
32        if let Some(name) = operation.name {
33            if self.used_operation_names.contains(&name.name) {
34                ctx.add_error("All defined operations must have unique names");
35                VisitFlow::Break
36            } else {
37                self.used_operation_names.push(name.name);
38                VisitFlow::Skip
39            }
40        } else {
41            VisitFlow::Skip
42        }
43    }
44
45    fn enter_fragment(
46        &mut self,
47        _ctx: &mut ValidationContext<'a>,
48        _fragment: &'a FragmentDefinition,
49        _info: &VisitInfo,
50    ) -> VisitFlow {
51        VisitFlow::Skip
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn valid_operation_names() {
61        let ctx = ASTContext::new();
62        let document = Document::parse(&ctx, "query Root { __typename }").unwrap();
63        UniqueOperationNames::validate(&ctx, document).unwrap();
64    }
65
66    #[test]
67    fn overlapping_fragment_names() {
68        let ctx = ASTContext::new();
69        let document = Document::parse(
70            &ctx,
71            "query Root { __typename } mutation Root { __typename }",
72        )
73        .unwrap();
74        UniqueOperationNames::validate(&ctx, document).unwrap_err();
75    }
76}