use std::time::{Duration, Instant};
pub struct Throttle {
interval: Duration,
last_run: Option<Instant>,
}
impl Throttle {
pub fn new(interval: Duration) -> Self {
Self {
interval,
last_run: None,
}
}
pub fn from_millis(ms: u64) -> Self {
Self::new(Duration::from_millis(ms))
}
pub fn should_run(&mut self) -> bool {
let now = Instant::now();
if let Some(last) = self.last_run {
if now.duration_since(last) < self.interval {
return false;
}
}
self.last_run = Some(now);
true
}
pub fn run<F, T>(&mut self, f: F) -> Option<T>
where
F: FnOnce() -> T,
{
if self.should_run() {
Some(f())
} else {
None
}
}
pub fn reset(&mut self) {
self.last_run = None;
}
}
pub struct Debounce {
delay: Duration,
last_call: Option<Instant>,
pending: bool,
}
impl Debounce {
pub fn new(delay: Duration) -> Self {
Self {
delay,
last_call: None,
pending: false,
}
}
pub fn from_millis(ms: u64) -> Self {
Self::new(Duration::from_millis(ms))
}
pub fn call(&mut self) {
self.last_call = Some(Instant::now());
self.pending = true;
}
pub fn is_ready(&self) -> bool {
if !self.pending {
return false;
}
if let Some(last) = self.last_call {
Instant::now().duration_since(last) >= self.delay
} else {
false
}
}
pub fn fire_if_ready(&mut self) -> bool {
if self.is_ready() {
self.pending = false;
true
} else {
false
}
}
pub fn reset(&mut self) {
self.last_call = None;
self.pending = false;
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn test_throttle() {
let mut throttle = Throttle::from_millis(50);
assert!(throttle.should_run()); assert!(!throttle.should_run());
thread::sleep(Duration::from_millis(60));
assert!(throttle.should_run()); }
#[test]
fn test_debounce() {
let mut debounce = Debounce::from_millis(50);
debounce.call();
assert!(!debounce.is_ready());
thread::sleep(Duration::from_millis(60));
assert!(debounce.is_ready()); assert!(debounce.fire_if_ready());
assert!(!debounce.is_ready()); }
}