Skip to main content

graphql_query/validate/rules/
unique_fragment_names.rs

1use bumpalo::collections::Vec;
2
3use super::super::{ValidationContext, ValidationRule};
4use crate::{ast::*, visit::*};
5
6/// Validates that no fragments 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-Fragment-Name-Uniqueness)
11pub 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}