graphql_query/validate/rules/
no_unused_fragments.rs1use bumpalo::collections::Vec;
2
3use super::super::{ValidationContext, ValidationRule};
4use crate::{ast::*, visit::*};
5
6pub struct NoUnusedFragments<'a> {
11 fragment_names: Vec<'a, &'a str>,
12 fragment_spreads: Vec<'a, &'a str>,
13}
14
15impl<'a> DefaultIn<'a> for NoUnusedFragments<'a> {
16 fn default_in(arena: &'a bumpalo::Bump) -> Self {
17 Self {
18 fragment_names: Vec::new_in(arena),
19 fragment_spreads: Vec::new_in(arena),
20 }
21 }
22}
23
24impl<'a> ValidationRule<'a> for NoUnusedFragments<'a> {}
25
26impl<'a> Visitor<'a, ValidationContext<'a>> for NoUnusedFragments<'a> {
27 fn enter_fragment(
28 &mut self,
29 _ctx: &mut ValidationContext<'a>,
30 fragment: &'a FragmentDefinition<'a>,
31 _info: &VisitInfo,
32 ) -> VisitFlow {
33 self.fragment_names.push(fragment.name.name);
34 VisitFlow::Next
35 }
36
37 fn enter_fragment_spread(
38 &mut self,
39 _ctx: &mut ValidationContext<'a>,
40 spread: &'a FragmentSpread<'a>,
41 _info: &VisitInfo,
42 ) -> VisitFlow {
43 self.fragment_spreads.push(spread.name.name);
44 VisitFlow::Skip
45 }
46
47 fn leave_document(
48 &mut self,
49 ctx: &mut ValidationContext<'a>,
50 _document: &'a Document<'a>,
51 _info: &VisitInfo,
52 ) -> VisitFlow {
53 for name in self.fragment_names.iter() {
54 if !self.fragment_spreads.contains(name) {
55 ctx.add_error("All defined fragments must be at least spread once");
56 }
57 }
58 VisitFlow::Next
59 }
60
61 fn enter_variable_definition(
62 &mut self,
63 _ctx: &mut ValidationContext<'a>,
64 _var_def: &'a VariableDefinition,
65 _info: &VisitInfo,
66 ) -> VisitFlow {
67 VisitFlow::Skip
68 }
69
70 fn enter_argument(
71 &mut self,
72 _ctx: &mut ValidationContext<'a>,
73 _argument: &'a Argument,
74 _info: &VisitInfo,
75 ) -> VisitFlow {
76 VisitFlow::Skip
77 }
78
79 fn enter_directive(
80 &mut self,
81 _ctx: &mut ValidationContext<'a>,
82 _directive: &'a Directive,
83 _info: &VisitInfo,
84 ) -> VisitFlow {
85 VisitFlow::Skip
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn valid_spread() {
95 let ctx = ASTContext::new();
96 let document = Document::parse(
97 &ctx,
98 "query { ...Root } fragment Root on Query { __typename }",
99 )
100 .unwrap();
101 NoUnusedFragments::validate(&ctx, document).unwrap();
102 }
103
104 #[test]
105 fn missing_spread() {
106 let ctx = ASTContext::new();
107 let document = Document::parse(
108 &ctx,
109 "query { __typename } fragment Root on Query { __typename }",
110 )
111 .unwrap();
112 NoUnusedFragments::validate(&ctx, document).unwrap_err();
113 }
114}