use std::collections::BTreeMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum TimerKind {
OneShot,
Repeating,
}
#[derive(Debug, Clone)]
pub(crate) struct Timer {
pub(crate) id: i32,
pub(crate) kind: TimerKind,
pub(crate) script: String,
pub(crate) interval: u64,
pub(crate) runs: bool,
pub(crate) due: u64,
pub(crate) processing: bool,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct Timers {
armed: BTreeMap<i32, Timer>,
elapsed: u64,
last_id: i32,
fail_next: bool,
}
impl Timers {
pub(crate) fn set(&mut self, kind: TimerKind, script: String, interval: i32) -> i32 {
if std::mem::take(&mut self.fail_next) {
return 0;
}
let runs = kind == TimerKind::Repeating || interval > 0;
let interval = u64::try_from(interval)
.unwrap_or(u64::from(u32::MAX))
.max(1);
self.last_id = self.last_id.saturating_add(1);
let id = self.last_id;
self.armed.insert(
id,
Timer {
id,
kind,
script,
interval,
runs,
due: self.elapsed.saturating_add(interval),
processing: false,
},
);
id
}
pub(crate) fn cancel(&mut self, id: i32) {
self.armed.remove(&id);
}
pub(crate) fn fail_next(&mut self) {
self.fail_next = true;
}
pub(crate) fn listed(&self) -> Vec<(String, i32)> {
self.armed
.values()
.map(|timer| {
(
timer.script.clone(),
i32::try_from(timer.interval).unwrap_or(i32::MAX),
)
})
.collect()
}
pub(crate) fn advance(&mut self, millis: u64) -> Vec<(i32, String)> {
self.elapsed = self.elapsed.saturating_add(millis);
let now = self.elapsed;
let mut due: Vec<(u64, i32)> = self
.armed
.values()
.filter(|timer| timer.due <= now && !timer.processing)
.map(|timer| (timer.due, timer.id))
.collect();
due.sort_unstable();
let mut ready = Vec::new();
for (_, id) in due {
let Some(timer) = self.armed.get_mut(&id) else {
continue;
};
let runs = timer.runs;
let one_shot = timer.kind == TimerKind::OneShot;
let script = timer.script.clone();
if one_shot {
self.armed.remove(&id);
} else {
timer.due = now.saturating_add(timer.interval);
}
if runs {
ready.push((id, script));
}
}
ready
}
pub(crate) fn begin(&mut self, id: i32) {
if let Some(timer) = self.armed.get_mut(&id) {
timer.processing = true;
}
}
pub(crate) fn end(&mut self, id: i32) {
if let Some(timer) = self.armed.get_mut(&id) {
timer.processing = false;
}
}
}