use core::fmt;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MeasurementRecordError {
Samples,
Total,
}
impl fmt::Display for MeasurementRecordError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::Samples => "measurement sample count would overflow",
Self::Total => "measurement total would overflow",
})
}
}
impl core::error::Error for MeasurementRecordError {}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct CheckedMeasurementAggregate {
samples: u64,
total: u128,
minimum: u128,
maximum: u128,
}
impl CheckedMeasurementAggregate {
pub const EMPTY: Self = Self {
samples: 0,
total: 0,
minimum: 0,
maximum: 0,
};
pub const fn try_record(&mut self, value: u128) -> Result<(), MeasurementRecordError> {
let Some(samples) = self.samples.checked_add(1) else {
return Err(MeasurementRecordError::Samples);
};
let Some(total) = self.total.checked_add(value) else {
return Err(MeasurementRecordError::Total);
};
if self.samples == 0 || value < self.minimum {
self.minimum = value;
}
if value > self.maximum {
self.maximum = value;
}
self.samples = samples;
self.total = total;
Ok(())
}
#[must_use]
pub const fn samples(self) -> u64 {
self.samples
}
#[must_use]
pub const fn total(self) -> u128 {
self.total
}
#[must_use]
pub const fn minimum(self) -> Option<u128> {
if self.samples == 0 {
None
} else {
Some(self.minimum)
}
}
#[must_use]
pub const fn maximum(self) -> Option<u128> {
if self.samples == 0 {
None
} else {
Some(self.maximum)
}
}
}
#[cfg(test)]
mod tests;