use std::collections::HashMap;
use std::sync::{Arc, Mutex, MutexGuard};
use crate::errors::HookableError;
use crate::hook::Hook;
use crate::{AsyncHookTrait, SyncHookTrait};
use futures::future::BoxFuture;
#[derive(Clone)]
pub struct Hookable {
hooks: Arc<Mutex<HashMap<String, Vec<Hook>>>>,
}
impl Default for Hookable {
fn default() -> Self {
Self::new()
}
}
impl Hookable {
pub fn new() -> Self {
Self {
hooks: Arc::new(Mutex::new(HashMap::new())),
}
}
fn hooks(&self) -> MutexGuard<'_, HashMap<String, Vec<Hook>>> {
self.hooks.lock().unwrap()
}
pub fn hook<T>(&self, name: impl Into<String>, hook: T)
where
T: SyncHookTrait,
{
self.hooks()
.entry(name.into())
.or_default()
.push(Hook::Sync(Box::new(hook)));
}
pub fn hook_async<T>(&self, name: impl Into<String>, hook: T)
where
T: AsyncHookTrait,
{
self.hooks()
.entry(name.into())
.or_default()
.push(Hook::Async(Box::new(hook)));
}
pub fn call(&self, name: impl Into<String>) -> Result<(), HookableError> {
let name = name.into();
let mut hooks = self.hooks();
let Some(hooks) = hooks.get_mut(&name) else {
return Err(HookableError::NoHookFound(name));
};
for hook in hooks.iter_mut() {
match hook {
Hook::Sync(sync) => sync(),
Hook::Async(_) => return Err(HookableError::AsyncHookCalledSync(name)),
}
}
Ok(())
}
pub async fn call_async(&self, name: impl Into<String>) -> Result<(), HookableError> {
let name = name.into();
let mut hooks = self.hooks();
let Some(hooks) = hooks.get_mut(&name) else {
return Err(HookableError::NoHookFound(name));
};
for hook in hooks.iter_mut() {
match hook {
Hook::Sync(sync) => sync(),
Hook::Async(async_hook) => async_hook().await,
}
}
Ok(())
}
}