Skip to main content

qubit_json/value/traverse/
json_tree_budget_tracker.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Provides reusable full-tree JSON budget accounting.
9
10use qubit_budget::MeasuredBudgetError;
11use qubit_budget::ResourceQuantity;
12use qubit_budget::json::JsonResource;
13use qubit_budget::json::JsonValueBudget;
14use qubit_budget::json::JsonValueLimits;
15use serde_json::Value;
16
17use super::JsonTreeReader;
18
19/// Fully accounts materialized JSON trees using an internally owned budget.
20///
21/// # Type Parameters
22///
23/// * `R` - Resource identity tracked by the owned value budget.
24/// * `Q` - Quantity representation used for resource accounting.
25///
26/// # Examples
27///
28/// ```
29/// use qubit_budget::json::{JsonResource, JsonValueLimits};
30/// use qubit_json::value::traverse::JsonTreeBudgetTracker;
31/// use serde_json::json;
32///
33/// let mut tracker = JsonTreeBudgetTracker::<JsonResource, usize>::new(
34///     JsonValueLimits::default(),
35/// );
36/// assert!(tracker.account(&json!({"ok": true})).is_ok());
37/// # Ok::<(), qubit_budget::MeasuredBudgetError<JsonResource, usize>>(())
38/// ```
39pub struct JsonTreeBudgetTracker<R = JsonResource, Q = usize>
40where
41    Q: ResourceQuantity,
42{
43    /// Internally owned budget accumulating successful tree-accounting runs.
44    budget: JsonValueBudget<R, Q>,
45}
46
47impl<R, Q> JsonTreeBudgetTracker<R, Q>
48where
49    R: Clone,
50    Q: ResourceQuantity,
51{
52    /// Creates a full-tree tracker with fresh budget state.
53    ///
54    /// # Parameters
55    ///
56    /// * `limits` - Resource limits used by the owned budget.
57    ///
58    /// # Returns
59    ///
60    /// A tracker initialized with the supplied limits.
61    #[inline]
62    #[must_use]
63    pub fn new(limits: JsonValueLimits<R, Q>) -> Self {
64        Self {
65            budget: JsonValueBudget::new(limits),
66        }
67    }
68
69    /// Charges every node and payload represented by `value`.
70    ///
71    /// # Parameters
72    ///
73    /// * `value` - JSON tree whose resources are charged.
74    ///
75    /// # Returns
76    ///
77    /// `Ok(())` when the complete tree is admitted.
78    ///
79    /// # Errors
80    ///
81    /// Returns the first measured budget rejection encountered while walking
82    /// the tree. Charges are committed only when the complete walk succeeds.
83    pub fn account(&mut self, value: &Value) -> Result<(), MeasuredBudgetError<R, Q>> {
84        let mut transaction = self.budget.transaction();
85        let result = JsonTreeReader::new(&mut transaction).account(value);
86        match result {
87            Ok(()) => transaction.commit(),
88            Err(error) => Err(error),
89        }
90    }
91
92    /// Restores the owned budget to its original configured state.
93    ///
94    /// This clears accumulated charges and makes the tracker ready for a new
95    /// independent accounting run.
96    ///
97    /// Resetting does not change the configured limits or resource identities;
98    /// it only discards usage accumulated since construction or the previous
99    /// reset.
100    #[inline(always)]
101    pub fn reset(&mut self) {
102        self.budget.reset();
103    }
104
105    /// Returns the owned budget for read-only inspection.
106    ///
107    /// # Returns
108    ///
109    /// A shared reference to the accumulated budget state.
110    #[must_use]
111    #[inline(always)]
112    pub const fn budget(&self) -> &JsonValueBudget<R, Q> {
113        &self.budget
114    }
115
116    /// Returns the owned budget for caller-managed accounting.
117    ///
118    /// # Returns
119    ///
120    /// A mutable reference to the accumulated budget state.
121    #[must_use]
122    #[inline(always)]
123    pub fn budget_mut(&mut self) -> &mut JsonValueBudget<R, Q> {
124        &mut self.budget
125    }
126
127    /// Consumes this tracker and returns its accumulated budget state.
128    ///
129    /// # Returns
130    ///
131    /// The owned budget, including all charges accumulated by this tracker.
132    #[must_use]
133    #[inline(always)]
134    pub fn into_budget(self) -> JsonValueBudget<R, Q> {
135        self.budget
136    }
137}