use ayun_core::{BoxFuture, Closure};
use std::{str::FromStr, sync::Arc};
#[derive(Clone)]
pub enum Handler {
Sync(Closure),
Async(Closure<BoxFuture<'static, ()>>),
}
impl Handler {
pub fn is_async(&self) -> bool {
match self {
Handler::Sync(_) => false,
Handler::Async(_) => true,
}
}
}
#[derive(Clone)]
pub struct Task {
schedule: cron::Schedule,
handler: Handler,
last_tick: Option<chrono::DateTime<chrono::Local>>,
}
impl Task {
fn new(schedule: cron::Schedule, handler: Handler) -> Self {
Self {
schedule,
handler,
last_tick: None,
}
}
pub fn background<F>(expression: &str, closure: F) -> Self
where
F: Fn() -> BoxFuture<'static> + 'static + Send + Sync,
{
Self::new(
cron::Schedule::from_str(expression).unwrap_or_else(|err| panic!("{err}")),
Handler::Async(Arc::new(closure)),
)
}
pub fn foreground<F>(expression: &str, closure: F) -> Self
where
F: Fn() + 'static + Send + Sync,
{
Self::new(
cron::Schedule::from_str(expression).unwrap_or_else(|err| panic!("{err}")),
Handler::Sync(Arc::new(closure)),
)
}
pub fn available(&self) -> bool {
let now = chrono::Local::now();
let mut available = false;
if let Some(last_tick) = self.last_tick {
for event in self.schedule.after(&last_tick) {
if event > now {
break;
}
available = true;
}
}
available || self.last_tick.is_none()
}
pub fn set_last_tick(&mut self, datetime: chrono::DateTime<chrono::Local>) {
self.last_tick = Some(datetime);
}
pub fn handler(&self) -> &Handler {
&self.handler
}
pub fn delay(&self) -> std::time::Duration {
let now = chrono::Local::now();
let mut duration = chrono::Duration::zero();
for event in self.schedule.after(&now).take(1) {
let interval = event - now;
if duration.is_zero() || interval < duration {
duration = interval;
}
}
duration
.to_std()
.unwrap_or(std::time::Duration::from_millis(500))
}
}