use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct RateLimiter {
cooldown: Duration,
start: Option<Instant>,
}
impl RateLimiter {
pub fn new(cooldown: Duration) -> Self {
assert!(!cooldown.is_zero());
Self {
cooldown,
start: None,
}
}
pub fn start_now(&mut self) -> Option<Instant> {
self.start.replace(Instant::now())
}
pub fn run(&mut self, f: impl FnOnce()) {
self.try_run(f).ok();
}
pub fn run_dt(&mut self, f: impl FnOnce(Duration)) {
let Some(start) = self.start else {
self.start_now();
return;
};
let now = Instant::now();
let elapsed = now - start;
if elapsed >= self.cooldown {
f(elapsed);
self.start.replace(now);
}
}
pub fn try_run(&mut self, f: impl FnOnce()) -> Result<(), Duration> {
let Some(start) = self.start else {
f();
self.start_now();
return Ok(());
};
let t_cold = start + self.cooldown;
let now = Instant::now();
if now < t_cold {
Err(t_cold - now)
} else {
f();
self.start.replace(now);
Ok(())
}
}
pub fn get_start(&self) -> Option<Instant> {
self.start
}
pub fn cooldown_period(&self) -> Duration {
self.cooldown
}
}
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct Timer<F: FnMut(Duration)> {
started: Instant,
on_drop: F,
}
impl<F: FnMut(Duration)> Timer<F> {
pub fn start(on_drop: F) -> Self {
Self {
started: Instant::now(),
on_drop,
}
}
}
impl<F: FnMut(Duration)> Drop for Timer<F> {
fn drop(&mut self) {
(self.on_drop)(self.started.elapsed());
}
}