Skip to main content

qubit_budget/resource/error/
measured_budget_error.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//! Defines failures produced while measuring resource-limited values.
9// qubit-style: allow source-test-pair
10
11use std::fmt::Debug;
12
13use thiserror::Error;
14
15use crate::resource::BudgetError;
16use crate::resource::InsufficientBudgetError;
17use crate::resource::LimitExceededError;
18use crate::resource::QuantityConversionError;
19
20/// Error returned when native measurement or budget validation rejects a value.
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::MeasuredBudgetError;
31/// use qubit_budget::QuantityConversionError;
32/// use qubit_budget::QuantityMeasurement;
33///
34/// let source = QuantityConversionError::new(QuantityMeasurement::Usize(256), "u8");
35/// let error = MeasuredBudgetError::<_, u8>::quantity("bytes", source);
36/// assert!(error.quantity_error().is_some());
37/// ```
38#[must_use]
39#[derive(Clone, Debug, Error)]
40pub enum MeasuredBudgetError<R, Q = u64>
41where
42    Q: Copy + Debug,
43{
44    /// A native measurement could not fit the configured quantity type.
45    #[error("resource {resource:?} has an unrepresentable measurement: {source}")]
46    Quantity {
47        /// Resource associated with the rejected measurement.
48        resource: R,
49        /// Native quantity conversion failure.
50        #[source]
51        source: QuantityConversionError,
52    },
53
54    /// A representable measurement exceeded its configured resource budget.
55    #[error(transparent)]
56    Budget(
57        /// Exact point-limit or cumulative-budget failure.
58        #[from]
59        BudgetError<R, Q>,
60    ),
61}
62
63impl<R, Q> MeasuredBudgetError<R, Q>
64where
65    Q: Copy + Debug,
66{
67    /// Creates a failure for a native measurement that did not fit `Q`.
68    ///
69    /// # Parameters
70    ///
71    /// * `resource` - Resource being measured.
72    /// * `source` - Exact native quantity conversion failure.
73    ///
74    /// # Returns
75    ///
76    /// A quantity representation failure retaining its resource identity.
77    #[inline(always)]
78    pub const fn quantity(resource: R, source: QuantityConversionError) -> Self {
79        Self::Quantity { resource, source }
80    }
81
82    /// Returns the contained budget failure when the measurement fit `Q`.
83    ///
84    /// # Returns
85    ///
86    /// `Some` for [`Self::Budget`], or `None` for [`Self::Quantity`].
87    #[must_use]
88    #[inline(always)]
89    pub const fn budget_error(&self) -> Option<&BudgetError<R, Q>> {
90        match self {
91            Self::Budget(error) => Some(error),
92            Self::Quantity { .. } => None,
93        }
94    }
95
96    /// Returns the native quantity conversion failure, when present.
97    ///
98    /// # Returns
99    ///
100    /// `Some` for [`Self::Quantity`], or `None` for [`Self::Budget`].
101    #[must_use]
102    #[inline(always)]
103    pub const fn quantity_error(&self) -> Option<&QuantityConversionError> {
104        match self {
105            Self::Quantity { source, .. } => Some(source),
106            Self::Budget(_) => None,
107        }
108    }
109
110    /// Returns the resource associated with this failure.
111    ///
112    /// The resource is present for both budget validation and quantity
113    /// conversion failures, so callers do not need to match the error variant
114    /// merely to attach resource context.
115    ///
116    /// # Returns
117    ///
118    /// Returns the resource associated with this failure.
119    #[must_use]
120    #[inline(always)]
121    pub const fn resource(&self) -> &R {
122        match self {
123            Self::Quantity { resource, .. } => resource,
124            Self::Budget(error) => error.resource(),
125        }
126    }
127
128    /// Consumes this failure and returns its associated resource.
129    ///
130    /// # Returns
131    ///
132    /// Consumes this failure and returns its associated resource.
133    #[inline(always)]
134    #[must_use]
135    pub fn into_resource(self) -> R {
136        match self {
137            Self::Quantity { resource, .. } => resource,
138            Self::Budget(error) => error.into_resource(),
139        }
140    }
141}
142
143impl<R, Q> From<LimitExceededError<R, Q>> for MeasuredBudgetError<R, Q>
144where
145    Q: Copy + Debug,
146{
147    /// Wraps a point-limit failure in a measured-budget failure.
148    ///
149    /// # Parameters
150    ///
151    /// * `error` - Precise point-limit failure to wrap.
152    ///
153    /// # Returns
154    ///
155    /// Wraps a point-limit failure in a measured-budget failure.
156    #[inline(always)]
157    fn from(error: LimitExceededError<R, Q>) -> Self {
158        Self::Budget(error.into())
159    }
160}
161
162impl<R, Q> From<InsufficientBudgetError<R, Q>> for MeasuredBudgetError<R, Q>
163where
164    Q: Copy + Debug,
165{
166    /// Wraps a cumulative-budget failure in a measured-budget failure.
167    ///
168    /// # Parameters
169    ///
170    /// * `error` - Precise cumulative-budget failure to wrap.
171    ///
172    /// # Returns
173    ///
174    /// Wraps a cumulative-budget failure in a measured-budget failure.
175    #[inline(always)]
176    fn from(error: InsufficientBudgetError<R, Q>) -> Self {
177        Self::Budget(error.into())
178    }
179}