Skip to main content

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}