use core::fmt;
use crate::{MeasurementStateError, add_total};
#[cfg(test)]
mod tests;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MeasurementCounterError {
SaturatedTotal,
DecreasingTotal,
}
impl fmt::Display for MeasurementCounterError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::SaturatedTotal => "counter total has overflowed",
Self::DecreasingTotal => "counter total decreased",
})
}
}
impl core::error::Error for MeasurementCounterError {}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct MeasurementCounter {
total: u128,
total_saturated: bool,
}
impl MeasurementCounter {
pub const ZERO: Self = Self {
total: 0,
total_saturated: false,
};
pub const fn from_parts(
total: u128,
total_saturated: bool,
) -> Result<Self, MeasurementStateError> {
if total_saturated && total != u128::MAX {
return Err(MeasurementStateError::InvalidSaturation);
}
Ok(Self {
total,
total_saturated,
})
}
pub const fn add(&mut self, amount: u128) {
add_total(&mut self.total, &mut self.total_saturated, amount);
}
pub const fn merge(&mut self, other: Self) {
self.add(other.total);
self.total_saturated |= other.total_saturated;
}
#[must_use]
pub const fn total(self) -> u128 {
self.total
}
#[must_use]
pub const fn total_saturated(self) -> bool {
self.total_saturated
}
pub const fn exact_total(self) -> Result<u128, MeasurementCounterError> {
if self.total_saturated {
Err(MeasurementCounterError::SaturatedTotal)
} else {
Ok(self.total)
}
}
pub const fn delta_since(self, previous: Self) -> Result<u128, MeasurementCounterError> {
if self.total_saturated || previous.total_saturated {
return Err(MeasurementCounterError::SaturatedTotal);
}
match self.total.checked_sub(previous.total) {
Some(delta) => Ok(delta),
None => Err(MeasurementCounterError::DecreasingTotal),
}
}
}