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}