use core::fmt;
use crate::MeasurementAggregate;
#[cfg(test)]
mod tests;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MeasurementMeanError {
TotalWithoutSamples,
SaturatedSamples,
SaturatedTotal,
}
impl fmt::Display for MeasurementMeanError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::TotalWithoutSamples => "nonzero measurement total without samples",
Self::SaturatedSamples => "measurement sample count is saturated",
Self::SaturatedTotal => "measurement total is saturated",
})
}
}
impl core::error::Error for MeasurementMeanError {}
pub const fn checked_mean(samples: u64, total: u64) -> Result<Option<u64>, MeasurementMeanError> {
if samples == 0 {
return if total == 0 {
Ok(None)
} else {
Err(MeasurementMeanError::TotalWithoutSamples)
};
}
if samples == u64::MAX {
return Err(MeasurementMeanError::SaturatedSamples);
}
if total == u64::MAX {
return Err(MeasurementMeanError::SaturatedTotal);
}
Ok(Some(total / samples))
}
pub const fn record_sample(samples: &mut u64, total: &mut u64, value: u64) {
*samples = samples.saturating_add(1);
*total = total.saturating_add(value);
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct MeasurementSummary {
aggregate: MeasurementAggregate,
latest: u64,
}
impl MeasurementSummary {
pub const EMPTY: Self = Self {
aggregate: MeasurementAggregate::EMPTY,
latest: 0,
};
pub const fn record(&mut self, value: u64) {
self.aggregate.record(value);
self.latest = value;
}
#[must_use]
pub const fn aggregate(self) -> MeasurementAggregate {
self.aggregate
}
#[must_use]
pub const fn samples(self) -> u64 {
self.aggregate.samples()
}
#[must_use]
pub const fn total(self) -> u64 {
self.aggregate.total()
}
pub const fn mean(self) -> Result<Option<u64>, MeasurementMeanError> {
self.aggregate.mean()
}
#[must_use]
pub const fn latest(self) -> Option<u64> {
if self.samples() == 0 {
None
} else {
Some(self.latest)
}
}
#[must_use]
pub const fn maximum(self) -> Option<u64> {
self.aggregate.maximum()
}
}