#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Surge {
limit: f32,
rising: bool,
last: Option<f32>,
}
impl Surge {
pub fn rising(limit: f32) -> Self {
Self {
limit: magnitude(limit),
rising: true,
last: None,
}
}
pub fn falling(limit: f32) -> Self {
Self {
limit: magnitude(limit),
rising: false,
last: None,
}
}
pub fn update(&mut self, value: f32) -> Option<f32> {
let exceeded = match self.last {
Some(previous) => {
let change = value - previous;
let watched = if self.rising { change } else { -change };
if watched > self.limit {
Some(watched)
} else {
None
}
}
None => None,
};
self.last = Some(value);
exceeded
}
}
fn magnitude(value: f32) -> f32 {
if value < 0.0 {
-value
} else {
value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_first_reading_has_no_rate() {
let mut surge = Surge::rising(1.0);
assert_eq!(surge.update(5.0), None);
}
#[test]
fn a_rapid_rise_reports_its_rate() {
let mut surge = Surge::rising(0.5);
surge.update(1.0);
assert_eq!(surge.update(1.25), None); assert_eq!(surge.update(2.0), Some(0.75)); }
#[test]
fn a_rising_alarm_ignores_a_fall() {
let mut surge = Surge::rising(0.5);
surge.update(5.0);
assert_eq!(surge.update(1.0), None); }
#[test]
fn a_falling_alarm_reports_a_rapid_drop() {
let mut surge = Surge::falling(0.5);
surge.update(3.0);
assert_eq!(surge.update(2.75), None); assert_eq!(surge.update(1.0), Some(1.75)); }
#[test]
fn a_negative_limit_is_treated_as_its_magnitude() {
let mut surge = Surge::rising(-0.5);
surge.update(1.0);
assert_eq!(surge.update(2.0), Some(1.0));
}
}