qubit_budget/json/decode/json_decode_limits.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//! Defines JSON decoding limits.
9
10use super::JsonDecodeLimitsBuilder;
11use crate::json::JsonResource;
12use crate::json::JsonValueLimits;
13use crate::resource::ResourceLimit;
14use crate::resource::ResourceQuantity;
15
16/// Optional limits for one JSON decoding session.
17///
18/// # Type Parameters
19///
20/// * `R` - Caller-defined resource identity retained by limits and errors.
21/// * `Q` - Exact unsigned quantity used for measurements and accounting.
22///
23/// # Examples
24///
25/// ```
26/// use qubit_budget::json::JsonDecodeLimits;
27///
28/// let limits = JsonDecodeLimits::builder().max_input_bytes(128_usize).max_depth(4_usize).build();
29/// assert_eq!(limits.max_input_bytes(), Some(128));
30/// assert_eq!(limits.value_limits().max_depth(), Some(4));
31/// ```
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33pub struct JsonDecodeLimits<R = JsonResource, Q = usize>
34where
35 Q: ResourceQuantity,
36{
37 /// Optional maximum for bytes read from the original JSON input.
38 input: Option<ResourceLimit<R, Q>>,
39 /// Optional maximum for bytes retained after input normalization.
40 normalized_input: Option<ResourceLimit<R, Q>>,
41 /// Limits applied to the decoded JSON value and its structure.
42 value: JsonValueLimits<R, Q>,
43}
44
45impl<R, Q> Default for JsonDecodeLimits<R, Q>
46where
47 Q: ResourceQuantity,
48{
49 /// Creates decoding limits with every dimension unconfigured.
50 ///
51 /// # Returns
52 ///
53 /// Creates decoding limits with every dimension unconfigured.
54 fn default() -> Self {
55 Self::new()
56 }
57}
58
59impl<R, Q> JsonDecodeLimits<R, Q>
60where
61 Q: ResourceQuantity,
62{
63 /// Creates an empty decoding limit set with no configured resource limits.
64 ///
65 /// # Returns
66 ///
67 /// Creates an empty decoding limit set with no configured resource limits.
68 #[inline]
69 #[must_use]
70 pub const fn new() -> Self {
71 Self {
72 input: None,
73 normalized_input: None,
74 value: JsonValueLimits::new(),
75 }
76 }
77
78 /// Creates a builder for JSON decoding limits.
79 ///
80 /// # Returns
81 ///
82 /// Creates a builder for JSON decoding limits.
83 #[inline]
84 #[must_use]
85 pub const fn builder() -> JsonDecodeLimitsBuilder<R, Q> {
86 JsonDecodeLimitsBuilder::new()
87 }
88
89 /// Converts these limits into a builder for further configuration.
90 ///
91 /// # Returns
92 ///
93 /// Converts these limits into a builder for further configuration.
94 #[inline]
95 #[must_use]
96 pub const fn into_builder(self) -> JsonDecodeLimitsBuilder<R, Q> {
97 JsonDecodeLimitsBuilder::from_limits(self)
98 }
99
100 /// Returns whether any decoding or nested value limit is configured.
101 ///
102 /// # Returns
103 ///
104 /// `true` when at least one input or nested value dimension has a finite
105 /// limit; otherwise `false`.
106 #[must_use]
107 #[inline(always)]
108 pub const fn has_limits(&self) -> bool {
109 self.input.is_some() || self.normalized_input.is_some() || self.value.has_limits()
110 }
111
112 /// Returns the complete raw input-byte limit, when configured.
113 ///
114 /// # Returns
115 ///
116 /// Returns the complete raw input-byte limit, when configured.
117 ///
118 /// `None` indicates that the corresponding limit or budget dimension is
119 /// unconfigured.
120 #[must_use]
121 #[inline(always)]
122 pub const fn input_bytes_limit(&self) -> Option<&ResourceLimit<R, Q>> {
123 self.input.as_ref()
124 }
125
126 /// Returns the complete normalized input-byte limit, when configured.
127 ///
128 /// # Returns
129 ///
130 /// Returns the complete normalized input-byte limit, when configured.
131 ///
132 /// `None` indicates that the corresponding limit or budget dimension is
133 /// unconfigured.
134 #[must_use]
135 #[inline(always)]
136 pub const fn normalized_input_bytes_limit(&self) -> Option<&ResourceLimit<R, Q>> {
137 self.normalized_input.as_ref()
138 }
139
140 /// Borrows the JSON value limits used for decoding.
141 ///
142 /// # Returns
143 ///
144 /// Borrows the JSON value limits used for decoding.
145 #[must_use]
146 #[inline(always)]
147 pub const fn value_limits(&self) -> &JsonValueLimits<R, Q> {
148 &self.value
149 }
150
151 /// Consumes these decoding limits and returns their JSON value limits.
152 ///
153 /// # Returns
154 ///
155 /// Consumes these decoding limits and returns their JSON value limits.
156 #[must_use]
157 #[inline]
158 pub fn into_value_limits(self) -> JsonValueLimits<R, Q> {
159 self.value
160 }
161
162 /// Returns the configured raw input-byte maximum.
163 ///
164 /// # Returns
165 ///
166 /// Returns the configured raw input-byte maximum.
167 ///
168 /// `None` indicates that the corresponding limit or budget dimension is
169 /// unconfigured.
170 #[must_use]
171 #[inline(always)]
172 pub const fn max_input_bytes(&self) -> Option<Q> {
173 limit_maximum(self.input.as_ref())
174 }
175
176 /// Returns the configured normalized input-byte maximum.
177 ///
178 /// # Returns
179 ///
180 /// Returns the configured normalized input-byte maximum.
181 ///
182 /// `None` indicates that the corresponding limit or budget dimension is
183 /// unconfigured.
184 #[must_use]
185 #[inline(always)]
186 pub const fn max_normalized_input_bytes(&self) -> Option<Q> {
187 limit_maximum(self.normalized_input.as_ref())
188 }
189
190 /// Replaces the raw input-byte limit during builder composition.
191 ///
192 /// # Parameters
193 ///
194 /// * `limit` - Resource-bound raw input-byte limit to install.
195 pub(super) fn set_input_bytes_limit(&mut self, limit: ResourceLimit<R, Q>) {
196 self.input = Some(limit);
197 }
198
199 /// Replaces the normalized input-byte limit during builder composition.
200 ///
201 /// # Parameters
202 ///
203 /// * `limit` - Resource-bound normalized input-byte limit to install.
204 pub(super) fn set_normalized_input_bytes_limit(&mut self, limit: ResourceLimit<R, Q>) {
205 self.normalized_input = Some(limit);
206 }
207
208 /// Replaces the JSON value limits during builder composition.
209 ///
210 /// # Parameters
211 ///
212 /// * `limits` - JSON value limits to apply during decoding.
213 pub(super) fn set_value_limits(&mut self, limits: JsonValueLimits<R, Q>) {
214 self.value = limits;
215 }
216}
217
218/// Extracts the maximum from an optional limit without exposing its resource.
219///
220/// # Type Parameters
221///
222/// * `R` - Caller-defined resource identity retained by limits and errors.
223/// * `Q` - Exact unsigned quantity used for measurements and accounting.
224///
225/// # Parameters
226///
227/// * `limit` - Optional resource-bound limit to inspect.
228///
229/// # Returns
230///
231/// `Some(maximum)` when the limit is configured, or `None` otherwise.
232#[inline(always)]
233const fn limit_maximum<R, Q>(limit: Option<&ResourceLimit<R, Q>>) -> Option<Q>
234where
235 Q: ResourceQuantity,
236{
237 match limit {
238 Some(limit) => Some(limit.maximum()),
239 None => None,
240 }
241}