noema 0.3.0

Noema IOC and DI framework for Rust
Documentation
use crate::core::*;
use async_trait::async_trait;
pub use noema_macros::event;
use std::{
    pin::Pin,
    sync::{OnceLock, RwLock},
};

/// Event trait for events
/// # Examples
/// ```ignore
/// #[event(name = "MyEvent", description = "A simple event")]
/// struct MyEvent{
///     pub information: String,
/// }
/// ```
pub trait Event {
    /// Name of the event
    fn name(&self) -> String {
        std::any::type_name::<Self>().to_string()
    }

    /// Describe the event
    fn describe(&self) -> String {
        format!("Event {}", self.name())
    }
}

/// Handler of an Event type E, processing the event
#[async_trait]
pub trait EventListener<E: Event + Send + Sync + 'static> {
    /// Name of the handler
    fn name(&self) -> String {
        std::any::type_name::<Self>().to_string()
    }

    /// Handle the event, returning a Result with the error if it fails
    async fn handle(&self, event: Arc<E>) -> Result<(), BoxDynError>;

    /// Handle errors from the event handler, using the provided error handler and event
    async fn on_error(
        &self,
        backgorund_error_handler: arc_dyn!(BackgroundErrorHandler),
        error: BoxDynError,
        event: Arc<E>,
    ) {
        backgorund_error_handler
            .handle(error, event, self.name())
            .await;
    }
}

/// Spawner for background tasks, used to run event handlers in the background
pub trait BackgroundSpawner: Send + Sync {
    fn spawn(&self, fut: Pin<Box<dyn Future<Output = ()> + Send>>);
}

/// Error handler for background tasks, used to handle errors from event handlers running in the background
#[async_trait]
pub trait BackgroundErrorHandler: Send + Sync {
    async fn handle(&self, error: BoxDynError, event: arc_dyn!(Event), handler_name: String);
}

pub static __NOEMA_DISPATCHER_SPAWNER: OnceLock<arc_dyn!(BackgroundSpawner)> = OnceLock::new();
pub static __NOEMA_DISPATCHER_ERROR_HANDLER: OnceLock<arc_dyn!(BackgroundErrorHandler)> =
    OnceLock::new();
pub trait Dispatcher<E: Event + Send + Sync + 'static> {
    /// Get all event handlers for the event type E
    fn get_event_handlers(
        &self,
    ) -> Arc<Vec<Arc<dyn Fn() -> Arc<dyn EventListener<E> + Send + Sync> + Send + Sync>>>;

    /// Dispatch event to all handlers according to how and on_error parameters
    fn dispatch(&self, event: E) {
        let spawner = __NOEMA_DISPATCHER_SPAWNER
            .get()
            .expect("use dispatchers init macro for set spawner")
            .clone();
        let error_handler = __NOEMA_DISPATCHER_ERROR_HANDLER
            .get()
            .expect("use dispatchers init macro for set error handler")
            .clone();
        let event = Arc::new(event);
        for h in self
            .get_event_handlers()
            .iter()
            .map(|factory| factory())
            .collect::<Vec<_>>()
        {
            let error_handler_clone = error_handler.clone();
            let event_clone = event.clone();
            let event_clone_2 = event.clone();
            spawner.spawn(Box::pin(async move {
                let err = h.handle(event_clone).await.err();
                if let Some(error) = err {
                    h.on_error(error_handler_clone, error, event_clone_2).await;
                }
            }));
        }
    }
}

pub trait EventListeners<E: Event + Send + Sync> {
    fn handlers(
        &self,
    ) -> Arc<RwLock<Vec<Arc<dyn Fn() -> Arc<dyn EventListener<E> + Send + Sync> + Send + Sync>>>>;

    fn add_handler(
        &self,
        factory: Arc<dyn Fn() -> Arc<dyn EventListener<E> + Send + Sync> + Send + Sync>,
    ) {
        self.handlers().write().unwrap().push(factory);
    }

    fn get_handlers(
        &self,
    ) -> Arc<Vec<Arc<dyn Fn() -> Arc<dyn EventListener<E> + Send + Sync> + Send + Sync>>> {
        Arc::new(self.handlers().read().unwrap().clone())
    }
}

/// Listen an Event using an EventHandler, which will be instantiated using dependency injection
/// The Event type must be registered before subscribing handlers to it
/// # Examples
/// ```ignore
/// listen::<MyEvent, MyEventHandler>();
/// ```
pub fn listen<E, H>()
where
    E: Event + Send + Sync + 'static,
    H: EventListener<E> + Injectable<Container> + Send + Sync + 'static,
    Container: EventListeners<E>,
{
    Container.add_handler(Arc::new(|| {
        let handler = H::inject(&Container);
        Arc::new(handler) as Arc<dyn EventListener<E> + Send + Sync>
    }));
}

pub fn dispatch<E: Event + Send + Sync + 'static>(event: E)
where
    Container: Dispatcher<E>,
{
    <Container as Dispatcher<E>>::dispatch(&Container, event);
}

/// Configure the dispatcher system, setting the spawner and error handler for background tasks
/// Spawner and error handler must implement Injectable trait
/// WARNING: IF the spawner or error handler is already set, it will panic
pub fn dispatcher_config<TSpawn, TErrorHandler>()
where
    TSpawn: BackgroundSpawner + Injectable<Container> + Send + Sync + 'static,
    TErrorHandler: BackgroundErrorHandler + Injectable<Container> + Send + Sync + 'static,
{
    use crate::dispatcher::{__NOEMA_DISPATCHER_ERROR_HANDLER, __NOEMA_DISPATCHER_SPAWNER};
    __NOEMA_DISPATCHER_SPAWNER.get().is_some().then(|| {
        panic!("Dispatcher spawner already set");
    });
    __NOEMA_DISPATCHER_SPAWNER
        .set(Arc::new(TSpawn::inject(&Container)))
        .is_err()
        .then(|| {
            panic!("Dispatcher spawner already set");
        });

    __NOEMA_DISPATCHER_ERROR_HANDLER.get().is_some().then(|| {
        panic!("Dispatcher error handler already set");
    });
    __NOEMA_DISPATCHER_ERROR_HANDLER
        .set(Arc::new(TErrorHandler::inject(&Container)))
        .is_err()
        .then(|| {
            panic!("Dispatcher error handler already set");
        });
}

#[cfg(test)]
mod test {
    use crate::core::*;
    use crate::dispatcher::{
        BackgroundErrorHandler, BackgroundSpawner, Dispatcher, Event, EventListener,
        EventListeners, dispatcher_config, event, listen,
    };
    #[event(name = "Papus", description = "A simple hola event")]
    struct Hola {
        value: i32,
    }
    #[event(name = "Adios", description = "A simple hola event")]
    struct Adios {
        value: i32,
    }

    #[derive(Injectable)]
    struct HolaHandler {}

    #[async_trait::async_trait]
    impl EventListener<Hola> for HolaHandler {
        async fn handle(&self, event: Arc<Hola>) -> Result<(), BoxDynError> {
            println!("Handling event: {}", event.describe());
            Ok(())
        }
    }

    #[derive(Injectable)]
    struct HolaErrorHandler {}
    #[async_trait::async_trait]
    impl EventListener<Hola> for HolaErrorHandler {
        async fn handle(&self, _: Arc<Hola>) -> Result<(), BoxDynError> {
            Err(Box::new(std::io::Error::new(
                std::io::ErrorKind::QuotaExceeded,
                "Simulated error in Hola handler",
            )))
        }
        async fn on_error(
            &self,
            backgorund_error_handler: arc_dyn!(BackgroundErrorHandler),
            error: BoxDynError,
            event: Arc<Hola>,
        ) {
            println!("IS IN THE HANDLER");
            println!(
                "Handling error in HolaErrorHandler for event {}: {}",
                event.describe(),
                error
            );
            backgorund_error_handler
                .handle(error, event, self.name())
                .await;
        }
    }

    #[derive(Injectable)]
    struct AdiosHandler {}

    #[async_trait::async_trait]
    impl EventListener<Adios> for AdiosHandler {
        async fn handle(&self, event: Arc<Adios>) -> NoemaResult<()> {
            println!("Handling event: {}", event.describe());
            Ok(())
        }
    }

    #[test]
    fn subscriber_test() {
        #[derive(Injectable)]
        struct FuturesSpawner {}

        impl BackgroundSpawner for FuturesSpawner {
            fn spawn(&self, fut: std::pin::Pin<Box<dyn Future<Output = ()> + Send>>) {
                futures::executor::block_on(fut);
            }
        }
        #[derive(Injectable)]
        struct StdErrorHandler {}
        #[async_trait::async_trait]
        impl BackgroundErrorHandler for StdErrorHandler {
            async fn handle(
                &self,
                error: BoxDynError,
                event: Arc<dyn Event + Send + Sync>,
                handler_name: String,
            ) {
                eprintln!(
                    "Error in handler {} for event {}: {}",
                    handler_name,
                    event.describe(),
                    error
                );
            }
        }
        let hola: Arc<dyn Dispatcher<Hola> + Send + Sync> = Arc::new(Container);

        #[derive(Injectable)]
        struct TestPapu {
            dispatcher: Arc<dyn Dispatcher<Hola> + Send + Sync>,
        }
        impl TestPapu {
            fn dispatch(&self, event: Hola) {
                self.dispatcher.dispatch(event);
            }
        }

        dispatcher_config::<FuturesSpawner, StdErrorHandler>();
        listen::<Hola, HolaHandler>();
        listen::<Hola, HolaHandler>();
        listen::<Hola, HolaErrorHandler>();
        listen::<Adios, AdiosHandler>();
        let test_papu = TestPapu::inject(&Container);
        test_papu.dispatch(Hola { value: 42 });
    }
}