Skip to main content

oxc_graphql_parser/
limit.rs

1use std::fmt;
2use std::hint::cold_path;
3
4/// A LimitTracker enforces a particular limit within the parser. It keeps
5/// track of utilization so that we can report how close to a limit we
6/// approached over the lifetime of the tracker.
7/// ```rust
8/// use oxc_graphql_parser::{Allocator, Parser};
9///
10/// let query = "
11/// {
12///     animal
13///     ...snackSelection
14///     ... on Pet {
15///       playmates {
16///         count
17///       }
18///     }
19/// }
20/// ";
21/// // Create a new instance of a parser given a query and a
22/// // recursion limit
23/// let allocator = Allocator::default();
24/// let parser = Parser::new(&allocator, query).recursion_limit(4);
25/// // Parse the query, and return an AST.
26/// let ast = parser.parse();
27/// // Retrieve the limits
28/// let usage = ast.recursion_limit();
29/// // Print out some of the usage details to see what happened during
30/// // our parse. `limit` just reports the limit we set, `high` is the
31/// // high-water mark of recursion usage.
32/// println!("{:?}", usage);
33/// println!("{:?}", usage.limit);
34/// println!("{:?}", usage.high);
35/// // Check that are no errors. These are not part of the AST.
36/// assert_eq!(0, ast.errors().len());
37///
38/// // Get the document root node
39/// let doc = ast.document();
40/// // ... continue
41/// ```
42#[derive(PartialEq, Eq, Clone, Copy)]
43pub struct LimitTracker {
44    pub(crate) current: usize,
45    /// High Water mark for this limit
46    pub high: usize,
47    /// Limit.
48    pub limit: usize,
49}
50
51impl LimitTracker {
52    pub fn new(limit: usize) -> Self {
53        Self { current: 0, high: 0, limit }
54    }
55
56    /// Return whether the limit was reached
57    #[must_use]
58    pub fn check_and_increment(&mut self) -> bool {
59        self.current += 1;
60        if self.current > self.high {
61            self.high = self.current;
62        }
63        let reached = self.current > self.limit;
64        if reached {
65            cold_path();
66            // Caller is gonna return early, keep increments and decrements balanced:
67            self.decrement()
68        }
69        reached
70    }
71
72    pub fn decrement(&mut self) {
73        self.current -= 1;
74    }
75}
76
77impl fmt::Debug for LimitTracker {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        write!(f, "recursion limit: {}, high: {}", self.limit, self.high)
80    }
81}