graphql_query/validate/rules/
unique_operation_names.rs1use bumpalo::collections::Vec;
2
3use super::super::{ValidationContext, ValidationRule};
4use crate::{ast::*, visit::*};
5
6pub 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}