use std::{fmt, hash::Hash, time::Duration};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TimerReturn<S> {
Reschedule(S),
Cancel,
}
impl<S> TimerReturn<S> {
pub fn should_reschedule(&self) -> bool {
match self {
TimerReturn::Reschedule(_) => true,
TimerReturn::Cancel => false,
}
}
pub fn map<T, F>(self, f: F) -> TimerReturn<T>
where
F: FnOnce(S) -> T,
{
match self {
TimerReturn::Reschedule(s) => TimerReturn::Reschedule(f(s)),
TimerReturn::Cancel => TimerReturn::Cancel,
}
}
}
pub trait OneshotState {
type Id: Hash + Clone + Eq;
fn id(&self) -> &Self::Id;
fn trigger(self);
}
pub trait PeriodicState {
type Id: Hash + Clone + Eq;
fn id(&self) -> &Self::Id;
fn trigger(self) -> TimerReturn<Self>
where
Self: Sized;
}
#[derive(Debug)]
pub enum TimerEntry<I, O, P>
where
I: Hash + Clone + Eq,
O: OneshotState<Id = I>,
P: PeriodicState<Id = I>,
{
OneShot {
timeout: Duration,
state: O,
},
Periodic {
delay: Duration,
period: Duration,
state: P,
},
}
impl<I, O, P> TimerEntry<I, O, P>
where
I: Hash + Clone + Eq,
O: OneshotState<Id = I>,
P: PeriodicState<Id = I>,
{
pub fn id(&self) -> &I {
match self {
TimerEntry::OneShot { state, .. } => state.id(),
TimerEntry::Periodic { state, .. } => state.id(),
}
}
pub fn delay(&self) -> &Duration {
match self {
TimerEntry::OneShot { timeout, .. } => timeout,
TimerEntry::Periodic { delay, .. } => delay,
}
}
}
pub trait Timer {
type Id: Hash + Clone + Eq;
type OneshotState: OneshotState<Id = Self::Id>;
type PeriodicState: PeriodicState<Id = Self::Id>;
fn schedule_once(&mut self, timeout: Duration, state: Self::OneshotState);
fn schedule_periodic(&mut self, delay: Duration, period: Duration, state: Self::PeriodicState);
fn cancel(&mut self, id: &Self::Id);
}
pub struct OneShotClosureState<I> {
id: I,
action: Box<dyn FnOnce(I) + Send + 'static>,
}
impl<I> OneShotClosureState<I> {
pub fn new<F>(id: I, action: F) -> Self
where
F: FnOnce(I) + Send + 'static,
{
OneShotClosureState {
id,
action: Box::new(action),
}
}
}
#[cfg(feature = "uuid-extras")]
impl OneShotClosureState<uuid::Uuid> {
pub fn with_random_id<F>(action: F) -> Self
where
F: FnOnce(uuid::Uuid) + Send + 'static,
{
Self::new(uuid::Uuid::new_v4(), action)
}
}
impl<I> OneshotState for OneShotClosureState<I>
where
I: Hash + Clone + Eq,
{
type Id = I;
fn id(&self) -> &Self::Id {
&self.id
}
fn trigger(self) {
(self.action)(self.id)
}
}
impl<I> fmt::Debug for OneShotClosureState<I>
where
I: Hash + Clone + Eq + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"OneShotClosureState(id={:?}, action=<function>)",
self.id
)
}
}
pub struct PeriodicClosureState<I> {
id: I,
action: Box<dyn FnMut(I) -> TimerReturn<()> + Send + 'static>,
}
impl<I> PeriodicClosureState<I> {
pub fn new<F>(id: I, action: F) -> Self
where
F: FnMut(I) -> TimerReturn<()> + Send + 'static,
{
PeriodicClosureState {
id,
action: Box::new(action),
}
}
}
#[cfg(feature = "uuid-extras")]
impl PeriodicClosureState<uuid::Uuid> {
pub fn with_random_id<F>(action: F) -> Self
where
F: FnMut(uuid::Uuid) -> TimerReturn<()> + Send + 'static,
{
Self::new(uuid::Uuid::new_v4(), action)
}
}
impl<I> PeriodicState for PeriodicClosureState<I>
where
I: Hash + Clone + Eq,
{
type Id = I;
fn id(&self) -> &Self::Id {
&self.id
}
fn trigger(mut self) -> TimerReturn<Self>
where
Self: Sized,
{
let res = (self.action)(self.id.clone());
res.map(|_| self)
}
}
impl<I> fmt::Debug for PeriodicClosureState<I>
where
I: Hash + Clone + Eq + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"PeriodicClosureState(id={:?}, action=<function>)",
self.id
)
}
}
pub trait ClosureTimer: Timer {
fn schedule_action_once<F>(&mut self, id: Self::Id, timeout: Duration, action: F)
where
F: FnOnce(Self::Id) + Send + 'static;
fn schedule_action_periodic<F>(
&mut self,
id: Self::Id,
delay: Duration,
period: Duration,
action: F,
) where
F: FnMut(Self::Id) -> TimerReturn<()> + Send + 'static;
}
impl<I, T> ClosureTimer for T
where
I: Hash + Clone + Eq,
T: Timer<
Id = I,
OneshotState = OneShotClosureState<I>,
PeriodicState = PeriodicClosureState<I>,
>,
{
fn schedule_action_once<F>(&mut self, id: Self::Id, timeout: Duration, action: F)
where
F: FnOnce(Self::Id) + Send + 'static,
{
self.schedule_once(timeout, OneShotClosureState::new(id, action))
}
fn schedule_action_periodic<F>(
&mut self,
id: Self::Id,
delay: Duration,
period: Duration,
action: F,
) where
F: FnMut(Self::Id) -> TimerReturn<()> + Send + 'static,
{
self.schedule_periodic(delay, period, PeriodicClosureState::new(id, action))
}
}