extern crate chrono;
extern crate cron;
use crate::errors::TaskResult;
use crate::scheduler_log;
use crate::task::Task;
use chrono::prelude::*;
use chrono::DateTime;
use cron::Schedule;
pub struct TaskGenerator<T>
where
T: TimeZone + Send + 'static,
{
discovery_function: Box<dyn FnMut() -> Option<TaskResult<Task<T>>>>,
schedule: Schedule,
timezone: T,
pub(crate) next_exec: DateTime<T>,
}
impl<T> TaskGenerator<T>
where
T: TimeZone + Clone + Send + 'static,
{
pub fn new<F>(expression: &str, timezone: T, function: F) -> TaskGenerator<T>
where
F: (FnMut() -> Option<TaskResult<Task<T>>>) + 'static,
{
let schedule: Schedule = expression.parse().unwrap();
TaskGenerator {
discovery_function: Box::new(function),
schedule: expression.parse().unwrap(),
timezone: timezone.clone(),
next_exec: schedule.upcoming(timezone).next().unwrap(),
}
}
pub(crate) fn run(&mut self) -> Option<TaskResult<Task<T>>> {
scheduler_log!(log::Level::Debug, "Executing task discovery function");
self.next_exec = self.schedule.upcoming(self.timezone.clone()).next()?;
match (self.discovery_function)() {
Some(t) => {
scheduler_log!(log::Level::Debug, "Task discovered, adding to queue");
Some(t)
}
None => {
scheduler_log!(log::Level::Debug, "No tasks were generated");
None
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
use chrono::Local;
#[test]
fn test_task_generation_with_result() {
let mut task_gen = TaskGenerator::new("* * * * * * *", Local, || {
Some(Task::new("* * * * * * *", None, Some(1), Local))
});
assert!(task_gen.run().is_some());
}
#[test]
fn test_task_generation_without_result() {
let mut task_gen = TaskGenerator::new("* * * * * * *", Local, || None);
assert!(task_gen.run().is_none());
}
#[test]
fn test_task_generator_scheduling() {
use chrono::Duration;
let mut generator = TaskGenerator::new("* * * * * * *", Utc, || {
Some(Task::new("* * * * * * *", None, None, Utc))
});
let now = Utc::now();
let expected_window_end = now + Duration::seconds(1);
assert!(generator.next_exec >= now);
assert!(generator.next_exec <= expected_window_end);
let _ = generator.run();
assert!(generator.next_exec > now);
let second_execution_window_end = generator.next_exec + Duration::seconds(1);
assert!(generator.next_exec <= second_execution_window_end);
}
}