use dashmap::DashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::LazyLock;
use tibba_error::Error;
use tracing::info;
const LOG_TARGET: &str = "tibba:hook";
type Result<T> = std::result::Result<T, Error>;
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait Task {
fn before(&self) -> BoxFuture<'_, Result<bool>> {
Box::pin(async { Ok(false) })
}
fn after(&self) -> BoxFuture<'_, Result<bool>> {
Box::pin(async { Ok(false) })
}
fn priority(&self) -> u8 {
0
}
}
static TASKS: LazyLock<DashMap<String, Arc<dyn Task + Send + Sync>>> = LazyLock::new(DashMap::new);
#[derive(Clone, Copy)]
enum TaskType {
Before,
After,
}
impl std::fmt::Display for TaskType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TaskType::Before => write!(f, "before"),
TaskType::After => write!(f, "after"),
}
}
}
async fn run_tasks(task_type: TaskType) -> Result<()> {
let mut executable_tasks: Vec<_> = TASKS
.iter()
.map(|item| {
(
item.key().clone(), item.value().priority(), item.value().clone(), )
})
.collect();
match task_type {
TaskType::Before => {
executable_tasks.sort_by_key(|k| k.1);
}
TaskType::After => {
executable_tasks.sort_by_key(|k| std::cmp::Reverse(k.1));
}
}
for (name, _, task) in executable_tasks {
let start = std::time::Instant::now();
let executed = match task_type {
TaskType::Before => task.before().await?,
TaskType::After => task.after().await?,
};
if executed {
info!(
target: LOG_TARGET,
task_type = %task_type,
name,
elapsed = start.elapsed().as_millis(),
);
}
}
Ok(())
}
pub fn register_task(name: &str, task: Arc<dyn Task + Send + Sync>) {
TASKS.insert(name.to_string(), task);
}
pub async fn run_before_tasks() -> Result<()> {
run_tasks(TaskType::Before).await
}
pub async fn run_after_tasks() -> Result<()> {
run_tasks(TaskType::After).await
}