1use async_trait::async_trait;
4use alun_core::{Plugin, Result};
5use std::collections::HashMap;
6use std::sync::Arc;
7use parking_lot::RwLock;
8
9type JobFn = Box<dyn Fn() -> tokio::task::JoinHandle<()> + Send + Sync>;
10
11pub struct SchedulerPlugin {
16 jobs: Arc<RwLock<HashMap<String, ScheduledJob>>>,
18}
19
20struct ScheduledJob {
21 cron: String,
22 description: String,
23 runner: JobFn,
24}
25
26impl SchedulerPlugin {
27 pub fn new() -> Self {
29 Self { jobs: Arc::new(RwLock::new(HashMap::new())) }
30 }
31
32 pub fn register<F>(
40 &self,
41 name: &str,
42 cron: &str,
43 desc: &str,
44 runner: F,
45 ) where
46 F: Fn() -> tokio::task::JoinHandle<()> + Send + Sync + 'static,
47 {
48 self.jobs.write().insert(name.to_string(), ScheduledJob {
49 cron: cron.to_string(),
50 description: desc.to_string(),
51 runner: Box::new(runner),
52 });
53 }
54
55 pub fn remove(&self, name: &str) {
57 self.jobs.write().remove(name);
58 }
59
60 pub fn list(&self) -> Vec<(String, String, String)> {
62 self.jobs.read()
63 .iter()
64 .map(|(k, v)| (k.clone(), v.cron.clone(), v.description.clone()))
65 .collect()
66 }
67
68 pub fn trigger(&self, name: &str) -> Option<tokio::task::JoinHandle<()>> {
70 let guard = self.jobs.read();
71 guard.get(name).map(|job| (job.runner)())
72 }
73}
74
75#[async_trait]
76impl Plugin for SchedulerPlugin {
77 fn name(&self) -> &str { "scheduler" }
78
79 async fn start(&self) -> Result<()> {
80 let count = self.jobs.read().len();
81 tracing::info!("定时任务插件: {} 个任务已注册", count);
82 for (name, job) in self.jobs.read().iter() {
83 tracing::info!(" - {} [{}] {}", name, job.cron, job.description);
84 }
85 Ok(())
86 }
87
88 async fn stop(&self) -> Result<()> {
89 tracing::info!("定时任务插件: 已停止");
90 Ok(())
91 }
92}
93
94impl Default for SchedulerPlugin {
95 fn default() -> Self { Self::new() }
96}