Skip to main content

qubit_budget/value/
string_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//! UTF-8 byte limits for one string value.
9
10use super::StringLimitsBuilder;
11use crate::resource::MeasuredBudgetError;
12use crate::resource::ResourceLimit;
13use crate::resource::ResourceQuantity;
14
15/// Optional point limit for one UTF-8 string's byte length.
16///
17/// # Type Parameters
18///
19/// * `R` - Caller-defined resource identity retained by limits and errors.
20/// * `Q` - Exact unsigned quantity used for measurements and accounting.
21///
22/// # Examples
23///
24/// ```
25/// use qubit_budget::ResourceLimit;
26/// use qubit_budget::StringLimits;
27///
28/// let limits = StringLimits::builder()
29///     .utf8_bytes_limit(ResourceLimit::new("name bytes", 5_u64))
30///     .build();
31/// limits.check("hello").expect("five UTF-8 bytes should fit");
32/// assert!(limits.check("hello!").is_err());
33/// ```
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35pub struct StringLimits<R, Q = u64>
36where
37    Q: ResourceQuantity,
38{
39    /// Optional inclusive maximum for one string's UTF-8 byte length.
40    max_utf8_bytes: Option<ResourceLimit<R, Q>>,
41}
42
43impl<R, Q> StringLimits<R, Q>
44where
45    Q: ResourceQuantity,
46{
47    /// Creates limits with no configured string bound.
48    ///
49    /// # Returns
50    ///
51    /// Creates limits with no configured string bound.
52    #[inline]
53    #[must_use = "the string limit check result must be handled"]
54    pub const fn new() -> Self {
55        Self { max_utf8_bytes: None }
56    }
57
58    /// Creates a builder for string limits.
59    ///
60    /// # Returns
61    ///
62    /// Creates a builder for string limits.
63    #[inline]
64    #[must_use]
65    pub const fn builder() -> StringLimitsBuilder<R, Q> {
66        StringLimitsBuilder::new()
67    }
68
69    /// Converts these limits into a builder for further configuration.
70    ///
71    /// # Returns
72    ///
73    /// Converts these limits into a builder for further configuration.
74    #[inline]
75    #[must_use]
76    pub const fn into_builder(self) -> StringLimitsBuilder<R, Q> {
77        StringLimitsBuilder::from_limits(self)
78    }
79
80    /// Returns the configured UTF-8 byte limit, if any.
81    ///
82    /// # Returns
83    ///
84    /// Returns the configured UTF-8 byte limit, if any.
85    ///
86    /// `None` indicates that the corresponding limit or budget dimension is
87    /// unconfigured.
88    #[must_use]
89    #[inline(always)]
90    pub const fn utf8_bytes_limit(&self) -> Option<&ResourceLimit<R, Q>> {
91        self.max_utf8_bytes.as_ref()
92    }
93
94    /// Checks one string without mutating the limits.
95    ///
96    /// The measured quantity is the string's UTF-8 byte length. A configured
97    /// limit returns a point budget error when the length is too large.
98    ///
99    /// # Parameters
100    ///
101    /// * `value` - UTF-8 string whose byte length is compared with the limit.
102    ///
103    /// # Returns
104    ///
105    /// `Ok(())` when the operation completes successfully.
106    ///
107    /// # Errors
108    ///
109    /// Returns [`MeasuredBudgetError`] when a native measurement cannot fit `Q`
110    /// or a configured limit rejects it.
111    #[inline]
112    #[must_use = "the string limit check result must be handled"]
113    pub fn check(&self, value: &str) -> Result<(), MeasuredBudgetError<R, Q>>
114    where
115        R: Clone,
116    {
117        let Some(limit) = self.max_utf8_bytes.as_ref() else {
118            return Ok(());
119        };
120        let bytes = Q::try_from_usize(value.len())
121            .map_err(|source| MeasuredBudgetError::quantity(limit.resource().clone(), source))?;
122        limit.check(bytes).map_err(MeasuredBudgetError::from)
123    }
124
125    /// Replaces the UTF-8 byte limit during builder composition.
126    ///
127    /// # Parameters
128    ///
129    /// * `limit` - Resource-bound UTF-8 byte limit to install.
130    #[inline(always)]
131    pub(super) fn set_utf8_bytes_limit(&mut self, limit: ResourceLimit<R, Q>) {
132        self.max_utf8_bytes = Some(limit);
133    }
134}
135
136impl<R, Q> Default for StringLimits<R, Q>
137where
138    Q: ResourceQuantity,
139{
140    /// Creates unconfigured string limits.
141    ///
142    /// # Returns
143    ///
144    /// Creates unconfigured string limits.
145    #[inline]
146    fn default() -> Self {
147        Self::new()
148    }
149}