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}