use std::time::{Duration, Instant};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Edge {
#[default]
Trailing,
Leading,
Both,
}
#[derive(Clone, Debug)]
pub struct Debouncer {
delay: Duration,
last_call: Option<Instant>,
edge: Edge,
leading_fired: bool,
pending: bool,
}
impl Debouncer {
pub fn new(delay: Duration) -> Self {
Self {
delay,
last_call: None,
edge: Edge::Trailing,
leading_fired: false,
pending: false,
}
}
pub fn leading(delay: Duration) -> Self {
Self {
delay,
last_call: None,
edge: Edge::Leading,
leading_fired: false,
pending: false,
}
}
pub fn both_edges(delay: Duration) -> Self {
Self {
delay,
last_call: None,
edge: Edge::Both,
leading_fired: false,
pending: false,
}
}
pub fn with_edge(mut self, edge: Edge) -> Self {
self.edge = edge;
self
}
pub fn call(&mut self) -> bool {
let now = Instant::now();
match self.edge {
Edge::Trailing => {
self.pending = true;
self.last_call = Some(now);
false
}
Edge::Leading => {
if !self.leading_fired {
self.leading_fired = true;
self.last_call = Some(now);
true
} else {
self.last_call = Some(now);
false
}
}
Edge::Both => {
self.pending = true;
if !self.leading_fired {
self.leading_fired = true;
self.last_call = Some(now);
true
} else {
self.last_call = Some(now);
false
}
}
}
}
pub fn is_ready(&mut self) -> bool {
if !self.pending {
return false;
}
if let Some(last) = self.last_call {
if last.elapsed() >= self.delay {
self.pending = false;
self.leading_fired = false;
self.last_call = None;
matches!(self.edge, Edge::Trailing | Edge::Both)
} else {
false
}
} else {
false
}
}
pub fn is_pending(&self) -> bool {
self.pending
}
pub fn cancel(&mut self) {
self.pending = false;
self.last_call = None;
self.leading_fired = false;
}
pub fn reset(&mut self) {
self.cancel();
}
pub fn remaining(&self) -> Option<Duration> {
if !self.pending {
return None;
}
self.last_call.map(|last| {
let elapsed = last.elapsed();
if elapsed >= self.delay {
Duration::ZERO
} else {
self.delay - elapsed
}
})
}
pub fn delay(&self) -> Duration {
self.delay
}
pub fn set_delay(&mut self, delay: Duration) {
self.delay = delay;
}
}
impl Default for Debouncer {
fn default() -> Self {
Self::new(Duration::from_millis(300))
}
}
#[derive(Clone, Debug)]
pub struct Throttle {
interval: Duration,
last_exec: Option<Instant>,
edge: Edge,
pending: bool,
}
impl Throttle {
pub fn new(interval: Duration) -> Self {
Self {
interval,
last_exec: None,
edge: Edge::Leading,
pending: false,
}
}
pub fn trailing(interval: Duration) -> Self {
Self {
interval,
last_exec: None,
edge: Edge::Trailing,
pending: false,
}
}
pub fn both_edges(interval: Duration) -> Self {
Self {
interval,
last_exec: None,
edge: Edge::Both,
pending: false,
}
}
pub fn with_edge(mut self, edge: Edge) -> Self {
self.edge = edge;
self
}
pub fn call(&mut self) -> bool {
let now = Instant::now();
let can_execute = match self.last_exec {
None => true,
Some(last) => last.elapsed() >= self.interval,
};
match self.edge {
Edge::Leading => {
if can_execute {
self.last_exec = Some(now);
self.pending = false;
true
} else {
false
}
}
Edge::Trailing => {
self.pending = true;
false
}
Edge::Both => {
if can_execute {
self.last_exec = Some(now);
self.pending = false;
true
} else {
self.pending = true;
false
}
}
}
}
pub fn is_ready(&mut self) -> bool {
if !self.pending {
return false;
}
let can_execute = match self.last_exec {
None => true,
Some(last) => last.elapsed() >= self.interval,
};
if can_execute && matches!(self.edge, Edge::Trailing | Edge::Both) {
self.last_exec = Some(Instant::now());
self.pending = false;
true
} else {
false
}
}
pub fn is_pending(&self) -> bool {
self.pending
}
pub fn cancel(&mut self) {
self.pending = false;
}
pub fn reset(&mut self) {
self.last_exec = None;
self.pending = false;
}
pub fn remaining(&self) -> Duration {
match self.last_exec {
None => Duration::ZERO,
Some(last) => {
let elapsed = last.elapsed();
if elapsed >= self.interval {
Duration::ZERO
} else {
self.interval - elapsed
}
}
}
}
pub fn interval(&self) -> Duration {
self.interval
}
pub fn set_interval(&mut self, interval: Duration) {
self.interval = interval;
}
}
impl Default for Throttle {
fn default() -> Self {
Self::new(Duration::from_millis(100))
}
}
pub fn debouncer(delay: Duration) -> Debouncer {
Debouncer::new(delay)
}
pub fn debounce_ms(ms: u64) -> Debouncer {
Debouncer::new(Duration::from_millis(ms))
}
pub fn throttle(interval: Duration) -> Throttle {
Throttle::new(interval)
}
pub fn throttle_ms(ms: u64) -> Throttle {
Throttle::new(Duration::from_millis(ms))
}