1use dashmap::DashMap;
16use std::future::Future;
17use std::pin::Pin;
18use std::sync::Arc;
19use std::sync::LazyLock;
20use tibba_error::Error;
21use tracing::info;
22
23const LOG_TARGET: &str = "tibba:hook";
26
27type Result<T> = std::result::Result<T, Error>;
28
29pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
31
32pub trait Task {
38 fn before(&self) -> BoxFuture<'_, Result<bool>> {
40 Box::pin(async { Ok(false) })
41 }
42 fn after(&self) -> BoxFuture<'_, Result<bool>> {
44 Box::pin(async { Ok(false) })
45 }
46 fn priority(&self) -> u8 {
48 0
49 }
50}
51
52static TASKS: LazyLock<DashMap<String, Arc<dyn Task + Send + Sync>>> = LazyLock::new(DashMap::new);
54
55#[derive(Clone, Copy)]
57enum TaskType {
58 Before,
59 After,
60}
61
62impl std::fmt::Display for TaskType {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 match self {
65 TaskType::Before => write!(f, "before"),
66 TaskType::After => write!(f, "after"),
67 }
68 }
69}
70
71async fn run_tasks(task_type: TaskType) -> Result<()> {
75 let mut executable_tasks: Vec<_> = TASKS
76 .iter()
77 .map(|item| {
78 (
79 item.key().clone(), item.value().priority(), item.value().clone(), )
83 })
84 .collect();
85
86 match task_type {
87 TaskType::Before => {
88 executable_tasks.sort_by_key(|k| k.1);
90 }
91 TaskType::After => {
92 executable_tasks.sort_by_key(|k| std::cmp::Reverse(k.1));
94 }
95 }
96
97 for (name, _, task) in executable_tasks {
98 let start = std::time::Instant::now();
99 let executed = match task_type {
100 TaskType::Before => task.before().await?,
101 TaskType::After => task.after().await?,
102 };
103
104 if executed {
105 info!(
106 target: LOG_TARGET,
107 task_type = %task_type,
108 name,
109 elapsed = start.elapsed().as_millis(),
110 );
111 }
112 }
113
114 Ok(())
115}
116
117pub fn register_task(name: &str, task: Arc<dyn Task + Send + Sync>) {
119 TASKS.insert(name.to_string(), task);
120}
121
122pub async fn run_before_tasks() -> Result<()> {
124 run_tasks(TaskType::Before).await
125}
126
127pub async fn run_after_tasks() -> Result<()> {
129 run_tasks(TaskType::After).await
130}