macro_rules! impl_max_gauge_float {
($name:ident, $ty:ty, $min_val:expr) => {
#[derive(Debug, Clone)]
pub struct $name {
max: $ty,
has_value: bool,
}
impl $name {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
max: $min_val,
has_value: false,
}
}
#[inline]
pub fn update(&mut self, sample: $ty) -> Result<(), crate::DataError> {
check_finite!(sample);
if !self.has_value || sample > self.max {
self.max = sample;
}
self.has_value = true;
Ok(())
}
#[inline]
pub fn take(&mut self) -> Option<$ty> {
if self.has_value {
let val = self.max;
self.max = $min_val;
self.has_value = false;
Option::Some(val)
} else {
Option::None
}
}
#[inline]
#[must_use]
pub fn peek(&self) -> Option<$ty> {
if self.has_value {
Option::Some(self.max)
} else {
Option::None
}
}
#[inline]
pub fn reset(&mut self) {
self.max = $min_val;
self.has_value = false;
}
}
impl Default for $name {
#[inline]
fn default() -> Self {
Self::new()
}
}
};
}
macro_rules! impl_max_gauge_int {
($name:ident, $ty:ty, $min_val:expr) => {
#[derive(Debug, Clone)]
pub struct $name {
max: $ty,
has_value: bool,
}
impl $name {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
max: $min_val,
has_value: false,
}
}
#[inline]
pub fn update(&mut self, sample: $ty) {
if !self.has_value || sample > self.max {
self.max = sample;
}
self.has_value = true;
}
#[inline]
pub fn take(&mut self) -> Option<$ty> {
if self.has_value {
let val = self.max;
self.max = $min_val;
self.has_value = false;
Option::Some(val)
} else {
Option::None
}
}
#[inline]
#[must_use]
pub fn peek(&self) -> Option<$ty> {
if self.has_value {
Option::Some(self.max)
} else {
Option::None
}
}
#[inline]
pub fn reset(&mut self) {
self.max = $min_val;
self.has_value = false;
}
}
impl Default for $name {
#[inline]
fn default() -> Self {
Self::new()
}
}
};
}
impl_max_gauge_float!(MaxGaugeF64, f64, f64::MIN);
impl_max_gauge_float!(MaxGaugeF32, f32, f32::MIN);
impl_max_gauge_int!(MaxGaugeI64, i64, i64::MIN);
impl_max_gauge_int!(MaxGaugeI32, i32, i32::MIN);
impl_max_gauge_int!(MaxGaugeI128, i128, i128::MIN);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
let mut g = MaxGaugeF64::new();
assert!(g.peek().is_none());
assert!(g.take().is_none());
}
#[test]
#[allow(clippy::float_cmp)]
fn tracks_max() {
let mut g = MaxGaugeF64::new();
g.update(10.0).unwrap();
g.update(50.0).unwrap();
g.update(30.0).unwrap();
assert_eq!(g.peek(), Some(50.0));
}
#[test]
#[allow(clippy::float_cmp)]
fn take_returns_and_resets() {
let mut g = MaxGaugeF64::new();
g.update(50.0).unwrap();
assert_eq!(g.take(), Some(50.0));
assert!(g.take().is_none());
g.update(20.0).unwrap();
assert_eq!(g.take(), Some(20.0));
}
#[test]
fn i64_basic() {
let mut g = MaxGaugeI64::new();
g.update(100);
g.update(200);
assert_eq!(g.take(), Some(200));
}
#[test]
fn reset() {
let mut g = MaxGaugeF64::new();
g.update(100.0).unwrap();
g.reset();
assert!(g.peek().is_none());
}
#[test]
fn default_is_empty() {
let g = MaxGaugeI32::default();
assert!(g.peek().is_none());
}
#[test]
fn i128_basic() {
let mut g = MaxGaugeI128::new();
g.update(100);
g.update(200);
assert_eq!(g.take(), Some(200));
}
#[test]
fn rejects_nan_and_inf() {
let mut g = MaxGaugeF64::new();
assert!(matches!(
g.update(f64::NAN),
Err(crate::DataError::NotANumber)
));
assert!(matches!(
g.update(f64::INFINITY),
Err(crate::DataError::Infinite)
));
assert!(matches!(
g.update(f64::NEG_INFINITY),
Err(crate::DataError::Infinite)
));
let mut g32 = MaxGaugeF32::new();
assert!(matches!(
g32.update(f32::NAN),
Err(crate::DataError::NotANumber)
));
}
}