modrun 1.0.2

Modular application composer for Tokio: domain modules, typed wiring, lifecycle
Documentation
use std::marker::PhantomData;

use crate::app::BuildState;
use crate::error::Error;
use crate::error::Result;
use crate::group::Group;
use crate::option::ModOption;
use crate::provide::{
    AsyncProviderFn, DynProvider, FallibleAsyncProviderFn, FallibleProviderFn, ProviderFn,
    ProviderMarker,
};
use crate::trace::provided_group;
use std::any::TypeId;
use std::any::type_name;

macro_rules! provide_group_fn {
    ($name:ident, $trait:ident) => {
        pub(crate) fn $name<M, F>(ctor: F) -> Box<dyn ModOption>
        where
            M: ProviderMarker,
            <M as ProviderMarker>::Output: Clone + Send + Sync + 'static,
            F: $trait<M> + 'static,
        {
            register_group::<M::Output>(ctor.into_provider())
        }
    };
}

provide_group_fn!(provide_group, ProviderFn);
provide_group_fn!(provide_group_result, FallibleProviderFn);
provide_group_fn!(provide_group_async, AsyncProviderFn);
provide_group_fn!(provide_group_result_async, FallibleAsyncProviderFn);

pub(crate) fn provide_group_dyn<T: Clone + Send + Sync + 'static>(
    provider: DynProvider,
) -> Box<dyn ModOption> {
    if provider.result_type() != TypeId::of::<T>() {
        return Box::new(GroupMemberTypeMismatchOption {
            expected: type_name::<T>(),
            actual: provider.result_name(),
        });
    }
    register_group::<T>(provider)
}

struct GroupMemberTypeMismatchOption {
    expected: &'static str,
    actual: &'static str,
}

impl ModOption for GroupMemberTypeMismatchOption {
    fn apply(self: Box<Self>, _app: &mut BuildState) -> Result<()> {
        Err(Error::GroupMemberTypeMismatch {
            expected: self.expected,
            actual: self.actual,
        })
    }
}

fn register_group<T>(provider: DynProvider) -> Box<dyn ModOption>
where
    T: Clone + Send + Sync + 'static,
{
    Box::new(ProvideGroupOption::<T> {
        provider,
        _marker: PhantomData,
    })
}

struct ProvideGroupOption<T> {
    provider: DynProvider,
    _marker: PhantomData<T>,
}

impl<T: Clone + Send + Sync + 'static> ModOption for ProvideGroupOption<T> {
    fn apply(self: Box<Self>, app: &mut BuildState) -> Result<()> {
        let type_name = self.provider.result_name();
        let constructor = self.provider.constructor_name();
        let scope = app.current_scope;
        app.container
            .insert_group_member_typed::<T>(self.provider, scope)?;
        provided_group(
            type_name,
            constructor,
            app.container.scopes().name(scope),
            false,
        );
        Ok(())
    }
}

pub(crate) fn init_group<T: Clone + Send + Sync + 'static>() -> Box<dyn ModOption> {
    Box::new(InitGroupOption(PhantomData::<T>))
}

struct InitGroupOption<T>(PhantomData<T>);

impl<T: Clone + Send + Sync + 'static> ModOption for InitGroupOption<T> {
    fn apply(self: Box<Self>, app: &mut BuildState) -> Result<()> {
        app.container.ensure_empty_group::<T>()
    }
}

pub(crate) fn require_group<T: Clone + Send + Sync + 'static>() -> Box<dyn ModOption> {
    Box::new(RequireGroupOption(PhantomData::<T>))
}

struct RequireGroupOption<T>(PhantomData<T>);

impl<T: Clone + Send + Sync + 'static> ModOption for RequireGroupOption<T> {
    fn apply(self: Box<Self>, app: &mut BuildState) -> Result<()> {
        app.container.ensure_empty_group::<T>()?;
        app.container
            .require_group_element(TypeId::of::<T>(), type_name::<Group<T>>());
        Ok(())
    }
}