///|
/// Mirrors the JS glob-watcher running/queued state machine.
/// Ensures the callback is not invoked concurrently and optionally
/// queues the next run while one is in progress.
struct CallbackQueue {
mut running : Bool
mut queued_event : @chokidar.Event?
queue_mode : Bool
}
///|
fn CallbackQueue::new(queue_mode : Bool) -> CallbackQueue {
{ running: false, queued_event: None, queue_mode }
}
///| Attempt to run `cb` with `event`.
/// - If idle: run immediately. After completion, run queued event if any.
/// - If busy and queue_mode=true: store event for next run.
/// - If busy and queue_mode=false: drop silently.
/// Errors from `cb` are forwarded to `on_err`; the queue continues running.
fn CallbackQueue::try_run(
self : CallbackQueue,
event : @chokidar.Event,
cb : (@chokidar.Event) -> Unit raise Error,
on_err : (String) -> Unit,
) -> Unit {
if self.running {
if self.queue_mode {
self.queued_event = Some(event)
}
return
}
self.running = true
let had_error : Array[Bool] = [false]
let error_msg : Array[String] = [""]
try {
cb(event)
} catch {
e => {
had_error[0] = true
error_msg[0] = e.to_string()
}
}
self.running = false
if had_error[0] {
on_err(error_msg[0])
}
// Drain one queued event if present.
match self.queued_event {
Some(queued) => {
self.queued_event = None
self.try_run(queued, cb, on_err)
}
None => ()
}
}
///| True if the callback is currently executing.
fn CallbackQueue::is_running(self : CallbackQueue) -> Bool {
self.running
}