qubit-budget 0.4.0

Dependency-light resource limit and budget accounting primitives for Qubit Rust crates
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
// =============================================================================
//    Copyright (c) 2025 - 2026 Haixing Hu.
//
//    SPDX-License-Identifier: Apache-2.0
//
//    Licensed under the Apache License, Version 2.0.
// =============================================================================
//! Defines optional structural input limits.

use super::StructureBudget;
use super::StructureLimitsBuilder;
use super::StructureResource;
use crate::resource::ResourceLimit;
use crate::resource::ResourceQuantity;

/// Optional limits for processing nested structural data.
///
/// `R` identifies the resource values reported in [`crate::BudgetError`], and
/// `Q` is the exact unsigned quantity used for all measurements. The default
/// configuration uses [`StructureResource`] and [`usize`].
///
/// # Type Parameters
///
/// * `R` - Caller-defined resource identity retained by limits and errors.
/// * `Q` - Exact unsigned quantity used for measurements and accounting.
///
/// # Examples
///
/// ```
/// use qubit_budget::StructureLimits;
///
/// let limits = StructureLimits::builder()
///     .max_depth(4)
///     .max_nodes(16)
///     .build();
/// let mut budget = limits.budget();
///
/// budget.check_depth(4).expect("the inclusive depth limit should fit");
/// budget.charge_node().expect("the first node should fit");
/// assert_eq!(budget.used_nodes(), Some(1));
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StructureLimits<R = StructureResource, Q = usize>
where
    Q: ResourceQuantity,
{
    /// Optional inclusive maximum nesting depth.
    max_depth: Option<ResourceLimit<R, Q>>,

    /// Optional cumulative maximum number of processed nodes.
    max_nodes: Option<ResourceLimit<R, Q>>,

    /// Optional inclusive maximum number of items in one sequence.
    max_sequence_items: Option<ResourceLimit<R, Q>>,

    /// Optional inclusive maximum number of entries in one map.
    max_map_entries: Option<ResourceLimit<R, Q>>,

    /// Optional inclusive maximum byte length of one structural key.
    max_key_bytes: Option<ResourceLimit<R, Q>>,
}

impl<R, Q> Default for StructureLimits<R, Q>
where
    Q: ResourceQuantity,
{
    /// Creates structural limits with every dimension unconfigured.
    ///
    /// # Returns
    ///
    /// Creates structural limits with every dimension unconfigured.
    fn default() -> Self {
        Self::new()
    }
}

impl<R, Q> StructureLimits<R, Q>
where
    Q: ResourceQuantity,
{
    /// Creates an unconfigured custom-resource limit set.
    ///
    /// The default [`StructureResource`]/`usize` configuration is constructed
    /// with [`Self::new`]. Custom resource sets use this constructor because
    /// their resource identity is supplied by each `*_limit` method.
    ///
    /// # Returns
    ///
    /// Creates an unconfigured custom-resource limit set.
    #[inline]
    #[must_use]
    pub const fn new() -> Self {
        Self {
            max_depth: None,
            max_nodes: None,
            max_sequence_items: None,
            max_map_entries: None,
            max_key_bytes: None,
        }
    }

    /// Creates a builder for structural limits.
    ///
    /// # Returns
    ///
    /// Creates a builder for structural limits.
    #[inline]
    #[must_use]
    pub const fn builder() -> StructureLimitsBuilder<R, Q> {
        StructureLimitsBuilder::new()
    }

    /// Converts these limits into a builder for further configuration.
    ///
    /// # Returns
    ///
    /// Converts these limits into a builder for further configuration.
    #[inline]
    #[must_use]
    pub const fn into_builder(self) -> StructureLimitsBuilder<R, Q> {
        StructureLimitsBuilder::from_limits(self)
    }

    /// Creates a builder by cloning this limit configuration.
    ///
    /// Unlike [`Self::into_builder`], this method leaves the original limits
    /// available for reuse.
    ///
    /// # Returns
    ///
    /// A builder initialized with a clone of every configured limit.
    #[inline]
    #[must_use]
    pub fn to_builder(&self) -> StructureLimitsBuilder<R, Q>
    where
        R: Clone,
    {
        StructureLimitsBuilder::from_limits(self.clone())
    }

    /// Returns whether any structural limit is configured.
    ///
    /// # Returns
    ///
    /// `true` when at least one structural dimension has a finite limit;
    /// otherwise `false`.
    #[must_use]
    #[inline(always)]
    pub const fn has_limits(&self) -> bool {
        self.max_depth.is_some()
            || self.max_nodes.is_some()
            || self.max_sequence_items.is_some()
            || self.max_map_entries.is_some()
            || self.max_key_bytes.is_some()
    }

    /// Returns the complete depth limit, when configured.
    ///
    /// # Returns
    ///
    /// Returns the complete depth limit, when configured.
    ///
    /// `None` indicates that the corresponding limit or budget dimension is
    /// unconfigured.
    #[must_use]
    #[inline(always)]
    pub const fn depth_limit(&self) -> Option<&ResourceLimit<R, Q>> {
        self.max_depth.as_ref()
    }

    /// Returns the complete node limit, when configured.
    ///
    /// # Returns
    ///
    /// Returns the complete node limit, when configured.
    ///
    /// `None` indicates that the corresponding limit or budget dimension is
    /// unconfigured.
    #[must_use]
    #[inline(always)]
    pub const fn nodes_limit(&self) -> Option<&ResourceLimit<R, Q>> {
        self.max_nodes.as_ref()
    }

    /// Returns the complete sequence-item limit, when configured.
    ///
    /// # Returns
    ///
    /// Returns the complete sequence-item limit, when configured.
    ///
    /// `None` indicates that the corresponding limit or budget dimension is
    /// unconfigured.
    #[must_use]
    #[inline(always)]
    pub const fn sequence_items_limit(&self) -> Option<&ResourceLimit<R, Q>> {
        self.max_sequence_items.as_ref()
    }

    /// Returns the complete map-entry limit, when configured.
    ///
    /// # Returns
    ///
    /// Returns the complete map-entry limit, when configured.
    ///
    /// `None` indicates that the corresponding limit or budget dimension is
    /// unconfigured.
    #[must_use]
    #[inline(always)]
    pub const fn map_entries_limit(&self) -> Option<&ResourceLimit<R, Q>> {
        self.max_map_entries.as_ref()
    }

    /// Returns the complete structural-key limit, when configured.
    ///
    /// # Returns
    ///
    /// Returns the complete structural-key limit, when configured.
    ///
    /// `None` indicates that the corresponding limit or budget dimension is
    /// unconfigured.
    #[must_use]
    #[inline(always)]
    pub const fn key_bytes_limit(&self) -> Option<&ResourceLimit<R, Q>> {
        self.max_key_bytes.as_ref()
    }

    /// Returns the configured maximum nesting depth.
    ///
    /// # Returns
    ///
    /// Returns the configured maximum nesting depth.
    ///
    /// `None` indicates that the corresponding limit or budget dimension is
    /// unconfigured.
    #[must_use]
    #[inline(always)]
    pub const fn max_depth(&self) -> Option<Q> {
        match self.max_depth.as_ref() {
            Some(limit) => Some(limit.maximum()),
            None => None,
        }
    }

    /// Returns the configured maximum number of processed nodes.
    ///
    /// # Returns
    ///
    /// Returns the configured maximum number of processed nodes.
    ///
    /// `None` indicates that the corresponding limit or budget dimension is
    /// unconfigured.
    #[must_use]
    #[inline(always)]
    pub const fn max_nodes(&self) -> Option<Q> {
        match self.max_nodes.as_ref() {
            Some(limit) => Some(limit.maximum()),
            None => None,
        }
    }

    /// Returns the configured maximum number of items in one sequence.
    ///
    /// # Returns
    ///
    /// Returns the configured maximum number of items in one sequence.
    ///
    /// `None` indicates that the corresponding limit or budget dimension is
    /// unconfigured.
    #[must_use]
    #[inline(always)]
    pub const fn max_sequence_items(&self) -> Option<Q> {
        match self.max_sequence_items.as_ref() {
            Some(limit) => Some(limit.maximum()),
            None => None,
        }
    }

    /// Returns the configured maximum number of entries in one map.
    ///
    /// # Returns
    ///
    /// Returns the configured maximum number of entries in one map.
    ///
    /// `None` indicates that the corresponding limit or budget dimension is
    /// unconfigured.
    #[must_use]
    #[inline(always)]
    pub const fn max_map_entries(&self) -> Option<Q> {
        match self.max_map_entries.as_ref() {
            Some(limit) => Some(limit.maximum()),
            None => None,
        }
    }

    /// Returns the configured maximum byte length of one structural key.
    ///
    /// # Returns
    ///
    /// Returns the configured maximum byte length of one structural key.
    ///
    /// `None` indicates that the corresponding limit or budget dimension is
    /// unconfigured.
    #[must_use]
    #[inline(always)]
    pub const fn max_key_bytes(&self) -> Option<Q> {
        match self.max_key_bytes.as_ref() {
            Some(limit) => Some(limit.maximum()),
            None => None,
        }
    }

    /// Creates an independent structural budget session from these limits.
    ///
    /// # Returns
    ///
    /// Creates an independent structural budget session from these limits.
    #[inline]
    #[must_use]
    pub fn budget(&self) -> StructureBudget<R, Q>
    where
        R: Clone,
    {
        StructureBudget::new(self.clone())
    }

    /// Replaces the depth limit during builder composition.
    ///
    /// # Parameters
    ///
    /// * `limit` - Resource-bound nesting-depth limit to install.
    #[inline(always)]
    pub(super) fn set_depth_limit(&mut self, limit: ResourceLimit<R, Q>) {
        self.max_depth = Some(limit);
    }

    /// Replaces the node limit during builder composition.
    ///
    /// # Parameters
    ///
    /// * `limit` - Resource-bound cumulative node limit to install.
    #[inline(always)]
    pub(super) fn set_nodes_limit(&mut self, limit: ResourceLimit<R, Q>) {
        self.max_nodes = Some(limit);
    }

    /// Replaces the sequence-item limit during builder composition.
    ///
    /// # Parameters
    ///
    /// * `limit` - Resource-bound sequence-item limit to install.
    #[inline(always)]
    pub(super) fn set_sequence_items_limit(&mut self, limit: ResourceLimit<R, Q>) {
        self.max_sequence_items = Some(limit);
    }

    /// Replaces the map-entry limit during builder composition.
    ///
    /// # Parameters
    ///
    /// * `limit` - Resource-bound map-entry limit to install.
    #[inline(always)]
    pub(super) fn set_map_entries_limit(&mut self, limit: ResourceLimit<R, Q>) {
        self.max_map_entries = Some(limit);
    }

    /// Replaces the key-byte limit during builder composition.
    ///
    /// # Parameters
    ///
    /// * `limit` - Resource-bound structural-key limit to install.
    #[inline(always)]
    pub(super) fn set_key_bytes_limit(&mut self, limit: ResourceLimit<R, Q>) {
        self.max_key_bytes = Some(limit);
    }
}

impl StructureLimits<StructureResource, usize> {
    /// Replaces the standard depth limit in a const builder operation.
    ///
    /// # Parameters
    ///
    /// * `maximum` - Inclusive maximum to configure.
    #[inline(always)]
    pub(super) const fn set_max_depth(&mut self, maximum: usize) {
        self.max_depth = Some(ResourceLimit::new(StructureResource::Depth, maximum));
    }

    /// Replaces the standard node limit in a const builder operation.
    ///
    /// # Parameters
    ///
    /// * `maximum` - Inclusive maximum to configure.
    #[inline(always)]
    pub(super) const fn set_max_nodes(&mut self, maximum: usize) {
        self.max_nodes = Some(ResourceLimit::new(StructureResource::Nodes, maximum));
    }

    /// Replaces the standard sequence-item limit in a const builder operation.
    ///
    /// # Parameters
    ///
    /// * `maximum` - Inclusive maximum to configure.
    #[inline(always)]
    pub(super) const fn set_max_sequence_items(&mut self, maximum: usize) {
        self.max_sequence_items = Some(ResourceLimit::new(StructureResource::SequenceItems, maximum));
    }

    /// Replaces the standard map-entry limit in a const builder operation.
    ///
    /// # Parameters
    ///
    /// * `maximum` - Inclusive maximum to configure.
    #[inline(always)]
    pub(super) const fn set_max_map_entries(&mut self, maximum: usize) {
        self.max_map_entries = Some(ResourceLimit::new(StructureResource::MapEntries, maximum));
    }

    /// Replaces the standard key-byte limit in a const builder operation.
    ///
    /// # Parameters
    ///
    /// * `maximum` - Inclusive maximum to configure.
    #[inline(always)]
    pub(super) const fn set_max_key_bytes(&mut self, maximum: usize) {
        self.max_key_bytes = Some(ResourceLimit::new(StructureResource::KeyBytes, maximum));
    }
}