pub mod cron;
pub use cron::CronSchedule;
#[cfg(test)]
mod tests;
use std::sync::Arc;
use std::time::{Duration, Instant};
pub struct Scheduler {
tasks: Vec<Task>,
}
struct Task {
kind: TaskKind,
initial_delay: Duration,
f: Arc<dyn Fn() + Send + Sync + 'static>,
}
enum TaskKind {
FixedRate(Duration),
FixedDelay(Duration),
Cron(CronSchedule),
}
impl Scheduler {
pub fn new() -> Self {
Scheduler { tasks: Vec::new() }
}
pub fn every(mut self, interval: Duration, task: impl Fn() + Send + Sync + 'static) -> Self {
self.tasks.push(Task {
kind: TaskKind::FixedRate(interval),
initial_delay: Duration::ZERO,
f: Arc::new(task),
});
self
}
pub fn after(mut self, delay: Duration, task: impl Fn() + Send + Sync + 'static) -> Self {
self.tasks.push(Task {
kind: TaskKind::FixedDelay(delay),
initial_delay: Duration::ZERO,
f: Arc::new(task),
});
self
}
pub fn cron(
mut self,
expr: &str,
task: impl Fn() + Send + Sync + 'static,
) -> Result<Self, String> {
let schedule = CronSchedule::parse(expr)?;
self.tasks.push(Task {
kind: TaskKind::Cron(schedule),
initial_delay: Duration::ZERO,
f: Arc::new(task),
});
Ok(self)
}
pub fn initial_delay(mut self, delay: Duration) -> Self {
if let Some(t) = self.tasks.last_mut() {
t.initial_delay = delay;
}
self
}
pub fn start(self) {
for task in self.tasks {
let f = task.f.clone();
let initial_delay = task.initial_delay;
std::thread::spawn(move || {
if !initial_delay.is_zero() {
std::thread::sleep(initial_delay);
}
match task.kind {
TaskKind::FixedRate(interval) => loop {
let start = Instant::now();
f();
let elapsed = start.elapsed();
if elapsed < interval {
std::thread::sleep(interval - elapsed);
}
},
TaskKind::FixedDelay(delay) => loop {
f();
std::thread::sleep(delay);
},
TaskKind::Cron(ref schedule) => {
let mut last_fired_secs: u64 = 0;
loop {
let now_secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
if now_secs != last_fired_secs
&& schedule.matches_epoch(now_secs)
{
last_fired_secs = now_secs;
f();
}
std::thread::sleep(Duration::from_millis(200));
}
}
}
});
}
}
}
impl Default for Scheduler {
fn default() -> Self {
Scheduler::new()
}
}