async_graphql/validation/visitors/
depth.rs

1use async_graphql_parser::types::Field;
2
3use crate::{
4    Positioned,
5    validation::visitor::{VisitMode, Visitor, VisitorContext},
6};
7
8pub struct DepthCalculate<'a> {
9    max_depth: &'a mut usize,
10    current_depth: usize,
11}
12
13impl<'a> DepthCalculate<'a> {
14    pub fn new(max_depth: &'a mut usize) -> Self {
15        Self {
16            max_depth,
17            current_depth: 0,
18        }
19    }
20}
21
22impl<'ctx> Visitor<'ctx> for DepthCalculate<'_> {
23    fn mode(&self) -> VisitMode {
24        VisitMode::Inline
25    }
26
27    fn enter_field(&mut self, _ctx: &mut VisitorContext<'ctx>, _field: &'ctx Positioned<Field>) {
28        self.current_depth += 1;
29        *self.max_depth = (*self.max_depth).max(self.current_depth);
30    }
31
32    fn exit_field(&mut self, _ctx: &mut VisitorContext<'ctx>, _field: &'ctx Positioned<Field>) {
33        self.current_depth -= 1;
34    }
35}
36
37#[cfg(test)]
38#[allow(clippy::diverging_sub_expression)]
39mod tests {
40    use super::*;
41    use crate::{
42        EmptyMutation, EmptySubscription, Object, Schema, parser::parse_query, validation::visit,
43    };
44
45    struct Query;
46
47    struct MyObj;
48
49    #[Object(internal)]
50    #[allow(unreachable_code)]
51    impl MyObj {
52        async fn a(&self) -> i32 {
53            todo!()
54        }
55
56        async fn b(&self) -> i32 {
57            todo!()
58        }
59
60        async fn c(&self) -> MyObj {
61            todo!()
62        }
63    }
64
65    #[Object(internal)]
66    #[allow(unreachable_code)]
67    impl Query {
68        async fn value(&self) -> i32 {
69            todo!()
70        }
71
72        async fn obj(&self) -> MyObj {
73            todo!()
74        }
75    }
76
77    fn check_depth(query: &str, expect_depth: usize) {
78        let registry =
79            Schema::<Query, EmptyMutation, EmptySubscription>::create_registry(Default::default());
80        let doc = parse_query(query).unwrap();
81        let mut ctx = VisitorContext::new(&registry, &doc, None);
82        let mut depth = 0;
83        let mut depth_calculate = DepthCalculate::new(&mut depth);
84        visit(&mut depth_calculate, &mut ctx, &doc);
85        assert_eq!(depth, expect_depth);
86    }
87
88    #[test]
89    fn depth() {
90        check_depth(
91            r#"{
92            value #1
93        }"#,
94            1,
95        );
96
97        check_depth(
98            r#"
99        {
100            obj { #1
101                a b #2
102            }
103        }"#,
104            2,
105        );
106
107        check_depth(
108            r#"
109        {
110            obj { # 1
111                a b c { # 2
112                    a b c { # 3
113                        a b # 4
114                    }
115                }
116            }
117        }"#,
118            4,
119        );
120
121        check_depth(
122            r#"
123        fragment A on MyObj {
124            a b ... A2 #2
125        }
126
127        fragment A2 on MyObj {
128            obj {
129                a #3
130            }
131        }
132
133        query {
134            obj { # 1
135                ... A
136            }
137        }"#,
138            3,
139        );
140
141        check_depth(
142            r#"
143        {
144            obj { # 1
145                ... on MyObj {
146                    a b #2
147                    ... on MyObj {
148                        obj {
149                            a #3
150                        }
151                    }
152                }
153            }
154        }"#,
155            3,
156        );
157    }
158}