graphql_query/validate/rules/
unique_fragment_names.rs1use bumpalo::collections::Vec;
2
3use super::super::{ValidationContext, ValidationRule};
4use crate::{ast::*, visit::*};
5
6pub struct UniqueFragmentNames<'a> {
12 used_fragment_names: Vec<'a, &'a str>,
13}
14
15impl<'a> DefaultIn<'a> for UniqueFragmentNames<'a> {
16 fn default_in(arena: &'a bumpalo::Bump) -> Self {
17 Self {
18 used_fragment_names: Vec::new_in(arena),
19 }
20 }
21}
22
23impl<'a> ValidationRule<'a> for UniqueFragmentNames<'a> {}
24
25impl<'a> Visitor<'a, ValidationContext<'a>> for UniqueFragmentNames<'a> {
26 fn enter_fragment(
27 &mut self,
28 ctx: &mut ValidationContext<'a>,
29 fragment: &'a FragmentDefinition<'a>,
30 _info: &VisitInfo,
31 ) -> VisitFlow {
32 if self.used_fragment_names.contains(&fragment.name.name) {
33 ctx.add_error("All defined fragments must have unique names");
34 VisitFlow::Break
35 } else {
36 self.used_fragment_names.push(fragment.name.name);
37 VisitFlow::Skip
38 }
39 }
40
41 fn enter_operation(
42 &mut self,
43 _ctx: &mut ValidationContext<'a>,
44 _operation: &'a OperationDefinition,
45 _info: &VisitInfo,
46 ) -> VisitFlow {
47 VisitFlow::Skip
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 #[test]
56 fn valid_fragment_names() {
57 let ctx = ASTContext::new();
58 let document = Document::parse(&ctx, "fragment Root on Query { __typename }").unwrap();
59 UniqueFragmentNames::validate(&ctx, document).unwrap();
60 }
61
62 #[test]
63 fn overlapping_fragment_names() {
64 let ctx = ASTContext::new();
65 let document = Document::parse(
66 &ctx,
67 "fragment Root on Query { __typename } fragment Root on Item { __typename }",
68 )
69 .unwrap();
70 UniqueFragmentNames::validate(&ctx, document).unwrap_err();
71 }
72}