use crate::workflow::runtime::timer::{TimerId, TimerWorkflow};
use std::{cell::RefCell, future::Future, rc::Rc, thread::LocalKey, time::Duration};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TimerHandle(TimerId);
pub type TimerSlot = LocalKey<RefCell<Option<TimerHandle>>>;
pub struct TimerApi;
impl TimerApi {
pub fn set_lifecycle_timer(
delay: Duration,
label: impl Into<String>,
task: impl Future<Output = ()> + 'static,
) -> TimerHandle {
TimerHandle(TimerWorkflow::set(delay, label, task))
}
pub fn set_guarded(
slot: &'static TimerSlot,
delay: Duration,
label: impl Into<String>,
task: impl Future<Output = ()> + 'static,
) -> bool {
slot.with_borrow_mut(|entry| {
if entry.is_some() {
return false;
}
let id = TimerWorkflow::set(delay, label, task);
*entry = Some(TimerHandle(id));
true
})
}
pub fn set_interval<F, Fut>(
interval: Duration,
label: impl Into<String>,
task: F,
) -> TimerHandle
where
F: FnMut() -> Fut + 'static,
Fut: Future<Output = ()> + 'static,
{
TimerHandle(TimerWorkflow::set_interval(interval, label, task))
}
pub fn set_guarded_interval<FInit, InitFut, FTick, TickFut>(
slot: &'static TimerSlot,
init_delay: Duration,
init_label: impl Into<String>,
init_task: FInit,
interval: Duration,
interval_label: impl Into<String>,
tick_task: FTick,
) -> bool
where
FInit: FnOnce() -> InitFut + 'static,
InitFut: Future<Output = ()> + 'static,
FTick: FnMut() -> TickFut + 'static,
TickFut: Future<Output = ()> + 'static,
{
let init_label = init_label.into();
let interval_label = interval_label.into();
slot.with_borrow_mut(|entry| {
if entry.is_some() {
return false;
}
let init_id_cell = Rc::new(RefCell::new(None));
let init_id_cell_task = Rc::clone(&init_id_cell);
let init_id = TimerWorkflow::set(init_delay, init_label, async move {
init_task().await;
let init_id = init_id_cell_task.borrow();
let Some(init_id) = init_id.as_ref() else {
return;
};
let still_armed = slot.with_borrow(|slot_val| slot_val.as_ref() == Some(init_id));
if !still_armed {
return;
}
let interval_id = TimerWorkflow::set_interval(interval, interval_label, tick_task);
let interval_handle = TimerHandle(interval_id);
slot.with_borrow_mut(|slot_val| {
let old = slot_val.replace(interval_handle);
if let Some(old_id) = old
&& old_id != interval_handle
{
TimerWorkflow::clear(old_id.0);
}
});
});
let init_handle = TimerHandle(init_id);
*init_id_cell.borrow_mut() = Some(init_handle);
*entry = Some(init_handle);
true
})
}
}