Skip to content

Instantly share code, notes, and snippets.

@tripplet
Created July 2, 2022 10:35
Show Gist options
  • Save tripplet/84b4a6b3934c8bb9266031d3852f2aba to your computer and use it in GitHub Desktop.
Save tripplet/84b4a6b3934c8bb9266031d3852f2aba to your computer and use it in GitHub Desktop.
use std::{
sync::{Arc, Mutex},
thread,
};
use crossbeam_channel::{Receiver, Sender};
use log::{debug, error};
use notify::{Error, Event};
use timer::Timer;
pub fn debounce_channel(
rx: Receiver<Result<Event, Error>>,
delay: chrono::Duration,
) -> Receiver<Event> {
let (tx, rx_debounced) = crossbeam_channel::unbounded();
let tx = Arc::new(tx);
thread::spawn(move || debounce_thread(&rx, &tx, delay));
rx_debounced
}
fn debounce_thread(
rx: &Receiver<Result<Event, Error>>,
tx: &Arc<Sender<Event>>,
delay: chrono::Duration,
) {
let timer = Timer::new();
let debounced_events = Arc::new(Mutex::new(Vec::with_capacity(10)));
for evt in rx.iter() {
match evt {
Ok(event) => {
let event_found = debounced_events.lock().unwrap().iter().any(
|(existing_event, _): &(Event, _)| {
existing_event.paths == event.paths && existing_event.kind == event.kind
},
);
if !event_found {
// No event in the vec for the given file with the same kind
// => Schedule new timer
let moved_event = event.clone();
let moved_tx = tx.clone();
let moved_debounced_events = debounced_events.clone();
let guard = timer.schedule_with_delay(delay, move || {
// The delay for an event has expired
// Get a lock on the vec of debounced events
let mut debounced_events_write = moved_debounced_events.lock().unwrap();
// Find the event in the vec
let pos = debounced_events_write.iter().position(
|(existing_event, _): &(Event, timer::Guard)| {
existing_event.paths[0] == moved_event.paths[0]
&& existing_event.kind == moved_event.kind
},
);
// Remove the event from the vec and send it to the debounced channel
if let Some(pos) = pos {
let (debounced_event, _) = debounced_events_write.swap_remove(pos);
moved_tx.send(debounced_event).unwrap();
}
});
// Add the event to the debounced events combined with the guard
// This keeps the timer alive until the event is removed from the vec
debounced_events.lock().unwrap().push((event, guard));
} else {
// There is already an event in the vec for the given file with the same kind
// We can ignore this new event and wait for the debounce timer to expire
debug!("Ignoring event {:?} for {:?}: ", event.kind, event.paths[0]);
}
}
Err(err) => error!("{err:?}"),
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment