use chrono::{DateTime, Duration, Local};
pub use cron::Schedule;
pub use uuid::Uuid;
pub struct Job<'a> {
schedule: Schedule,
run: Box<dyn (FnMut() -> ()) + 'a>,
last_tick: Option<DateTime<Local>>,
limit_missed_runs: usize,
job_id: Uuid,
}
impl<'a> Job<'a> {
pub fn new<T>(schedule: Schedule, run: T) -> Job<'a>
where
T: 'a,
T: FnMut() -> (),
{
Job {
schedule,
run: Box::new(run),
last_tick: None,
limit_missed_runs: 1,
job_id: Uuid::new_v4(),
}
}
fn tick(&mut self) {
let now = Local::now();
if self.last_tick.is_none() {
self.last_tick = Some(now);
return;
}
if self.limit_missed_runs > 0 {
self.schedule
.after(&self.last_tick.unwrap())
.take(self.limit_missed_runs)
.take_while(|&event| event <= now)
.for_each(|_| (self.run)());
} else {
self.schedule
.after(&self.last_tick.unwrap())
.take_while(|&event| event <= now)
.for_each(|_| (self.run)());
}
self.last_tick = Some(now);
}
pub fn limit_missed_runs(&mut self, limit: usize) {
self.limit_missed_runs = limit;
}
pub fn last_tick(&mut self, last_tick: Option<DateTime<Local>>) {
self.last_tick = last_tick;
}
}
#[derive(Default)]
pub struct JobScheduler<'a> {
jobs: Vec<Job<'a>>,
}
impl<'a> JobScheduler<'a> {
pub fn new() -> JobScheduler<'a> {
JobScheduler { jobs: Vec::new() }
}
pub fn add(&mut self, job: Job<'a>) -> Uuid {
let job_id = job.job_id;
self.jobs.push(job);
job_id
}
pub fn remove(&mut self, job_id: Uuid) -> bool {
let mut found_index = None;
for (i, job) in self.jobs.iter().enumerate() {
if job.job_id == job_id {
found_index = Some(i);
break;
}
}
if found_index.is_some() {
self.jobs.remove(found_index.unwrap());
}
found_index.is_some()
}
pub fn tick(&mut self) {
for job in &mut self.jobs {
job.tick();
}
}
pub fn time_till_next_job(&self) -> std::time::Duration {
if self.jobs.is_empty() {
return std::time::Duration::from_millis(500);
}
let mut duration = Duration::zero();
let now = Local::now();
for job in self.jobs.iter() {
for event in job.schedule.upcoming(Local).take(1) {
let d = event - now;
if duration.is_zero() || d < duration {
duration = d;
}
}
}
duration.to_std().unwrap()
}
}
#[cfg(test)]
mod test {
#[test]
fn it_works() {
println!("rcron");
}
#[test]
fn test_run() {
use super::Job;
use super::JobScheduler;
use std::time::Duration;
let mut sched = JobScheduler::new();
sched.add(Job::new("1/2 * * * * *".parse().unwrap(), || {
println!("I get executed every 2 seconds!");
}));
loop {
sched.tick();
std::thread::sleep(Duration::from_millis(500));
}
}
}