#[derive(Debug, Clone)]
pub struct DebounceU64 {
threshold: u64,
consecutive: u64,
}
impl DebounceU64 {
#[inline]
pub fn new(threshold: u64) -> Result<Self, crate::ConfigError> {
if threshold == 0 {
return Err(crate::ConfigError::Invalid("threshold must be positive"));
}
Ok(Self {
threshold,
consecutive: 0,
})
}
#[inline]
#[must_use]
pub fn update(&mut self, active: bool) -> bool {
if active {
self.consecutive += 1;
} else {
self.consecutive = 0;
}
self.consecutive >= self.threshold
}
#[inline]
#[must_use]
pub fn count(&self) -> u64 {
self.consecutive
}
#[inline]
pub fn reset(&mut self) {
self.consecutive = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn triggers_after_threshold() {
let mut d = DebounceU64::new(3).unwrap();
assert!(!d.update(true));
assert!(!d.update(true));
assert!(d.update(true)); }
#[test]
fn resets_on_false() {
let mut d = DebounceU64::new(3).unwrap();
assert!(!d.update(true));
assert!(!d.update(true));
assert!(!d.update(false)); assert!(!d.update(true)); assert!(!d.update(true));
assert!(d.update(true)); }
#[test]
fn no_false_trigger_at_threshold_minus_one() {
let mut d = DebounceU64::new(5).unwrap();
for _ in 0..4 {
assert!(!d.update(true));
}
assert!(!d.update(false)); }
#[test]
fn stays_triggered() {
let mut d = DebounceU64::new(2).unwrap();
assert!(!d.update(true));
assert!(d.update(true));
assert!(d.update(true)); }
#[test]
fn reset() {
let mut d = DebounceU64::new(2).unwrap();
let _ = d.update(true);
d.reset();
assert_eq!(d.count(), 0);
}
#[test]
fn rejects_zero_threshold() {
assert!(matches!(
DebounceU64::new(0),
Err(crate::ConfigError::Invalid(_))
));
}
}