graphql_query/validate/rules/
lone_anonymous_operation.rs

1use super::super::context::ValidationContext;
2use super::super::validate::ValidationRule;
3use crate::{ast::*, visit::*};
4
5/// Validate a document to only contain one anonymous operation or multiple named operations.
6///
7/// See [`ValidationRule`]
8/// [Reference](https://spec.graphql.org/October2021/#sec-Lone-Anonymous-Operation)
9#[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}