hookable 0.1.1

A thread-safe hook system that allows registering and executing sync and async hooks.
Documentation
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(())
    }
}