use core::fmt;
#[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 {
samples: u64,
total: u64,
latest: u64,
maximum: u64,
}
impl MeasurementSummary {
pub const EMPTY: Self = Self {
samples: 0,
total: 0,
latest: 0,
maximum: 0,
};
pub const fn record(&mut self, value: u64) {
record_sample(&mut self.samples, &mut self.total, value);
self.latest = value;
if value > self.maximum {
self.maximum = value;
}
}
#[must_use]
pub const fn samples(self) -> u64 {
self.samples
}
#[must_use]
pub const fn total(self) -> u64 {
self.total
}
pub const fn mean(self) -> Result<Option<u64>, MeasurementMeanError> {
checked_mean(self.samples, self.total)
}
#[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> {
if self.samples == 0 {
None
} else {
Some(self.maximum)
}
}
}