Skip to main content

qubit_budget/structure/
structure_limits.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Defines optional structural input limits.
9
10use super::StructureBudget;
11use super::StructureLimitsBuilder;
12use super::StructureResource;
13use crate::resource::ResourceLimit;
14use crate::resource::ResourceQuantity;
15
16/// Optional limits for processing nested structural data.
17///
18/// `R` identifies the resource values reported in [`crate::BudgetError`], and
19/// `Q` is the exact unsigned quantity used for all measurements. The default
20/// configuration uses [`StructureResource`] and [`usize`].
21///
22/// # Type Parameters
23///
24/// * `R` - Caller-defined resource identity retained by limits and errors.
25/// * `Q` - Exact unsigned quantity used for measurements and accounting.
26///
27/// # Examples
28///
29/// ```
30/// use qubit_budget::StructureLimits;
31///
32/// let limits = StructureLimits::builder()
33///     .max_depth(4)
34///     .max_nodes(16)
35///     .build();
36/// let mut budget = limits.budget();
37///
38/// budget.check_depth(4).expect("the inclusive depth limit should fit");
39/// budget.charge_node().expect("the first node should fit");
40/// assert_eq!(budget.used_nodes(), Some(1));
41/// ```
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43pub struct StructureLimits<R = StructureResource, Q = usize>
44where
45    Q: ResourceQuantity,
46{
47    /// Optional inclusive maximum nesting depth.
48    max_depth: Option<ResourceLimit<R, Q>>,
49
50    /// Optional cumulative maximum number of processed nodes.
51    max_nodes: Option<ResourceLimit<R, Q>>,
52
53    /// Optional inclusive maximum number of items in one sequence.
54    max_sequence_items: Option<ResourceLimit<R, Q>>,
55
56    /// Optional inclusive maximum number of entries in one map.
57    max_map_entries: Option<ResourceLimit<R, Q>>,
58
59    /// Optional inclusive maximum byte length of one structural key.
60    max_key_bytes: Option<ResourceLimit<R, Q>>,
61}
62
63impl<R, Q> Default for StructureLimits<R, Q>
64where
65    Q: ResourceQuantity,
66{
67    /// Creates structural limits with every dimension unconfigured.
68    ///
69    /// # Returns
70    ///
71    /// Creates structural limits with every dimension unconfigured.
72    fn default() -> Self {
73        Self::new()
74    }
75}
76
77impl<R, Q> StructureLimits<R, Q>
78where
79    Q: ResourceQuantity,
80{
81    /// Creates an unconfigured custom-resource limit set.
82    ///
83    /// The default [`StructureResource`]/`usize` configuration is constructed
84    /// with [`Self::new`]. Custom resource sets use this constructor because
85    /// their resource identity is supplied by each `*_limit` method.
86    ///
87    /// # Returns
88    ///
89    /// Creates an unconfigured custom-resource limit set.
90    #[inline]
91    #[must_use]
92    pub const fn new() -> Self {
93        Self {
94            max_depth: None,
95            max_nodes: None,
96            max_sequence_items: None,
97            max_map_entries: None,
98            max_key_bytes: None,
99        }
100    }
101
102    /// Creates a builder for structural limits.
103    ///
104    /// # Returns
105    ///
106    /// Creates a builder for structural limits.
107    #[inline]
108    #[must_use]
109    pub const fn builder() -> StructureLimitsBuilder<R, Q> {
110        StructureLimitsBuilder::new()
111    }
112
113    /// Converts these limits into a builder for further configuration.
114    ///
115    /// # Returns
116    ///
117    /// Converts these limits into a builder for further configuration.
118    #[inline]
119    #[must_use]
120    pub const fn into_builder(self) -> StructureLimitsBuilder<R, Q> {
121        StructureLimitsBuilder::from_limits(self)
122    }
123
124    /// Creates a builder by cloning this limit configuration.
125    ///
126    /// Unlike [`Self::into_builder`], this method leaves the original limits
127    /// available for reuse.
128    ///
129    /// # Returns
130    ///
131    /// A builder initialized with a clone of every configured limit.
132    #[inline]
133    #[must_use]
134    pub fn to_builder(&self) -> StructureLimitsBuilder<R, Q>
135    where
136        R: Clone,
137    {
138        StructureLimitsBuilder::from_limits(self.clone())
139    }
140
141    /// Returns whether any structural limit is configured.
142    ///
143    /// # Returns
144    ///
145    /// `true` when at least one structural dimension has a finite limit;
146    /// otherwise `false`.
147    #[must_use]
148    #[inline(always)]
149    pub const fn has_limits(&self) -> bool {
150        self.max_depth.is_some()
151            || self.max_nodes.is_some()
152            || self.max_sequence_items.is_some()
153            || self.max_map_entries.is_some()
154            || self.max_key_bytes.is_some()
155    }
156
157    /// Returns the complete depth limit, when configured.
158    ///
159    /// # Returns
160    ///
161    /// Returns the complete depth limit, when configured.
162    ///
163    /// `None` indicates that the corresponding limit or budget dimension is
164    /// unconfigured.
165    #[must_use]
166    #[inline(always)]
167    pub const fn depth_limit(&self) -> Option<&ResourceLimit<R, Q>> {
168        self.max_depth.as_ref()
169    }
170
171    /// Returns the complete node limit, when configured.
172    ///
173    /// # Returns
174    ///
175    /// Returns the complete node limit, when configured.
176    ///
177    /// `None` indicates that the corresponding limit or budget dimension is
178    /// unconfigured.
179    #[must_use]
180    #[inline(always)]
181    pub const fn nodes_limit(&self) -> Option<&ResourceLimit<R, Q>> {
182        self.max_nodes.as_ref()
183    }
184
185    /// Returns the complete sequence-item limit, when configured.
186    ///
187    /// # Returns
188    ///
189    /// Returns the complete sequence-item limit, when configured.
190    ///
191    /// `None` indicates that the corresponding limit or budget dimension is
192    /// unconfigured.
193    #[must_use]
194    #[inline(always)]
195    pub const fn sequence_items_limit(&self) -> Option<&ResourceLimit<R, Q>> {
196        self.max_sequence_items.as_ref()
197    }
198
199    /// Returns the complete map-entry limit, when configured.
200    ///
201    /// # Returns
202    ///
203    /// Returns the complete map-entry limit, when configured.
204    ///
205    /// `None` indicates that the corresponding limit or budget dimension is
206    /// unconfigured.
207    #[must_use]
208    #[inline(always)]
209    pub const fn map_entries_limit(&self) -> Option<&ResourceLimit<R, Q>> {
210        self.max_map_entries.as_ref()
211    }
212
213    /// Returns the complete structural-key limit, when configured.
214    ///
215    /// # Returns
216    ///
217    /// Returns the complete structural-key limit, when configured.
218    ///
219    /// `None` indicates that the corresponding limit or budget dimension is
220    /// unconfigured.
221    #[must_use]
222    #[inline(always)]
223    pub const fn key_bytes_limit(&self) -> Option<&ResourceLimit<R, Q>> {
224        self.max_key_bytes.as_ref()
225    }
226
227    /// Returns the configured maximum nesting depth.
228    ///
229    /// # Returns
230    ///
231    /// Returns the configured maximum nesting depth.
232    ///
233    /// `None` indicates that the corresponding limit or budget dimension is
234    /// unconfigured.
235    #[must_use]
236    #[inline(always)]
237    pub const fn max_depth(&self) -> Option<Q> {
238        match self.max_depth.as_ref() {
239            Some(limit) => Some(limit.maximum()),
240            None => None,
241        }
242    }
243
244    /// Returns the configured maximum number of processed nodes.
245    ///
246    /// # Returns
247    ///
248    /// Returns the configured maximum number of processed nodes.
249    ///
250    /// `None` indicates that the corresponding limit or budget dimension is
251    /// unconfigured.
252    #[must_use]
253    #[inline(always)]
254    pub const fn max_nodes(&self) -> Option<Q> {
255        match self.max_nodes.as_ref() {
256            Some(limit) => Some(limit.maximum()),
257            None => None,
258        }
259    }
260
261    /// Returns the configured maximum number of items in one sequence.
262    ///
263    /// # Returns
264    ///
265    /// Returns the configured maximum number of items in one sequence.
266    ///
267    /// `None` indicates that the corresponding limit or budget dimension is
268    /// unconfigured.
269    #[must_use]
270    #[inline(always)]
271    pub const fn max_sequence_items(&self) -> Option<Q> {
272        match self.max_sequence_items.as_ref() {
273            Some(limit) => Some(limit.maximum()),
274            None => None,
275        }
276    }
277
278    /// Returns the configured maximum number of entries in one map.
279    ///
280    /// # Returns
281    ///
282    /// Returns the configured maximum number of entries in one map.
283    ///
284    /// `None` indicates that the corresponding limit or budget dimension is
285    /// unconfigured.
286    #[must_use]
287    #[inline(always)]
288    pub const fn max_map_entries(&self) -> Option<Q> {
289        match self.max_map_entries.as_ref() {
290            Some(limit) => Some(limit.maximum()),
291            None => None,
292        }
293    }
294
295    /// Returns the configured maximum byte length of one structural key.
296    ///
297    /// # Returns
298    ///
299    /// Returns the configured maximum byte length of one structural key.
300    ///
301    /// `None` indicates that the corresponding limit or budget dimension is
302    /// unconfigured.
303    #[must_use]
304    #[inline(always)]
305    pub const fn max_key_bytes(&self) -> Option<Q> {
306        match self.max_key_bytes.as_ref() {
307            Some(limit) => Some(limit.maximum()),
308            None => None,
309        }
310    }
311
312    /// Creates an independent structural budget session from these limits.
313    ///
314    /// # Returns
315    ///
316    /// Creates an independent structural budget session from these limits.
317    #[inline]
318    #[must_use]
319    pub fn budget(&self) -> StructureBudget<R, Q>
320    where
321        R: Clone,
322    {
323        StructureBudget::new(self.clone())
324    }
325
326    /// Replaces the depth limit during builder composition.
327    ///
328    /// # Parameters
329    ///
330    /// * `limit` - Resource-bound nesting-depth limit to install.
331    #[inline(always)]
332    pub(super) fn set_depth_limit(&mut self, limit: ResourceLimit<R, Q>) {
333        self.max_depth = Some(limit);
334    }
335
336    /// Replaces the node limit during builder composition.
337    ///
338    /// # Parameters
339    ///
340    /// * `limit` - Resource-bound cumulative node limit to install.
341    #[inline(always)]
342    pub(super) fn set_nodes_limit(&mut self, limit: ResourceLimit<R, Q>) {
343        self.max_nodes = Some(limit);
344    }
345
346    /// Replaces the sequence-item limit during builder composition.
347    ///
348    /// # Parameters
349    ///
350    /// * `limit` - Resource-bound sequence-item limit to install.
351    #[inline(always)]
352    pub(super) fn set_sequence_items_limit(&mut self, limit: ResourceLimit<R, Q>) {
353        self.max_sequence_items = Some(limit);
354    }
355
356    /// Replaces the map-entry limit during builder composition.
357    ///
358    /// # Parameters
359    ///
360    /// * `limit` - Resource-bound map-entry limit to install.
361    #[inline(always)]
362    pub(super) fn set_map_entries_limit(&mut self, limit: ResourceLimit<R, Q>) {
363        self.max_map_entries = Some(limit);
364    }
365
366    /// Replaces the key-byte limit during builder composition.
367    ///
368    /// # Parameters
369    ///
370    /// * `limit` - Resource-bound structural-key limit to install.
371    #[inline(always)]
372    pub(super) fn set_key_bytes_limit(&mut self, limit: ResourceLimit<R, Q>) {
373        self.max_key_bytes = Some(limit);
374    }
375}
376
377impl StructureLimits<StructureResource, usize> {
378    /// Replaces the standard depth limit in a const builder operation.
379    ///
380    /// # Parameters
381    ///
382    /// * `maximum` - Inclusive maximum to configure.
383    #[inline(always)]
384    pub(super) const fn set_max_depth(&mut self, maximum: usize) {
385        self.max_depth = Some(ResourceLimit::new(StructureResource::Depth, maximum));
386    }
387
388    /// Replaces the standard node limit in a const builder operation.
389    ///
390    /// # Parameters
391    ///
392    /// * `maximum` - Inclusive maximum to configure.
393    #[inline(always)]
394    pub(super) const fn set_max_nodes(&mut self, maximum: usize) {
395        self.max_nodes = Some(ResourceLimit::new(StructureResource::Nodes, maximum));
396    }
397
398    /// Replaces the standard sequence-item limit in a const builder operation.
399    ///
400    /// # Parameters
401    ///
402    /// * `maximum` - Inclusive maximum to configure.
403    #[inline(always)]
404    pub(super) const fn set_max_sequence_items(&mut self, maximum: usize) {
405        self.max_sequence_items = Some(ResourceLimit::new(StructureResource::SequenceItems, maximum));
406    }
407
408    /// Replaces the standard map-entry limit in a const builder operation.
409    ///
410    /// # Parameters
411    ///
412    /// * `maximum` - Inclusive maximum to configure.
413    #[inline(always)]
414    pub(super) const fn set_max_map_entries(&mut self, maximum: usize) {
415        self.max_map_entries = Some(ResourceLimit::new(StructureResource::MapEntries, maximum));
416    }
417
418    /// Replaces the standard key-byte limit in a const builder operation.
419    ///
420    /// # Parameters
421    ///
422    /// * `maximum` - Inclusive maximum to configure.
423    #[inline(always)]
424    pub(super) const fn set_max_key_bytes(&mut self, maximum: usize) {
425        self.max_key_bytes = Some(ResourceLimit::new(StructureResource::KeyBytes, maximum));
426    }
427}