noema 0.1.1

Noema IOC and DI framework for Rust
Documentation
use std::error::Error;
use std::sync::Arc;

/// Input trait for inputs producing output of type Output
/// # Examples
/// ```ignore
/// pub struct MyInput {
///     pub data: String,
/// }
/// impl Input for MyInput {
///     type Output = usize; // Output type is usize
/// }
/// ```
pub trait Input {
    fn name(&self) -> String {
        std::any::type_name::<Self>().to_string()
    }
    fn description(&self) -> String {
        format!("Input of type {}", self.name())
    }
    type Output;
}

/// Handler of an Input type T, producing an output of type T::Output
/// Examples
/// ```ignore
/// // First Declare Input type
/// pub struct MyInput {
///     pub data: String,
/// }
/// impl Input for MyInput {
///     type Output = usize; // Output type is usize
/// }
///
/// // Then implement InputHandler for it
/// struct MyInputHandler;
/// #[async_trait]
/// impl InputHandler<MyInput> for MyInputHandler {
///     async fn handle(&self, input: Arc<MyInput>) -> Result<<MyInput as Input>::Output, Box<dyn Error>> {
///         Ok(input.data.len()) // Return length of input data
///     }
/// }
/// ```
#[async_trait::async_trait]
pub trait InputHandler<T: Input + Send + Sync> {
    async fn handle(&self, input: Arc<T>) -> Result<T::Output, Box<dyn Error>>;
}

/// Mediator for Input type I
#[async_trait::async_trait]
pub trait Mediator<I: Input + Send + Sync + 'static> {
    /// Get the input handler for the input type I
    fn get_input_handler(&self) -> Arc<dyn InputHandler<I> + Send + Sync>;

    /// Send input and get output
    async fn send(&self, input: I) -> Result<I::Output, Box<dyn Error>> {
        let input = Arc::new(input);
        let handler = self.get_input_handler();
        handler.handle(input).await
    }
}

/// Macro to define handlers for input types
/// HANDLER MUST IMPLEMENT Injectable TRAIT
///
/// Supports multiple handler registrations:
/// - Input: Handler
///
/// # Examples
/// ```ignore
/// handlers!(
///     MyInput: MyInputHandler,
///     MyOtherInput: MyOtherInputHandler,
///     ThirdInput: ThirdInputHandler
/// );
/// ```
#[macro_export]
macro_rules! handlers {
    // Entry point - process all items
    ($($item:tt)*) => {
        $crate::__handlers_internal!($($item)*);
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! __handlers_internal {
    // Base case - empty
    () => {};

    // Input: Handler, more items
    ($tinput:path: $thandler:ty, $($rest:tt)*) => {
        const _: () = {
            use $crate::inject::Injectable;
            use $crate::mediator::{InputHandler, Mediator};
            use $crate::resolver::Resolver;
            use std::sync::Arc;

            impl Mediator<$tinput> for () {
                fn get_input_handler(&self) -> Arc<dyn InputHandler<$tinput> + Send + Sync> {
                    let handler = <$thandler as Injectable>::inject();
                    Arc::new(handler) as Arc<dyn InputHandler<$tinput> + Send + Sync>
                }
            }

            impl Resolver<dyn Mediator<$tinput> + Send + Sync> for () {
                fn resolve(&self) -> Arc<dyn Mediator<$tinput> + Send + Sync + 'static> {
                    Arc::new(())
                }
            }
        };
        $crate::__handlers_internal!($($rest)*);
    };

    // Input: Handler, last item
    ($tinput:path: $thandler:ty) => {
        const _: () = {
            use $crate::inject::Injectable;
            use $crate::mediator::{InputHandler, Mediator};
            use $crate::resolver::Resolver;
            use std::sync::Arc;

            impl Mediator<$tinput> for () {
                fn get_input_handler(&self) -> Arc<dyn InputHandler<$tinput> + Send + Sync> {
                    let handler = <$thandler as Injectable>::inject();
                    Arc::new(handler) as Arc<dyn InputHandler<$tinput> + Send + Sync>
                }
            }

            impl Resolver<dyn Mediator<$tinput> + Send + Sync> for () {
                fn resolve(&self) -> Arc<dyn Mediator<$tinput> + Send + Sync + 'static> {
                    Arc::new(())
                }
            }
        };
    };
}

/// Send an input and get the output
pub async fn send<I: Input + Send + Sync + 'static>(
    input: I,
) -> Result<I::Output, Box<dyn std::error::Error>>
where
    (): Mediator<I>,
{
    <() as Mediator<I>>::send(&(), input).await
}

#[cfg(test)]
mod test {
    #[test]
    fn mediator_test() {
        use crate::mediator::{Input, InputHandler, send};
        use async_trait::async_trait;
        use futures::executor::block_on;
        use std::error::Error;
        use std::sync::Arc;
        // Define input type
        struct TestInput {
            pub value: u32,
        }
        impl Input for TestInput {
            type Output = u32;
        }
        // Define input handler
        struct TestInputHandler;
        #[async_trait]
        impl InputHandler<TestInput> for TestInputHandler {
            async fn handle(
                &self,
                input: Arc<TestInput>,
            ) -> Result<<TestInput as Input>::Output, Box<dyn Error>> {
                Ok(input.value + 1)
            }
        }
        impl crate::inject::Injectable for TestInputHandler {
            fn inject() -> Self {
                TestInputHandler
            }
        }
        crate::handlers!(TestInput: TestInputHandler); // Register handlers
        let output = block_on(send(TestInput { value: 41 })).unwrap(); // Send
        assert!(output == 42); // Check output
    }
}