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}