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}