#![allow(dead_code)]
use chrono::{DateTime, Duration, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScheduledTask {
pub name: String,
pub schedule: Schedule,
pub next_run: DateTime<Utc>,
pub last_run: Option<DateTime<Utc>>,
pub enabled: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Schedule {
Interval { seconds: u64 },
Daily { hour: u8, minute: u8 },
}
impl ScheduledTask {
pub fn interval(name: &str, seconds: u64) -> Self {
Self {
name: name.to_string(),
schedule: Schedule::Interval { seconds },
next_run: Utc::now() + Duration::seconds(seconds as i64),
last_run: None,
enabled: true,
}
}
pub fn daily(name: &str, hour: u8, minute: u8) -> Self {
let now = Utc::now();
let today = now.date_naive();
let run_time = today.and_hms_opt(hour as u32, minute as u32, 0)
.unwrap_or_else(|| today.and_hms_opt(0, 0, 0).unwrap());
let next_run = if run_time.and_utc() > now {
run_time.and_utc()
} else {
(run_time + Duration::days(1)).and_utc()
};
Self {
name: name.to_string(),
schedule: Schedule::Daily { hour, minute },
next_run,
last_run: None,
enabled: true,
}
}
pub fn is_due(&self) -> bool {
self.enabled && Utc::now() >= self.next_run
}
pub fn mark_run(&mut self) {
self.last_run = Some(Utc::now());
self.next_run = self.calculate_next_run();
}
fn calculate_next_run(&self) -> DateTime<Utc> {
match &self.schedule {
Schedule::Interval { seconds } => Utc::now() + Duration::seconds(*seconds as i64),
Schedule::Daily { hour, minute } => {
let now = Utc::now();
let today = now.date_naive();
let run_time = today.and_hms_opt(*hour as u32, *minute as u32, 0)
.unwrap_or_else(|| today.and_hms_opt(0, 0, 0).unwrap());
if run_time.and_utc() > now {
run_time.and_utc()
} else {
(run_time + Duration::days(1)).and_utc()
}
}
}
}
}
pub struct Scheduler {
tasks: Vec<ScheduledTask>,
}
impl Scheduler {
pub fn new() -> Self {
Self { tasks: Vec::new() }
}
pub fn add_task(&mut self, task: ScheduledTask) {
self.tasks.push(task);
}
pub fn due_tasks(&self) -> Vec<&ScheduledTask> {
self.tasks.iter().filter(|t| t.is_due()).collect()
}
pub fn mark_task_run(&mut self, name: &str) {
if let Some(task) = self.tasks.iter_mut().find(|t| t.name == name) {
task.mark_run();
}
}
pub fn time_until_next(&self) -> Option<std::time::Duration> {
self.tasks
.iter()
.filter(|t| t.enabled)
.map(|t| t.next_run)
.min()
.map(|next| {
let now = Utc::now();
if next > now {
(next - now).to_std().unwrap_or(std::time::Duration::ZERO)
} else {
std::time::Duration::ZERO
}
})
}
}
impl Default for Scheduler {
fn default() -> Self {
Self::new()
}
}