graphql_query/validate/rules/
lone_anonymous_operation.rs1use super::super::context::ValidationContext;
2use super::super::validate::ValidationRule;
3use crate::{ast::*, visit::*};
4
5#[derive(Default)]
10pub struct LoneAnonymousOperation {
11 operations: usize,
12 has_anonymous: bool,
13}
14
15impl<'a> ValidationRule<'a> for LoneAnonymousOperation {}
16
17impl<'a> Visitor<'a, ValidationContext<'a>> for LoneAnonymousOperation {
18 fn enter_fragment(
19 &mut self,
20 _ctx: &mut ValidationContext<'a>,
21 _operation: &'a FragmentDefinition<'a>,
22 _info: &VisitInfo,
23 ) -> VisitFlow {
24 VisitFlow::Skip
25 }
26
27 fn enter_operation(
28 &mut self,
29 _ctx: &mut ValidationContext<'a>,
30 operation: &'a OperationDefinition<'a>,
31 _info: &VisitInfo,
32 ) -> VisitFlow {
33 self.has_anonymous |= operation.name.is_none();
34 self.operations += 1;
35 VisitFlow::Skip
36 }
37
38 fn leave_document(
39 &mut self,
40 ctx: &mut ValidationContext<'a>,
41 _document: &'a Document<'a>,
42 _info: &VisitInfo,
43 ) -> VisitFlow {
44 if self.has_anonymous && self.operations > 1 {
45 ctx.add_error("Anonymous operation must be the only defined operation.");
46 }
47 VisitFlow::Next
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 #[test]
56 fn lone_operation() {
57 let ctx = ASTContext::new();
58 let document = Document::parse(&ctx, "query { __typename }").unwrap();
59 LoneAnonymousOperation::validate(&ctx, document).unwrap();
60 }
61
62 #[test]
63 fn two_named() {
64 let ctx = ASTContext::new();
65 let document =
66 Document::parse(&ctx, "query A { __typename } query B { __typename }").unwrap();
67 LoneAnonymousOperation::validate(&ctx, document).unwrap();
68 }
69
70 #[test]
71 fn two_anonymous() {
72 let ctx = ASTContext::new();
73 let document = Document::parse(&ctx, "{ __typename } { __typename }").unwrap();
74 LoneAnonymousOperation::validate(&ctx, document).unwrap_err();
75 }
76}