1use std::collections::HashMap;
2use std::sync::Mutex;
3use lazy_static::lazy_static;
4use std::thread;
5use std::time::Duration;
6use chrono::prelude::*;
7use colored::*;
8use cron_parser::parse;
9
10lazy_static! {
11 pub static ref CRON_REGISTRY: Mutex<CronRegistry> = Mutex::new(CronRegistry::new());
12}
13
14pub struct CronRegistry {
15 tasks: HashMap<String, (String, fn())>,
16}
17
18impl CronRegistry {
19 pub fn new() -> Self {
20 CronRegistry {
21 tasks: HashMap::new(),
22 }
23 }
24
25 pub fn register(&mut self, name: String, cron_expression: String, task: fn()) {
26 if let Ok(_) = parse(&cron_expression, &Local::now()) {
27 self.tasks.insert(name.clone(), (cron_expression.clone(), task));
28 self.log_next_run(&name, &cron_expression);
29 } else {
30 println!("Invalid cron expression for {}: {}", name.bright_red(), cron_expression.bright_red());
31 }
32 }
33
34 fn log_next_run(&self, name: &str, cron_expression: &str) {
35 println!("Cron expression for {}: {}", name.bright_yellow(), cron_expression);
36 if let Ok(next_run) = parse(cron_expression, &Local::now()) {
37 let now = Local::now();
38 let duration = next_run - now;
39 let hours = duration.num_hours();
40 let minutes = duration.num_minutes() % 60;
41 println!(
42 "{}\n{}\n{}\n{}\n{}\n{}",
43 "+---------------------------------+".bright_blue(),
44 format!("| Current time: {}", now.format("%Y-%m-%d %H:%M:%S")).bright_red(),
45 format!("| Function: {}", name).bright_green(),
46 format!("| Frequency: {}", cron_expression).bright_yellow(),
47 format!("| Next run in: {} hours, {} minutes", hours, minutes).bright_red(),
48 "+---------------------------------+".bright_blue()
49 );
50 } else {
51 println!("Failed to calculate the next run time for {} with expression {}", name.bright_red(), cron_expression.bright_red());
52 }
53 }
54
55 pub fn get_tasks(&self) -> &HashMap<String, (String, fn())> {
56 &self.tasks
57 }
58}
59
60pub fn cron_runtime() {
61 thread::spawn(|| {
62 loop {
63 let tasks: HashMap<String, (String, fn())> = CRON_REGISTRY.lock().unwrap().get_tasks().clone();
64 let now = Local::now();
65
66 for (name, (cron_expression, task)) in tasks {
67 if let Ok(next_run) = parse(&cron_expression, &now.with_timezone(&Utc)) {
68 let next_run = next_run.with_timezone(&Local);
69
70 println!(
71 "Next run for {}: {}",
72 name.bright_cyan().bold(),
73 next_run.format("%Y-%m-%d %H:%M:%S").to_string().bold()
74 );
75
76 if next_run <= now + chrono::Duration::seconds(60) {
77 println!("Running task: {}", name.bright_green());
78 task();
79 } else {
80 println!("Task {} not due yet. Next run: {}", name.bright_yellow(), next_run.to_rfc3339());
81 }
82 } else {
83 println!("No upcoming run for {}", name.bright_red());
84 }
85 }
86
87 thread::sleep(Duration::from_secs(60));
88 }
89 });
90}