///|
/// A runner that keeps only the newest pending item: pushing while the consumer is
/// busy replaces what it will take next, so a burst collapses to its last value.
///
/// A service uses it for work that is idempotent and worth doing only once — a
/// configuration reload, a cache refresh, a re-index triggered by a write.
pub struct StableRunner[T] {
  queue : @aqueue.Queue[T]
}

///|
/// A stable runner with nothing pending. The queue holds one item and drops the
/// oldest when a newer one arrives, so a producer never waits and never queues more
/// than one turn of work.
pub fn[T] StableRunner::new() -> StableRunner[T] {
  { queue: @aqueue.Queue::Queue(kind=DiscardOldest(1)), }
}

///|
/// Make `item` the pending one, replacing whatever was pending. The queue holds one
/// item and discards the older one, so this never waits for the consumer.
pub async fn[T] StableRunner::push(self : StableRunner[T], item : T) -> Unit {
  self.queue.put(item)
}

///|
/// Take the pending item and hand it to `handle`, one at a time, until the runner is
/// closed.
pub async fn[T] StableRunner::run(
  self : StableRunner[T],
  handle : async (T) -> Unit,
) -> Unit {
  while true {
    let item = Some(self.queue.get()) catch { _ => None }
    match item {
      Some(item) => handle(item)
      None => return
    }
  }
}

///|
/// Stop accepting work and wake the consumer so it can finish.
pub fn[T] StableRunner::close(self : StableRunner[T]) -> Unit {
  self.queue.close()
}