diff options
| author | HampusM <hampus@hampusmat.com> | 2022-10-09 12:05:24 +0200 | 
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2022-10-09 17:03:01 +0200 | 
| commit | 5b0c6a52022e67a2d9cee251b3d08b9cb2b5f6cb (patch) | |
| tree | c33f06eaab96ec43e477ea5ecd2af93e9d739097 | |
| parent | 97c789e38bb8e61389a3808d241689e623144344 (diff) | |
refactor!: reorganize DI containers
BREAKING CHANGE: DIContainer, AsyncDIContainer & the binding structs have been relocated
21 files changed, 3013 insertions, 2926 deletions
diff --git a/examples/async/bootstrap.rs b/examples/async/bootstrap.rs index dd2febe..9b495c2 100644 --- a/examples/async/bootstrap.rs +++ b/examples/async/bootstrap.rs @@ -1,9 +1,8 @@  use std::sync::Arc;  use anyhow::Result; -use syrette::async_di_container::AsyncDIContainer; -use syrette::declare_default_factory;  use syrette::ptr::TransientPtr; +use syrette::{declare_default_factory, AsyncDIContainer};  use crate::animals::cat::Cat;  use crate::animals::dog::Dog; diff --git a/macros/src/factory/build_declare_interfaces.rs b/macros/src/factory/build_declare_interfaces.rs index 61e162f..6ab135e 100644 --- a/macros/src/factory/build_declare_interfaces.rs +++ b/macros/src/factory/build_declare_interfaces.rs @@ -12,10 +12,10 @@ pub fn build_declare_factory_interfaces(          quote! {              syrette::declare_interface!(                  syrette::castable_factory::threadsafe::ThreadsafeCastableFactory< -                    (std::sync::Arc<syrette::async_di_container::AsyncDIContainer>,), +                    (std::sync::Arc<syrette::AsyncDIContainer>,),                      #factory_interface                  > -> syrette::interfaces::factory::IThreadsafeFactory< -                    (std::sync::Arc<syrette::async_di_container::AsyncDIContainer>,), +                    (std::sync::Arc<syrette::AsyncDIContainer>,),                      #factory_interface                  >,                  async = true @@ -23,7 +23,7 @@ pub fn build_declare_factory_interfaces(              syrette::declare_interface!(                  syrette::castable_factory::threadsafe::ThreadsafeCastableFactory< -                    (std::sync::Arc<syrette::async_di_container::AsyncDIContainer>,), +                    (std::sync::Arc<syrette::AsyncDIContainer>,),                      #factory_interface                  > -> syrette::interfaces::any_factory::AnyThreadsafeFactory,                  async = true @@ -33,17 +33,17 @@ pub fn build_declare_factory_interfaces(          quote! {              syrette::declare_interface!(                  syrette::castable_factory::blocking::CastableFactory< -                    (std::rc::Rc<syrette::di_container::DIContainer>,), +                    (std::rc::Rc<syrette::DIContainer>,),                      #factory_interface                  > -> syrette::interfaces::factory::IFactory< -                    (std::rc::Rc<syrette::di_container::DIContainer>,), +                    (std::rc::Rc<syrette::DIContainer>,),                      #factory_interface                  >              );              syrette::declare_interface!(                  syrette::castable_factory::blocking::CastableFactory< -                    (std::rc::Rc<syrette::di_container::DIContainer>,), +                    (std::rc::Rc<syrette::DIContainer>,),                      #factory_interface                  > -> syrette::interfaces::any_factory::AnyFactory              ); diff --git a/macros/src/injectable/implementation.rs b/macros/src/injectable/implementation.rs index 82cbe16..c907f32 100644 --- a/macros/src/injectable/implementation.rs +++ b/macros/src/injectable/implementation.rs @@ -91,7 +91,7 @@ impl InjectableImpl                  {                      fn resolve<'di_container, 'fut>(                          #di_container_var: &'di_container std::sync::Arc< -                            syrette::async_di_container::AsyncDIContainer +                            syrette::AsyncDIContainer                          >,                          mut #dependency_history_var: Vec<&'static str>,                      ) -> syrette::future::BoxFuture< diff --git a/src/async_di_container.rs b/src/async_di_container.rs deleted file mode 100644 index 3943dae..0000000 --- a/src/async_di_container.rs +++ /dev/null @@ -1,1641 +0,0 @@ -//! Asynchronous dependency injection container. -//! -//! # Examples -//! ``` -//! use std::collections::HashMap; -//! use std::error::Error; -//! -//! use syrette::{injectable, AsyncDIContainer}; -//! -//! trait IDatabaseService: Send + Sync -//! { -//!     fn get_all_records(&self, table_name: String) -> HashMap<String, String>; -//! } -//! -//! struct DatabaseService {} -//! -//! #[injectable(IDatabaseService, async = true)] -//! impl DatabaseService -//! { -//!     fn new() -> Self -//!     { -//!         Self {} -//!     } -//! } -//! -//! impl IDatabaseService for DatabaseService -//! { -//!     fn get_all_records(&self, table_name: String) -> HashMap<String, String> -//!     { -//!         // Do stuff here -//!         HashMap::<String, String>::new() -//!     } -//! } -//! -//! #[tokio::main] -//! async fn main() -> Result<(), Box<dyn Error>> -//! { -//!     let mut di_container = AsyncDIContainer::new(); -//! -//!     di_container -//!         .bind::<dyn IDatabaseService>() -//!         .to::<DatabaseService>() -//!         .await?; -//! -//!     let database_service = di_container -//!         .get::<dyn IDatabaseService>() -//!         .await? -//!         .transient()?; -//! -//!     Ok(()) -//! } -//! ``` -use std::any::type_name; -use std::marker::PhantomData; -use std::sync::Arc; - -use tokio::sync::Mutex; - -#[cfg(feature = "factory")] -use crate::castable_factory::threadsafe::ThreadsafeCastableFactory; -use crate::di_container_binding_map::DIContainerBindingMap; -use crate::errors::async_di_container::{ -    AsyncBindingBuilderError, -    AsyncBindingScopeConfiguratorError, -    AsyncBindingWhenConfiguratorError, -    AsyncDIContainerError, -}; -use crate::future::BoxFuture; -use crate::interfaces::async_injectable::AsyncInjectable; -use crate::libs::intertrait::cast::error::CastError; -use crate::libs::intertrait::cast::{CastArc, CastBox}; -use crate::provider::r#async::{ -    AsyncProvidable, -    AsyncSingletonProvider, -    AsyncTransientTypeProvider, -    IAsyncProvider, -}; -use crate::ptr::{SomeThreadsafePtr, ThreadsafeSingletonPtr, TransientPtr}; - -/// Alias for a threadsafe boxed function. -#[cfg(feature = "factory")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))] -pub type BoxFn<Args, Return> = Box<(dyn Fn<Args, Output = Return> + Send + Sync)>; - -/// When configurator for a binding for type 'Interface' inside a [`AsyncDIContainer`]. -pub struct AsyncBindingWhenConfigurator<Interface> -where -    Interface: 'static + ?Sized + Send + Sync, -{ -    di_container: Arc<AsyncDIContainer>, -    interface_phantom: PhantomData<Interface>, -} - -impl<Interface> AsyncBindingWhenConfigurator<Interface> -where -    Interface: 'static + ?Sized + Send + Sync, -{ -    fn new(di_container: Arc<AsyncDIContainer>) -> Self -    { -        Self { -            di_container, -            interface_phantom: PhantomData, -        } -    } - -    /// Configures the binding to have a name. -    /// -    /// # Errors -    /// Will return Err if no binding for the interface already exists. -    pub async fn when_named( -        &self, -        name: &'static str, -    ) -> Result<(), AsyncBindingWhenConfiguratorError> -    { -        let mut bindings_lock = self.di_container.bindings.lock().await; - -        let binding = bindings_lock.remove::<Interface>(None).map_or_else( -            || { -                Err(AsyncBindingWhenConfiguratorError::BindingNotFound( -                    type_name::<Interface>(), -                )) -            }, -            Ok, -        )?; - -        bindings_lock.set::<Interface>(Some(name), binding); - -        Ok(()) -    } -} - -/// Scope configurator for a binding for type 'Interface' inside a [`AsyncDIContainer`]. -pub struct AsyncBindingScopeConfigurator<Interface, Implementation> -where -    Interface: 'static + ?Sized + Send + Sync, -    Implementation: AsyncInjectable, -{ -    di_container: Arc<AsyncDIContainer>, -    interface_phantom: PhantomData<Interface>, -    implementation_phantom: PhantomData<Implementation>, -} - -impl<Interface, Implementation> AsyncBindingScopeConfigurator<Interface, Implementation> -where -    Interface: 'static + ?Sized + Send + Sync, -    Implementation: AsyncInjectable, -{ -    fn new(di_container: Arc<AsyncDIContainer>) -> Self -    { -        Self { -            di_container, -            interface_phantom: PhantomData, -            implementation_phantom: PhantomData, -        } -    } - -    /// Configures the binding to be in a transient scope. -    /// -    /// This is the default. -    pub async fn in_transient_scope(&self) -> AsyncBindingWhenConfigurator<Interface> -    { -        let mut bindings_lock = self.di_container.bindings.lock().await; - -        bindings_lock.set::<Interface>( -            None, -            Box::new(AsyncTransientTypeProvider::<Implementation>::new()), -        ); - -        AsyncBindingWhenConfigurator::new(self.di_container.clone()) -    } - -    /// Configures the binding to be in a singleton scope. -    /// -    /// # Errors -    /// Will return Err if resolving the implementation fails. -    pub async fn in_singleton_scope( -        &self, -    ) -> Result<AsyncBindingWhenConfigurator<Interface>, AsyncBindingScopeConfiguratorError> -    { -        let singleton: ThreadsafeSingletonPtr<Implementation> = -            ThreadsafeSingletonPtr::from( -                Implementation::resolve(&self.di_container, Vec::new()) -                    .await -                    .map_err( -                        AsyncBindingScopeConfiguratorError::SingletonResolveFailed, -                    )?, -            ); - -        let mut bindings_lock = self.di_container.bindings.lock().await; - -        bindings_lock -            .set::<Interface>(None, Box::new(AsyncSingletonProvider::new(singleton))); - -        Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) -    } -} - -/// Binding builder for type `Interface` inside a [`AsyncDIContainer`]. -pub struct AsyncBindingBuilder<Interface> -where -    Interface: 'static + ?Sized + Send + Sync, -{ -    di_container: Arc<AsyncDIContainer>, -    interface_phantom: PhantomData<Interface>, -} - -impl<Interface> AsyncBindingBuilder<Interface> -where -    Interface: 'static + ?Sized + Send + Sync, -{ -    fn new(di_container: Arc<AsyncDIContainer>) -> Self -    { -        Self { -            di_container, -            interface_phantom: PhantomData, -        } -    } - -    /// Creates a binding of type `Interface` to type `Implementation` inside of the -    /// associated [`AsyncDIContainer`]. -    /// -    /// The scope of the binding is transient. But that can be changed by using the -    /// returned [`AsyncBindingScopeConfigurator`] -    /// -    /// # Errors -    /// Will return Err if the associated [`AsyncDIContainer`] already have a binding for -    /// the interface. -    /// -    /// # Examples -    /// ``` -    /// # use std::error::Error; -    /// # -    /// # use syrette::{AsyncDIContainer, injectable}; -    /// # -    /// # trait Foo: Send + Sync {} -    /// # -    /// # struct Bar {} -    /// # -    /// # #[injectable(Foo, async = true)] -    /// # impl Bar { -    /// #   fn new() -> Self -    /// #   { -    /// #       Self {} -    /// #   } -    /// # } -    /// # -    /// # impl Foo for Bar {} -    /// # -    /// # #[tokio::main] -    /// # async fn main() -> Result<(), Box<dyn Error>> -    /// # { -    /// # let mut di_container = AsyncDIContainer::new(); -    /// # -    /// di_container.bind::<dyn Foo>().to::<Bar>().await?; -    /// # -    /// # Ok(()) -    /// # } -    /// ``` -    pub async fn to<Implementation>( -        &self, -    ) -> Result< -        AsyncBindingScopeConfigurator<Interface, Implementation>, -        AsyncBindingBuilderError, -    > -    where -        Implementation: AsyncInjectable, -    { -        { -            let bindings_lock = self.di_container.bindings.lock().await; - -            if bindings_lock.has::<Interface>(None) { -                return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< -                    Interface, -                >( -                ))); -            } -        } - -        let binding_scope_configurator = -            AsyncBindingScopeConfigurator::new(self.di_container.clone()); - -        binding_scope_configurator.in_transient_scope().await; - -        Ok(binding_scope_configurator) -    } - -    /// Creates a binding of factory type `Interface` to a factory inside of the -    /// associated [`AsyncDIContainer`]. -    /// -    /// # Errors -    /// Will return Err if the associated [`AsyncDIContainer`] already have a binding -    /// for the interface. -    /// -    /// # Examples -    /// ``` -    /// # use std::error::Error; -    /// # -    /// # use syrette::{AsyncDIContainer, factory}; -    /// # use syrette::ptr::TransientPtr; -    /// # -    /// # trait Foo: Send + Sync {} -    /// # -    /// # struct Bar -    /// # { -    /// #   num: i32, -    /// #   some_str: String -    /// # } -    /// # -    /// # impl Foo for Bar {} -    /// # -    /// # #[factory(threadsafe = true)] -    /// # type FooFactory = dyn Fn(i32, String) -> dyn Foo; -    /// # -    /// # #[tokio::main] -    /// # async fn main() -> Result<(), Box<dyn Error>> -    /// # { -    /// # let mut di_container = AsyncDIContainer::new(); -    /// # -    /// di_container -    ///     .bind::<FooFactory>() -    ///     .to_factory(&|_| { -    ///         Box::new(|num, some_str| { -    ///             let bar = TransientPtr::new(Bar { num, some_str }); -    /// -    ///             bar as TransientPtr<dyn Foo> -    ///         }) -    ///     }) -    ///     .await?; -    /// # -    /// # Ok(()) -    /// # } -    /// ``` -    #[cfg(feature = "factory")] -    #[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))] -    pub async fn to_factory<Args, Return, FactoryFunc>( -        &self, -        factory_func: &'static FactoryFunc, -    ) -> Result<AsyncBindingWhenConfigurator<Interface>, AsyncBindingBuilderError> -    where -        Args: 'static, -        Return: 'static + ?Sized, -        Interface: Fn<Args, Output = Return> + Send + Sync, -        FactoryFunc: -            Fn<(Arc<AsyncDIContainer>,), Output = BoxFn<Args, Return>> + Send + Sync, -    { -        use crate::provider::r#async::AsyncFactoryVariant; - -        let mut bindings_lock = self.di_container.bindings.lock().await; - -        if bindings_lock.has::<Interface>(None) { -            return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< -                Interface, -            >( -            ))); -        } - -        let factory_impl = ThreadsafeCastableFactory::new(factory_func); - -        bindings_lock.set::<Interface>( -            None, -            Box::new(crate::provider::r#async::AsyncFactoryProvider::new( -                crate::ptr::ThreadsafeFactoryPtr::new(factory_impl), -                AsyncFactoryVariant::Normal, -            )), -        ); - -        Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) -    } - -    /// Creates a binding of factory type `Interface` to a async factory inside of the -    /// associated [`AsyncDIContainer`]. -    /// -    /// # Errors -    /// Will return Err if the associated [`AsyncDIContainer`] already have a binding -    /// for the interface. -    /// -    /// # Examples -    /// ``` -    /// # use std::error::Error; -    /// # use std::time::Duration; -    /// # -    /// # use syrette::{AsyncDIContainer, factory, async_closure}; -    /// # use syrette::ptr::TransientPtr; -    /// # -    /// # trait Foo: Send + Sync {} -    /// # -    /// # struct Bar -    /// # { -    /// #   num: i32, -    /// #   some_str: String -    /// # } -    /// # -    /// # impl Foo for Bar {} -    /// # -    /// # #[factory(async = true)] -    /// # type FooFactory = dyn Fn(i32, String) -> dyn Foo; -    /// # -    /// # #[tokio::main] -    /// # async fn main() -> Result<(), Box<dyn Error>> -    /// # { -    /// # let mut di_container = AsyncDIContainer::new(); -    /// # -    /// di_container -    ///     .bind::<FooFactory>() -    ///     .to_async_factory(&|_| { -    ///         async_closure!(|num, some_str| { -    ///             let bar = TransientPtr::new(Bar { num, some_str }); -    /// -    ///             tokio::time::sleep(Duration::from_secs(2)).await; -    /// -    ///             bar as TransientPtr<dyn Foo> -    ///         }) -    ///     }) -    ///     .await?; -    /// # -    /// # Ok(()) -    /// # } -    /// ``` -    #[cfg(feature = "factory")] -    #[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))] -    pub async fn to_async_factory<Args, Return, FactoryFunc>( -        &self, -        factory_func: &'static FactoryFunc, -    ) -> Result<AsyncBindingWhenConfigurator<Interface>, AsyncBindingBuilderError> -    where -        Args: 'static, -        Return: 'static + ?Sized, -        Interface: -            Fn<Args, Output = crate::future::BoxFuture<'static, Return>> + Send + Sync, -        FactoryFunc: Fn< -                (Arc<AsyncDIContainer>,), -                Output = BoxFn<Args, crate::future::BoxFuture<'static, Return>>, -            > + Send -            + Sync, -    { -        use crate::provider::r#async::AsyncFactoryVariant; - -        let mut bindings_lock = self.di_container.bindings.lock().await; - -        if bindings_lock.has::<Interface>(None) { -            return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< -                Interface, -            >( -            ))); -        } - -        let factory_impl = ThreadsafeCastableFactory::new(factory_func); - -        bindings_lock.set::<Interface>( -            None, -            Box::new(crate::provider::r#async::AsyncFactoryProvider::new( -                crate::ptr::ThreadsafeFactoryPtr::new(factory_impl), -                AsyncFactoryVariant::Normal, -            )), -        ); - -        Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) -    } - -    /// Creates a binding of type `Interface` to a factory that takes no arguments -    /// inside of the associated [`AsyncDIContainer`]. -    /// -    /// # Errors -    /// Will return Err if the associated [`AsyncDIContainer`] already have a binding -    /// for the interface. -    /// -    /// # Examples -    /// ``` -    /// # use std::error::Error; -    /// # -    /// # use syrette::AsyncDIContainer; -    /// # use syrette::ptr::TransientPtr; -    /// # -    /// # trait Foo: Send + Sync {} -    /// # -    /// # struct Bar -    /// # { -    /// #   num: i32, -    /// #   some_str: String -    /// # } -    /// # -    /// # impl Foo for Bar {} -    /// # -    /// # #[tokio::main] -    /// # async fn main() -> Result<(), Box<dyn Error>> -    /// # { -    /// # let mut di_container = AsyncDIContainer::new(); -    /// # -    /// di_container -    ///     .bind::<dyn Foo>() -    ///     .to_default_factory(&|_| { -    ///         Box::new(|| { -    ///             let bar = TransientPtr::new(Bar { -    ///                 num: 42, -    ///                 some_str: "hello".to_string(), -    ///             }); -    /// -    ///             bar as TransientPtr<dyn Foo> -    ///         }) -    ///     }) -    ///     .await?; -    /// # -    /// # Ok(()) -    /// # } -    /// ``` -    #[cfg(feature = "factory")] -    #[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))] -    pub async fn to_default_factory<Return, FactoryFunc>( -        &self, -        factory_func: &'static FactoryFunc, -    ) -> Result<AsyncBindingWhenConfigurator<Interface>, AsyncBindingBuilderError> -    where -        Return: 'static + ?Sized, -        FactoryFunc: Fn< -                (Arc<AsyncDIContainer>,), -                Output = BoxFn<(), crate::ptr::TransientPtr<Return>>, -            > + Send -            + Sync, -    { -        use crate::provider::r#async::AsyncFactoryVariant; - -        let mut bindings_lock = self.di_container.bindings.lock().await; - -        if bindings_lock.has::<Interface>(None) { -            return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< -                Interface, -            >( -            ))); -        } - -        let factory_impl = ThreadsafeCastableFactory::new(factory_func); - -        bindings_lock.set::<Interface>( -            None, -            Box::new(crate::provider::r#async::AsyncFactoryProvider::new( -                crate::ptr::ThreadsafeFactoryPtr::new(factory_impl), -                AsyncFactoryVariant::Default, -            )), -        ); - -        Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) -    } - -    /// Creates a binding of factory type `Interface` to a async factory inside of the -    /// associated [`AsyncDIContainer`]. -    /// -    /// # Errors -    /// Will return Err if the associated [`AsyncDIContainer`] already have a binding -    /// for the interface. -    /// -    /// # Examples -    /// ``` -    /// # use std::error::Error; -    /// # use std::time::Duration; -    /// # -    /// # use syrette::{AsyncDIContainer, async_closure}; -    /// # use syrette::ptr::TransientPtr; -    /// # -    /// # trait Foo: Send + Sync {} -    /// # -    /// # struct Bar -    /// # { -    /// #   num: i32, -    /// #   some_str: String -    /// # } -    /// # -    /// # impl Foo for Bar {} -    /// # -    /// # #[tokio::main] -    /// # async fn main() -> Result<(), Box<dyn Error>> -    /// # { -    /// # let mut di_container = AsyncDIContainer::new(); -    /// # -    /// di_container -    ///     .bind::<dyn Foo>() -    ///     .to_async_default_factory(&|_| { -    ///         async_closure!(|| { -    ///             let bar = TransientPtr::new(Bar { -    ///                 num: 42, -    ///                 some_str: "hello".to_string(), -    ///             }); -    /// -    ///             tokio::time::sleep(Duration::from_secs(1)).await; -    /// -    ///             bar as TransientPtr<dyn Foo> -    ///         }) -    ///     }) -    ///     .await?; -    /// # -    /// # Ok(()) -    /// # } -    /// ``` -    #[cfg(feature = "factory")] -    #[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))] -    pub async fn to_async_default_factory<Return, FactoryFunc>( -        &self, -        factory_func: &'static FactoryFunc, -    ) -> Result<AsyncBindingWhenConfigurator<Interface>, AsyncBindingBuilderError> -    where -        Return: 'static + ?Sized, -        FactoryFunc: Fn< -                (Arc<AsyncDIContainer>,), -                Output = BoxFn<(), crate::future::BoxFuture<'static, Return>>, -            > + Send -            + Sync, -    { -        use crate::provider::r#async::AsyncFactoryVariant; - -        let mut bindings_lock = self.di_container.bindings.lock().await; - -        if bindings_lock.has::<Interface>(None) { -            return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< -                Interface, -            >( -            ))); -        } - -        let factory_impl = ThreadsafeCastableFactory::new(factory_func); - -        bindings_lock.set::<Interface>( -            None, -            Box::new(crate::provider::r#async::AsyncFactoryProvider::new( -                crate::ptr::ThreadsafeFactoryPtr::new(factory_impl), -                AsyncFactoryVariant::AsyncDefault, -            )), -        ); - -        Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) -    } -} - -/// Dependency injection container. -pub struct AsyncDIContainer -{ -    bindings: Mutex<DIContainerBindingMap<dyn IAsyncProvider>>, -} - -impl AsyncDIContainer -{ -    /// Returns a new `AsyncDIContainer`. -    #[must_use] -    pub fn new() -> Arc<Self> -    { -        Arc::new(Self { -            bindings: Mutex::new(DIContainerBindingMap::new()), -        }) -    } - -    /// Returns a new [`AsyncBindingBuilder`] for the given interface. -    #[must_use] -    pub fn bind<Interface>(self: &mut Arc<Self>) -> AsyncBindingBuilder<Interface> -    where -        Interface: 'static + ?Sized + Send + Sync, -    { -        AsyncBindingBuilder::<Interface>::new(self.clone()) -    } - -    /// Returns the type bound with `Interface`. -    /// -    /// # Errors -    /// Will return `Err` if: -    /// - No binding for `Interface` exists -    /// - Resolving the binding for `Interface` fails -    /// - Casting the binding for `Interface` fails -    pub async fn get<Interface>( -        self: &Arc<Self>, -    ) -> Result<SomeThreadsafePtr<Interface>, AsyncDIContainerError> -    where -        Interface: 'static + ?Sized + Send + Sync, -    { -        self.get_bound::<Interface>(Vec::new(), None).await -    } - -    /// Returns the type bound with `Interface` and the specified name. -    /// -    /// # Errors -    /// Will return `Err` if: -    /// - No binding for `Interface` with name `name` exists -    /// - Resolving the binding for `Interface` fails -    /// - Casting the binding for `Interface` fails -    pub async fn get_named<Interface>( -        self: &Arc<Self>, -        name: &'static str, -    ) -> Result<SomeThreadsafePtr<Interface>, AsyncDIContainerError> -    where -        Interface: 'static + ?Sized + Send + Sync, -    { -        self.get_bound::<Interface>(Vec::new(), Some(name)).await -    } - -    #[doc(hidden)] -    pub async fn get_bound<Interface>( -        self: &Arc<Self>, -        dependency_history: Vec<&'static str>, -        name: Option<&'static str>, -    ) -> Result<SomeThreadsafePtr<Interface>, AsyncDIContainerError> -    where -        Interface: 'static + ?Sized + Send + Sync, -    { -        let binding_providable = self -            .get_binding_providable::<Interface>(name, dependency_history) -            .await?; - -        self.handle_binding_providable(binding_providable).await -    } - -    async fn handle_binding_providable<Interface>( -        self: &Arc<Self>, -        binding_providable: AsyncProvidable, -    ) -> Result<SomeThreadsafePtr<Interface>, AsyncDIContainerError> -    where -        Interface: 'static + ?Sized + Send + Sync, -    { -        match binding_providable { -            AsyncProvidable::Transient(transient_binding) => { -                Ok(SomeThreadsafePtr::Transient( -                    transient_binding.cast::<Interface>().map_err(|_| { -                        AsyncDIContainerError::CastFailed { -                            interface: type_name::<Interface>(), -                            binding_kind: "transient", -                        } -                    })?, -                )) -            } -            AsyncProvidable::Singleton(singleton_binding) => { -                Ok(SomeThreadsafePtr::ThreadsafeSingleton( -                    singleton_binding -                        .cast::<Interface>() -                        .map_err(|err| match err { -                            CastError::NotArcCastable(_) => { -                                AsyncDIContainerError::InterfaceNotAsync(type_name::< -                                    Interface, -                                >( -                                )) -                            } -                            CastError::CastFailed { from: _, to: _ } => { -                                AsyncDIContainerError::CastFailed { -                                    interface: type_name::<Interface>(), -                                    binding_kind: "singleton", -                                } -                            } -                        })?, -                )) -            } -            #[cfg(feature = "factory")] -            AsyncProvidable::Factory(factory_binding) => { -                use crate::interfaces::factory::IThreadsafeFactory; - -                let factory = factory_binding -                    .cast::<dyn IThreadsafeFactory<(Arc<AsyncDIContainer>,), Interface>>() -                    .map_err(|err| match err { -                        CastError::NotArcCastable(_) => { -                            AsyncDIContainerError::InterfaceNotAsync( -                                type_name::<Interface>(), -                            ) -                        } -                        CastError::CastFailed { from: _, to: _ } => { -                            AsyncDIContainerError::CastFailed { -                                interface: type_name::<Interface>(), -                                binding_kind: "factory", -                            } -                        } -                    })?; - -                Ok(SomeThreadsafePtr::ThreadsafeFactory( -                    factory(self.clone()).into(), -                )) -            } -            #[cfg(feature = "factory")] -            AsyncProvidable::DefaultFactory(binding) => { -                use crate::interfaces::factory::IThreadsafeFactory; - -                let default_factory = Self::cast_factory_binding::< -                    dyn IThreadsafeFactory< -                        (Arc<AsyncDIContainer>,), -                        dyn Fn<(), Output = TransientPtr<Interface>> + Send + Sync, -                    >, -                >(binding, "default factory")?; - -                Ok(SomeThreadsafePtr::Transient(default_factory(self.clone())())) -            } -            #[cfg(feature = "factory")] -            AsyncProvidable::AsyncDefaultFactory(binding) => { -                use crate::interfaces::factory::IThreadsafeFactory; - -                let async_default_factory = Self::cast_factory_binding::< -                    dyn IThreadsafeFactory< -                        (Arc<AsyncDIContainer>,), -                        dyn Fn<(), Output = BoxFuture<'static, TransientPtr<Interface>>> -                            + Send -                            + Sync, -                    >, -                >( -                    binding, "async default factory" -                )?; - -                Ok(SomeThreadsafePtr::Transient( -                    async_default_factory(self.clone())().await, -                )) -            } -        } -    } - -    #[cfg(feature = "factory")] -    fn cast_factory_binding<Type: 'static + ?Sized>( -        factory_binding: Arc<dyn crate::interfaces::any_factory::AnyThreadsafeFactory>, -        binding_kind: &'static str, -    ) -> Result<Arc<Type>, AsyncDIContainerError> -    { -        factory_binding.cast::<Type>().map_err(|err| match err { -            CastError::NotArcCastable(_) => { -                AsyncDIContainerError::InterfaceNotAsync(type_name::<Type>()) -            } -            CastError::CastFailed { from: _, to: _ } => { -                AsyncDIContainerError::CastFailed { -                    interface: type_name::<Type>(), -                    binding_kind, -                } -            } -        }) -    } - -    async fn get_binding_providable<Interface>( -        self: &Arc<Self>, -        name: Option<&'static str>, -        dependency_history: Vec<&'static str>, -    ) -> Result<AsyncProvidable, AsyncDIContainerError> -    where -        Interface: 'static + ?Sized + Send + Sync, -    { -        let provider; - -        { -            let bindings_lock = self.bindings.lock().await; - -            provider = bindings_lock -                .get::<Interface>(name) -                .map_or_else( -                    || { -                        Err(AsyncDIContainerError::BindingNotFound { -                            interface: type_name::<Interface>(), -                            name, -                        }) -                    }, -                    Ok, -                )? -                .clone(); -        } - -        provider -            .provide(self, dependency_history) -            .await -            .map_err(|err| AsyncDIContainerError::BindingResolveFailed { -                reason: err, -                interface: type_name::<Interface>(), -            }) -    } -} - -#[cfg(test)] -mod tests -{ -    use std::error::Error; - -    use async_trait::async_trait; -    use mockall::mock; - -    use super::*; -    use crate::errors::injectable::InjectableError; -    use crate::ptr::TransientPtr; - -    mod subjects -    { -        //! Test subjects. - -        use std::fmt::Debug; -        use std::sync::Arc; - -        use async_trait::async_trait; -        use syrette_macros::declare_interface; - -        use super::AsyncDIContainer; -        use crate::interfaces::async_injectable::AsyncInjectable; -        use crate::ptr::TransientPtr; - -        pub trait IUserManager: Send + Sync -        { -            fn add_user(&self, user_id: i128); - -            fn remove_user(&self, user_id: i128); -        } - -        pub struct UserManager {} - -        impl UserManager -        { -            pub fn new() -> Self -            { -                Self {} -            } -        } - -        impl IUserManager for UserManager -        { -            fn add_user(&self, _user_id: i128) -            { -                // ... -            } - -            fn remove_user(&self, _user_id: i128) -            { -                // ... -            } -        } - -        use crate as syrette; - -        declare_interface!(UserManager -> IUserManager); - -        #[async_trait] -        impl AsyncInjectable for UserManager -        { -            async fn resolve( -                _: &Arc<AsyncDIContainer>, -                _dependency_history: Vec<&'static str>, -            ) -> Result<TransientPtr<Self>, crate::errors::injectable::InjectableError> -            where -                Self: Sized, -            { -                Ok(TransientPtr::new(Self::new())) -            } -        } - -        pub trait INumber: Send + Sync -        { -            fn get(&self) -> i32; - -            fn set(&mut self, number: i32); -        } - -        impl PartialEq for dyn INumber -        { -            fn eq(&self, other: &Self) -> bool -            { -                self.get() == other.get() -            } -        } - -        impl Debug for dyn INumber -        { -            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result -            { -                f.write_str(format!("{}", self.get()).as_str()) -            } -        } - -        pub struct Number -        { -            pub num: i32, -        } - -        impl Number -        { -            pub fn new() -> Self -            { -                Self { num: 0 } -            } -        } - -        impl INumber for Number -        { -            fn get(&self) -> i32 -            { -                self.num -            } - -            fn set(&mut self, number: i32) -            { -                self.num = number; -            } -        } - -        declare_interface!(Number -> INumber, async = true); - -        #[async_trait] -        impl AsyncInjectable for Number -        { -            async fn resolve( -                _: &Arc<AsyncDIContainer>, -                _dependency_history: Vec<&'static str>, -            ) -> Result<TransientPtr<Self>, crate::errors::injectable::InjectableError> -            where -                Self: Sized, -            { -                Ok(TransientPtr::new(Self::new())) -            } -        } -    } - -    #[tokio::test] -    async fn can_bind_to() -> Result<(), Box<dyn Error>> -    { -        let mut di_container = AsyncDIContainer::new(); - -        { -            assert_eq!(di_container.bindings.lock().await.count(), 0); -        } - -        di_container -            .bind::<dyn subjects::IUserManager>() -            .to::<subjects::UserManager>() -            .await?; - -        { -            assert_eq!(di_container.bindings.lock().await.count(), 1); -        } - -        Ok(()) -    } - -    #[tokio::test] -    async fn can_bind_to_transient() -> Result<(), Box<dyn Error>> -    { -        let mut di_container = AsyncDIContainer::new(); - -        { -            assert_eq!(di_container.bindings.lock().await.count(), 0); -        } - -        di_container -            .bind::<dyn subjects::IUserManager>() -            .to::<subjects::UserManager>() -            .await? -            .in_transient_scope() -            .await; - -        { -            assert_eq!(di_container.bindings.lock().await.count(), 1); -        } - -        Ok(()) -    } - -    #[tokio::test] -    async fn can_bind_to_transient_when_named() -> Result<(), Box<dyn Error>> -    { -        let mut di_container = AsyncDIContainer::new(); - -        { -            assert_eq!(di_container.bindings.lock().await.count(), 0); -        } - -        di_container -            .bind::<dyn subjects::IUserManager>() -            .to::<subjects::UserManager>() -            .await? -            .in_transient_scope() -            .await -            .when_named("regular") -            .await?; - -        { -            assert_eq!(di_container.bindings.lock().await.count(), 1); -        } - -        Ok(()) -    } - -    #[tokio::test] -    async fn can_bind_to_singleton() -> Result<(), Box<dyn Error>> -    { -        let mut di_container = AsyncDIContainer::new(); - -        { -            assert_eq!(di_container.bindings.lock().await.count(), 0); -        } - -        di_container -            .bind::<dyn subjects::IUserManager>() -            .to::<subjects::UserManager>() -            .await? -            .in_singleton_scope() -            .await?; - -        { -            assert_eq!(di_container.bindings.lock().await.count(), 1); -        } - -        Ok(()) -    } - -    #[tokio::test] -    async fn can_bind_to_singleton_when_named() -> Result<(), Box<dyn Error>> -    { -        let mut di_container = AsyncDIContainer::new(); - -        { -            assert_eq!(di_container.bindings.lock().await.count(), 0); -        } - -        di_container -            .bind::<dyn subjects::IUserManager>() -            .to::<subjects::UserManager>() -            .await? -            .in_singleton_scope() -            .await? -            .when_named("cool") -            .await?; - -        { -            assert_eq!(di_container.bindings.lock().await.count(), 1); -        } - -        Ok(()) -    } - -    #[tokio::test] -    #[cfg(feature = "factory")] -    async fn can_bind_to_factory() -> Result<(), Box<dyn Error>> -    { -        use crate as syrette; -        use crate::factory; - -        #[factory(threadsafe = true)] -        type IUserManagerFactory = dyn Fn() -> dyn subjects::IUserManager; - -        let mut di_container = AsyncDIContainer::new(); - -        { -            assert_eq!(di_container.bindings.lock().await.count(), 0); -        } - -        di_container -            .bind::<IUserManagerFactory>() -            .to_factory(&|_| { -                Box::new(|| { -                    let user_manager: TransientPtr<dyn subjects::IUserManager> = -                        TransientPtr::new(subjects::UserManager::new()); - -                    user_manager -                }) -            }) -            .await?; - -        { -            assert_eq!(di_container.bindings.lock().await.count(), 1); -        } - -        Ok(()) -    } - -    #[tokio::test] -    #[cfg(feature = "factory")] -    async fn can_bind_to_factory_when_named() -> Result<(), Box<dyn Error>> -    { -        use crate as syrette; -        use crate::factory; - -        #[factory(threadsafe = true)] -        type IUserManagerFactory = dyn Fn() -> dyn subjects::IUserManager; - -        let mut di_container = AsyncDIContainer::new(); - -        { -            assert_eq!(di_container.bindings.lock().await.count(), 0); -        } - -        di_container -            .bind::<IUserManagerFactory>() -            .to_factory(&|_| { -                Box::new(|| { -                    let user_manager: TransientPtr<dyn subjects::IUserManager> = -                        TransientPtr::new(subjects::UserManager::new()); - -                    user_manager -                }) -            }) -            .await? -            .when_named("awesome") -            .await?; - -        { -            assert_eq!(di_container.bindings.lock().await.count(), 1); -        } - -        Ok(()) -    } - -    #[tokio::test] -    async fn can_get() -> Result<(), Box<dyn Error>> -    { -        mock! { -            Provider {} - -            #[async_trait] -            impl IAsyncProvider for Provider -            { -                async fn provide( -                    &self, -                    di_container: &Arc<AsyncDIContainer>, -                    dependency_history: Vec<&'static str>, -                ) -> Result<AsyncProvidable, InjectableError>; - -                fn do_clone(&self) -> Box<dyn IAsyncProvider>; -            } -        } - -        let di_container = AsyncDIContainer::new(); - -        let mut mock_provider = MockProvider::new(); - -        mock_provider.expect_do_clone().returning(|| { -            let mut inner_mock_provider = MockProvider::new(); - -            inner_mock_provider.expect_provide().returning(|_, _| { -                Ok(AsyncProvidable::Transient(TransientPtr::new( -                    subjects::UserManager::new(), -                ))) -            }); - -            Box::new(inner_mock_provider) -        }); - -        { -            di_container -                .bindings -                .lock() -                .await -                .set::<dyn subjects::IUserManager>(None, Box::new(mock_provider)); -        } - -        di_container -            .get::<dyn subjects::IUserManager>() -            .await? -            .transient()?; - -        Ok(()) -    } - -    #[tokio::test] -    async fn can_get_named() -> Result<(), Box<dyn Error>> -    { -        mock! { -            Provider {} - -            #[async_trait] -            impl IAsyncProvider for Provider -            { -                async fn provide( -                    &self, -                    di_container: &Arc<AsyncDIContainer>, -                    dependency_history: Vec<&'static str>, -                ) -> Result<AsyncProvidable, InjectableError>; - -                fn do_clone(&self) -> Box<dyn IAsyncProvider>; -            } -        } - -        let di_container = AsyncDIContainer::new(); - -        let mut mock_provider = MockProvider::new(); - -        mock_provider.expect_do_clone().returning(|| { -            let mut inner_mock_provider = MockProvider::new(); - -            inner_mock_provider.expect_provide().returning(|_, _| { -                Ok(AsyncProvidable::Transient(TransientPtr::new( -                    subjects::UserManager::new(), -                ))) -            }); - -            Box::new(inner_mock_provider) -        }); - -        { -            di_container -                .bindings -                .lock() -                .await -                .set::<dyn subjects::IUserManager>( -                    Some("special"), -                    Box::new(mock_provider), -                ); -        } - -        di_container -            .get_named::<dyn subjects::IUserManager>("special") -            .await? -            .transient()?; - -        Ok(()) -    } - -    #[tokio::test] -    async fn can_get_singleton() -> Result<(), Box<dyn Error>> -    { -        mock! { -            Provider {} - -            #[async_trait] -            impl IAsyncProvider for Provider -            { -                async fn provide( -                    &self, -                    di_container: &Arc<AsyncDIContainer>, -                    dependency_history: Vec<&'static str>, -                ) -> Result<AsyncProvidable, InjectableError>; - -                fn do_clone(&self) -> Box<dyn IAsyncProvider>; -            } -        } - -        let di_container = AsyncDIContainer::new(); - -        let mut mock_provider = MockProvider::new(); - -        let mut singleton = ThreadsafeSingletonPtr::new(subjects::Number::new()); - -        ThreadsafeSingletonPtr::get_mut(&mut singleton).unwrap().num = 2820; - -        mock_provider.expect_do_clone().returning(move || { -            let mut inner_mock_provider = MockProvider::new(); - -            let singleton_clone = singleton.clone(); - -            inner_mock_provider.expect_provide().returning(move |_, _| { -                Ok(AsyncProvidable::Singleton(singleton_clone.clone())) -            }); - -            Box::new(inner_mock_provider) -        }); - -        { -            di_container -                .bindings -                .lock() -                .await -                .set::<dyn subjects::INumber>(None, Box::new(mock_provider)); -        } - -        let first_number_rc = di_container -            .get::<dyn subjects::INumber>() -            .await? -            .threadsafe_singleton()?; - -        assert_eq!(first_number_rc.get(), 2820); - -        let second_number_rc = di_container -            .get::<dyn subjects::INumber>() -            .await? -            .threadsafe_singleton()?; - -        assert_eq!(first_number_rc.as_ref(), second_number_rc.as_ref()); - -        Ok(()) -    } - -    #[tokio::test] -    async fn can_get_singleton_named() -> Result<(), Box<dyn Error>> -    { -        mock! { -            Provider {} - -            #[async_trait] -            impl IAsyncProvider for Provider -            { -                async fn provide( -                    &self, -                    di_container: &Arc<AsyncDIContainer>, -                    dependency_history: Vec<&'static str>, -                ) -> Result<AsyncProvidable, InjectableError>; - -                fn do_clone(&self) -> Box<dyn IAsyncProvider>; -            } -        } - -        let di_container = AsyncDIContainer::new(); - -        let mut mock_provider = MockProvider::new(); - -        let mut singleton = ThreadsafeSingletonPtr::new(subjects::Number::new()); - -        ThreadsafeSingletonPtr::get_mut(&mut singleton).unwrap().num = 2820; - -        mock_provider.expect_do_clone().returning(move || { -            let mut inner_mock_provider = MockProvider::new(); - -            let singleton_clone = singleton.clone(); - -            inner_mock_provider.expect_provide().returning(move |_, _| { -                Ok(AsyncProvidable::Singleton(singleton_clone.clone())) -            }); - -            Box::new(inner_mock_provider) -        }); - -        { -            di_container -                .bindings -                .lock() -                .await -                .set::<dyn subjects::INumber>(Some("cool"), Box::new(mock_provider)); -        } - -        let first_number_rc = di_container -            .get_named::<dyn subjects::INumber>("cool") -            .await? -            .threadsafe_singleton()?; - -        assert_eq!(first_number_rc.get(), 2820); - -        let second_number_rc = di_container -            .get_named::<dyn subjects::INumber>("cool") -            .await? -            .threadsafe_singleton()?; - -        assert_eq!(first_number_rc.as_ref(), second_number_rc.as_ref()); - -        Ok(()) -    } - -    #[tokio::test] -    #[cfg(feature = "factory")] -    async fn can_get_factory() -> Result<(), Box<dyn Error>> -    { -        trait IUserManager: Send + Sync -        { -            fn add_user(&mut self, user_id: i128); - -            fn remove_user(&mut self, user_id: i128); -        } - -        struct UserManager -        { -            users: Vec<i128>, -        } - -        impl UserManager -        { -            fn new(users: Vec<i128>) -> Self -            { -                Self { users } -            } -        } - -        impl IUserManager for UserManager -        { -            fn add_user(&mut self, user_id: i128) -            { -                self.users.push(user_id); -            } - -            fn remove_user(&mut self, user_id: i128) -            { -                let user_index = -                    self.users.iter().position(|user| *user == user_id).unwrap(); - -                self.users.remove(user_index); -            } -        } - -        use crate as syrette; - -        #[crate::factory(threadsafe = true)] -        type IUserManagerFactory = dyn Fn(Vec<i128>) -> dyn IUserManager; - -        mock! { -            Provider {} - -            #[async_trait] -            impl IAsyncProvider for Provider -            { -                async fn provide( -                    &self, -                    di_container: &Arc<AsyncDIContainer>, -                    dependency_history: Vec<&'static str>, -                ) -> Result<AsyncProvidable, InjectableError>; - -                fn do_clone(&self) -> Box<dyn IAsyncProvider>; -            } -        } - -        let di_container = AsyncDIContainer::new(); - -        let mut mock_provider = MockProvider::new(); - -        mock_provider.expect_do_clone().returning(|| { -            type FactoryFunc = Box< -                (dyn Fn<(Vec<i128>,), Output = TransientPtr<dyn IUserManager>> + Send + Sync) -            >; - -            let mut inner_mock_provider = MockProvider::new(); - -            let factory_func: &'static (dyn Fn< -                (Arc<AsyncDIContainer>,), -                Output = FactoryFunc> + Send + Sync) = &|_| { -                Box::new(|users| { -                    let user_manager: TransientPtr<dyn IUserManager> = -                        TransientPtr::new(UserManager::new(users)); - -                    user_manager -                }) -            }; - -            inner_mock_provider.expect_provide().returning(|_, _| { -                Ok(AsyncProvidable::Factory( -                    crate::ptr::ThreadsafeFactoryPtr::new( -                        ThreadsafeCastableFactory::new(factory_func), -                    ), -                )) -            }); - -            Box::new(inner_mock_provider) -        }); - -        { -            di_container -                .bindings -                .lock() -                .await -                .set::<IUserManagerFactory>(None, Box::new(mock_provider)); -        } - -        di_container -            .get::<IUserManagerFactory>() -            .await? -            .threadsafe_factory()?; - -        Ok(()) -    } - -    #[tokio::test] -    #[cfg(feature = "factory")] -    async fn can_get_factory_named() -> Result<(), Box<dyn Error>> -    { -        trait IUserManager: Send + Sync -        { -            fn add_user(&mut self, user_id: i128); - -            fn remove_user(&mut self, user_id: i128); -        } - -        struct UserManager -        { -            users: Vec<i128>, -        } - -        impl UserManager -        { -            fn new(users: Vec<i128>) -> Self -            { -                Self { users } -            } -        } - -        impl IUserManager for UserManager -        { -            fn add_user(&mut self, user_id: i128) -            { -                self.users.push(user_id); -            } - -            fn remove_user(&mut self, user_id: i128) -            { -                let user_index = -                    self.users.iter().position(|user| *user == user_id).unwrap(); - -                self.users.remove(user_index); -            } -        } - -        use crate as syrette; - -        #[crate::factory(threadsafe = true)] -        type IUserManagerFactory = dyn Fn(Vec<i128>) -> dyn IUserManager; - -        mock! { -            Provider {} - -            #[async_trait] -            impl IAsyncProvider for Provider -            { -                async fn provide( -                    &self, -                    di_container: &Arc<AsyncDIContainer>, -                    dependency_history: Vec<&'static str>, -                ) -> Result<AsyncProvidable, InjectableError>; - -                fn do_clone(&self) -> Box<dyn IAsyncProvider>; -            } -        } - -        let di_container = AsyncDIContainer::new(); - -        let mut mock_provider = MockProvider::new(); - -        mock_provider.expect_do_clone().returning(|| { -            type FactoryFunc = Box< -                (dyn Fn<(Vec<i128>,), Output = TransientPtr<dyn IUserManager>> + Send + Sync) -            >; - -            let mut inner_mock_provider = MockProvider::new(); - -            let factory_func: &'static (dyn Fn< -                (Arc<AsyncDIContainer>,), -                Output = FactoryFunc> + Send + Sync) = &|_| { -                Box::new(|users| { -                    let user_manager: TransientPtr<dyn IUserManager> = -                        TransientPtr::new(UserManager::new(users)); - -                    user_manager -                }) -            }; - -            inner_mock_provider.expect_provide().returning(|_, _| { -                Ok(AsyncProvidable::Factory( -                    crate::ptr::ThreadsafeFactoryPtr::new( -                        ThreadsafeCastableFactory::new(factory_func), -                    ), -                )) -            }); - -            Box::new(inner_mock_provider) -        }); - -        { -            di_container -                .bindings -                .lock() -                .await -                .set::<IUserManagerFactory>(Some("special"), Box::new(mock_provider)); -        } - -        di_container -            .get_named::<IUserManagerFactory>("special") -            .await? -            .threadsafe_factory()?; - -        Ok(()) -    } -} diff --git a/src/di_container.rs b/src/di_container.rs deleted file mode 100644 index 2ed1e25..0000000 --- a/src/di_container.rs +++ /dev/null @@ -1,1267 +0,0 @@ -//! Dependency injection container. -//! -//! # Examples -//! ``` -//! use std::collections::HashMap; -//! use std::error::Error; -//! -//! use syrette::{injectable, DIContainer}; -//! -//! trait IDatabaseService -//! { -//!     fn get_all_records(&self, table_name: String) -> HashMap<String, String>; -//! } -//! -//! struct DatabaseService {} -//! -//! #[injectable(IDatabaseService)] -//! impl DatabaseService -//! { -//!     fn new() -> Self -//!     { -//!         Self {} -//!     } -//! } -//! -//! impl IDatabaseService for DatabaseService -//! { -//!     fn get_all_records(&self, table_name: String) -> HashMap<String, String> -//!     { -//!         // Do stuff here -//!         HashMap::<String, String>::new() -//!     } -//! } -//! -//! fn main() -> Result<(), Box<dyn Error>> -//! { -//!     let mut di_container = DIContainer::new(); -//! -//!     di_container -//!         .bind::<dyn IDatabaseService>() -//!         .to::<DatabaseService>() -//!         .map_err(|err| err.to_string())?; -//! -//!     let database_service = di_container -//!         .get::<dyn IDatabaseService>() -//!         .map_err(|err| err.to_string())? -//!         .transient()?; -//! -//!     Ok(()) -//! } -//! ``` -use std::any::type_name; -use std::cell::RefCell; -use std::marker::PhantomData; -use std::rc::Rc; - -#[cfg(feature = "factory")] -use crate::castable_factory::blocking::CastableFactory; -use crate::di_container_binding_map::DIContainerBindingMap; -use crate::errors::di_container::{ -    BindingBuilderError, -    BindingScopeConfiguratorError, -    BindingWhenConfiguratorError, -    DIContainerError, -}; -use crate::interfaces::injectable::Injectable; -use crate::libs::intertrait::cast::{CastBox, CastRc}; -use crate::provider::blocking::{ -    IProvider, -    Providable, -    SingletonProvider, -    TransientTypeProvider, -}; -use crate::ptr::{SingletonPtr, SomePtr}; - -/// When configurator for a binding for type 'Interface' inside a [`DIContainer`]. -pub struct BindingWhenConfigurator<Interface> -where -    Interface: 'static + ?Sized, -{ -    di_container: Rc<DIContainer>, -    interface_phantom: PhantomData<Interface>, -} - -impl<Interface> BindingWhenConfigurator<Interface> -where -    Interface: 'static + ?Sized, -{ -    fn new(di_container: Rc<DIContainer>) -> Self -    { -        Self { -            di_container, -            interface_phantom: PhantomData, -        } -    } - -    /// Configures the binding to have a name. -    /// -    /// # Errors -    /// Will return Err if no binding for the interface already exists. -    pub fn when_named( -        &self, -        name: &'static str, -    ) -> Result<(), BindingWhenConfiguratorError> -    { -        let mut bindings_mut = self.di_container.bindings.borrow_mut(); - -        let binding = bindings_mut.remove::<Interface>(None).map_or_else( -            || { -                Err(BindingWhenConfiguratorError::BindingNotFound(type_name::< -                    Interface, -                >( -                ))) -            }, -            Ok, -        )?; - -        bindings_mut.set::<Interface>(Some(name), binding); - -        Ok(()) -    } -} - -/// Scope configurator for a binding for type 'Interface' inside a [`DIContainer`]. -pub struct BindingScopeConfigurator<Interface, Implementation> -where -    Interface: 'static + ?Sized, -    Implementation: Injectable, -{ -    di_container: Rc<DIContainer>, -    interface_phantom: PhantomData<Interface>, -    implementation_phantom: PhantomData<Implementation>, -} - -impl<Interface, Implementation> BindingScopeConfigurator<Interface, Implementation> -where -    Interface: 'static + ?Sized, -    Implementation: Injectable, -{ -    fn new(di_container: Rc<DIContainer>) -> Self -    { -        Self { -            di_container, -            interface_phantom: PhantomData, -            implementation_phantom: PhantomData, -        } -    } - -    /// Configures the binding to be in a transient scope. -    /// -    /// This is the default. -    #[allow(clippy::must_use_candidate)] -    pub fn in_transient_scope(&self) -> BindingWhenConfigurator<Interface> -    { -        let mut bindings_mut = self.di_container.bindings.borrow_mut(); - -        bindings_mut.set::<Interface>( -            None, -            Box::new(TransientTypeProvider::<Implementation>::new()), -        ); - -        BindingWhenConfigurator::new(self.di_container.clone()) -    } - -    /// Configures the binding to be in a singleton scope. -    /// -    /// # Errors -    /// Will return Err if resolving the implementation fails. -    pub fn in_singleton_scope( -        &self, -    ) -> Result<BindingWhenConfigurator<Interface>, BindingScopeConfiguratorError> -    { -        let singleton: SingletonPtr<Implementation> = SingletonPtr::from( -            Implementation::resolve(&self.di_container, Vec::new()) -                .map_err(BindingScopeConfiguratorError::SingletonResolveFailed)?, -        ); - -        let mut bindings_mut = self.di_container.bindings.borrow_mut(); - -        bindings_mut.set::<Interface>(None, Box::new(SingletonProvider::new(singleton))); - -        Ok(BindingWhenConfigurator::new(self.di_container.clone())) -    } -} - -/// Binding builder for type `Interface` inside a [`DIContainer`]. -pub struct BindingBuilder<Interface> -where -    Interface: 'static + ?Sized, -{ -    di_container: Rc<DIContainer>, -    interface_phantom: PhantomData<Interface>, -} - -impl<Interface> BindingBuilder<Interface> -where -    Interface: 'static + ?Sized, -{ -    fn new(di_container: Rc<DIContainer>) -> Self -    { -        Self { -            di_container, -            interface_phantom: PhantomData, -        } -    } - -    /// Creates a binding of type `Interface` to type `Implementation` inside of the -    /// associated [`DIContainer`]. -    /// -    /// The scope of the binding is transient. But that can be changed by using the -    /// returned [`BindingScopeConfigurator`] -    /// -    /// # Errors -    /// Will return Err if the associated [`DIContainer`] already have a binding for -    /// the interface. -    /// -    /// # Examples -    /// ``` -    /// # use std::error::Error; -    /// # -    /// # use syrette::{DIContainer, injectable}; -    /// # -    /// # trait Foo {} -    /// # -    /// # struct Bar {} -    /// # -    /// # #[injectable(Foo)] -    /// # impl Bar { -    /// #   fn new() -> Self -    /// #   { -    /// #       Self {} -    /// #   } -    /// # } -    /// # -    /// # impl Foo for Bar {} -    /// # -    /// # fn main() -> Result<(), Box<dyn Error>> -    /// # { -    /// # let mut di_container = DIContainer::new(); -    /// # -    /// di_container.bind::<dyn Foo>().to::<Bar>(); -    /// # -    /// # Ok(()) -    /// # } -    /// ``` -    pub fn to<Implementation>( -        &self, -    ) -> Result<BindingScopeConfigurator<Interface, Implementation>, BindingBuilderError> -    where -        Implementation: Injectable, -    { -        { -            let bindings = self.di_container.bindings.borrow(); - -            if bindings.has::<Interface>(None) { -                return Err(BindingBuilderError::BindingAlreadyExists(type_name::< -                    Interface, -                >( -                ))); -            } -        } - -        let binding_scope_configurator = -            BindingScopeConfigurator::new(self.di_container.clone()); - -        binding_scope_configurator.in_transient_scope(); - -        Ok(binding_scope_configurator) -    } - -    /// Creates a binding of factory type `Interface` to a factory inside of the -    /// associated [`DIContainer`]. -    /// -    /// # Errors -    /// Will return Err if the associated [`DIContainer`] already have a binding for -    /// the interface. -    /// -    /// # Examples -    /// ``` -    /// # use std::error::Error; -    /// # -    /// # use syrette::{DIContainer, factory}; -    /// # use syrette::ptr::TransientPtr; -    /// # -    /// # trait ICustomerID {} -    /// # trait ICustomer {} -    /// # -    /// # struct Customer -    /// # { -    /// #   name: String, -    /// #   id: TransientPtr<dyn ICustomerID> -    /// # } -    /// # -    /// # impl Customer { -    /// #   fn new(name: String, id: TransientPtr<dyn ICustomerID>) -> Self -    /// #   { -    /// #       Self { name, id } -    /// #   } -    /// # } -    /// # -    /// # impl ICustomer for Customer {} -    /// # -    /// # #[factory] -    /// # type ICustomerFactory = dyn Fn(String, u32) -> dyn ICustomer; -    /// # -    /// # #[factory] -    /// # type ICustomerIDFactory = dyn Fn(u32) -> dyn ICustomerID; -    /// # -    /// # fn main() -> Result<(), Box<dyn Error>> -    /// # { -    /// # let mut di_container = DIContainer::new(); -    /// # -    /// di_container -    ///     .bind::<ICustomerFactory>() -    ///     .to_factory(&|context| { -    ///         Box::new(move |name, id| { -    ///             let customer_id_factory = context -    ///                 .get::<ICustomerIDFactory>() -    ///                 .unwrap() -    ///                 .factory() -    ///                 .unwrap(); -    /// -    ///             let customer_id = customer_id_factory(id); -    /// -    ///             let customer = TransientPtr::new(Customer::new(name, customer_id)); -    /// -    ///             customer as TransientPtr<dyn ICustomer> -    ///         }) -    ///     }); -    /// # -    /// # Ok(()) -    /// # } -    /// ``` -    #[cfg(feature = "factory")] -    #[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))] -    pub fn to_factory<Args, Return, Func>( -        &self, -        factory_func: &'static Func, -    ) -> Result<BindingWhenConfigurator<Interface>, BindingBuilderError> -    where -        Args: 'static, -        Return: 'static + ?Sized, -        Interface: Fn<Args, Output = crate::ptr::TransientPtr<Return>>, -        Func: Fn<(std::rc::Rc<DIContainer>,), Output = Box<Interface>>, -    { -        { -            let bindings = self.di_container.bindings.borrow(); - -            if bindings.has::<Interface>(None) { -                return Err(BindingBuilderError::BindingAlreadyExists(type_name::< -                    Interface, -                >( -                ))); -            } -        } - -        let mut bindings_mut = self.di_container.bindings.borrow_mut(); - -        let factory_impl = CastableFactory::new(factory_func); - -        bindings_mut.set::<Interface>( -            None, -            Box::new(crate::provider::blocking::FactoryProvider::new( -                crate::ptr::FactoryPtr::new(factory_impl), -                false, -            )), -        ); - -        Ok(BindingWhenConfigurator::new(self.di_container.clone())) -    } - -    /// Creates a binding of type `Interface` to a factory that takes no arguments -    /// inside of the associated [`DIContainer`]. -    /// -    /// # Errors -    /// Will return Err if the associated [`DIContainer`] already have a binding for -    /// the interface. -    /// -    /// # Examples -    /// ``` -    /// # use std::error::Error; -    /// # -    /// # use syrette::{DIContainer, factory}; -    /// # use syrette::ptr::TransientPtr; -    /// # -    /// # trait IBuffer {} -    /// # -    /// # struct Buffer<const SIZE: usize> -    /// # { -    /// #   buf: [u8; SIZE] -    /// # } -    /// # -    /// # impl<const SIZE: usize> Buffer<SIZE> -    /// # { -    /// #   fn new() -> Self -    /// #   { -    /// #       Self { -    /// #           buf: [0; SIZE] -    /// #       } -    /// #   } -    /// # } -    /// # -    /// # impl<const SIZE: usize> IBuffer for Buffer<SIZE> {} -    /// # -    /// # const BUFFER_SIZE: usize = 12; -    /// # -    /// # fn main() -> Result<(), Box<dyn Error>> -    /// # { -    /// # let mut di_container = DIContainer::new(); -    /// # -    /// di_container.bind::<dyn IBuffer>().to_default_factory(&|_| { -    ///     Box::new(|| { -    ///         let buffer = TransientPtr::new(Buffer::<BUFFER_SIZE>::new()); -    /// -    ///         buffer as TransientPtr<dyn IBuffer> -    ///     }) -    /// }); -    /// # -    /// # Ok(()) -    /// # } -    /// ``` -    #[cfg(feature = "factory")] -    #[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))] -    pub fn to_default_factory<Return, FactoryFunc>( -        &self, -        factory_func: &'static FactoryFunc, -    ) -> Result<BindingWhenConfigurator<Interface>, BindingBuilderError> -    where -        Return: 'static + ?Sized, -        FactoryFunc: Fn< -            (Rc<DIContainer>,), -            Output = crate::ptr::TransientPtr< -                dyn Fn<(), Output = crate::ptr::TransientPtr<Return>>, -            >, -        >, -    { -        { -            let bindings = self.di_container.bindings.borrow(); - -            if bindings.has::<Interface>(None) { -                return Err(BindingBuilderError::BindingAlreadyExists(type_name::< -                    Interface, -                >( -                ))); -            } -        } - -        let mut bindings_mut = self.di_container.bindings.borrow_mut(); - -        let factory_impl = CastableFactory::new(factory_func); - -        bindings_mut.set::<Interface>( -            None, -            Box::new(crate::provider::blocking::FactoryProvider::new( -                crate::ptr::FactoryPtr::new(factory_impl), -                true, -            )), -        ); - -        Ok(BindingWhenConfigurator::new(self.di_container.clone())) -    } -} - -/// Dependency injection container. -pub struct DIContainer -{ -    bindings: RefCell<DIContainerBindingMap<dyn IProvider>>, -} - -impl DIContainer -{ -    /// Returns a new `DIContainer`. -    #[must_use] -    pub fn new() -> Rc<Self> -    { -        Rc::new(Self { -            bindings: RefCell::new(DIContainerBindingMap::new()), -        }) -    } - -    /// Returns a new [`BindingBuilder`] for the given interface. -    #[must_use] -    pub fn bind<Interface>(self: &mut Rc<Self>) -> BindingBuilder<Interface> -    where -        Interface: 'static + ?Sized, -    { -        BindingBuilder::<Interface>::new(self.clone()) -    } - -    /// Returns the type bound with `Interface`. -    /// -    /// # Errors -    /// Will return `Err` if: -    /// - No binding for `Interface` exists -    /// - Resolving the binding for `Interface` fails -    /// - Casting the binding for `Interface` fails -    pub fn get<Interface>(self: &Rc<Self>) -> Result<SomePtr<Interface>, DIContainerError> -    where -        Interface: 'static + ?Sized, -    { -        self.get_bound::<Interface>(Vec::new(), None) -    } - -    /// Returns the type bound with `Interface` and the specified name. -    /// -    /// # Errors -    /// Will return `Err` if: -    /// - No binding for `Interface` with name `name` exists -    /// - Resolving the binding for `Interface` fails -    /// - Casting the binding for `Interface` fails -    pub fn get_named<Interface>( -        self: &Rc<Self>, -        name: &'static str, -    ) -> Result<SomePtr<Interface>, DIContainerError> -    where -        Interface: 'static + ?Sized, -    { -        self.get_bound::<Interface>(Vec::new(), Some(name)) -    } - -    #[doc(hidden)] -    pub fn get_bound<Interface>( -        self: &Rc<Self>, -        dependency_history: Vec<&'static str>, -        name: Option<&'static str>, -    ) -> Result<SomePtr<Interface>, DIContainerError> -    where -        Interface: 'static + ?Sized, -    { -        let binding_providable = -            self.get_binding_providable::<Interface>(name, dependency_history)?; - -        self.handle_binding_providable(binding_providable) -    } - -    fn handle_binding_providable<Interface>( -        self: &Rc<Self>, -        binding_providable: Providable, -    ) -> Result<SomePtr<Interface>, DIContainerError> -    where -        Interface: 'static + ?Sized, -    { -        match binding_providable { -            Providable::Transient(transient_binding) => Ok(SomePtr::Transient( -                transient_binding.cast::<Interface>().map_err(|_| { -                    DIContainerError::CastFailed { -                        interface: type_name::<Interface>(), -                        binding_kind: "transient", -                    } -                })?, -            )), -            Providable::Singleton(singleton_binding) => Ok(SomePtr::Singleton( -                singleton_binding.cast::<Interface>().map_err(|_| { -                    DIContainerError::CastFailed { -                        interface: type_name::<Interface>(), -                        binding_kind: "singleton", -                    } -                })?, -            )), -            #[cfg(feature = "factory")] -            Providable::Factory(factory_binding) => { -                use crate::interfaces::factory::IFactory; - -                let factory = factory_binding -                    .cast::<dyn IFactory<(Rc<DIContainer>,), Interface>>() -                    .map_err(|_| DIContainerError::CastFailed { -                        interface: type_name::<Interface>(), -                        binding_kind: "factory", -                    })?; - -                Ok(SomePtr::Factory(factory(self.clone()).into())) -            } -            #[cfg(feature = "factory")] -            Providable::DefaultFactory(factory_binding) => { -                use crate::interfaces::factory::IFactory; -                use crate::ptr::TransientPtr; - -                let default_factory = factory_binding -                    .cast::<dyn IFactory< -                        (Rc<DIContainer>,), -                        dyn Fn<(), Output = TransientPtr<Interface>>, -                    >>() -                    .map_err(|_| DIContainerError::CastFailed { -                        interface: type_name::<Interface>(), -                        binding_kind: "default factory", -                    })?; - -                Ok(SomePtr::Transient(default_factory(self.clone())())) -            } -        } -    } - -    fn get_binding_providable<Interface>( -        self: &Rc<Self>, -        name: Option<&'static str>, -        dependency_history: Vec<&'static str>, -    ) -> Result<Providable, DIContainerError> -    where -        Interface: 'static + ?Sized, -    { -        self.bindings -            .borrow() -            .get::<Interface>(name) -            .map_or_else( -                || { -                    Err(DIContainerError::BindingNotFound { -                        interface: type_name::<Interface>(), -                        name, -                    }) -                }, -                Ok, -            )? -            .provide(self, dependency_history) -            .map_err(|err| DIContainerError::BindingResolveFailed { -                reason: err, -                interface: type_name::<Interface>(), -            }) -    } -} - -#[cfg(test)] -mod tests -{ -    use std::error::Error; - -    use mockall::mock; - -    use super::*; -    use crate::errors::injectable::InjectableError; -    use crate::provider::blocking::IProvider; -    use crate::ptr::TransientPtr; - -    mod subjects -    { -        //! Test subjects. - -        use std::fmt::Debug; -        use std::rc::Rc; - -        use syrette_macros::declare_interface; - -        use super::DIContainer; -        use crate::interfaces::injectable::Injectable; -        use crate::ptr::TransientPtr; - -        pub trait IUserManager -        { -            fn add_user(&self, user_id: i128); - -            fn remove_user(&self, user_id: i128); -        } - -        pub struct UserManager {} - -        impl UserManager -        { -            pub fn new() -> Self -            { -                Self {} -            } -        } - -        impl IUserManager for UserManager -        { -            fn add_user(&self, _user_id: i128) -            { -                // ... -            } - -            fn remove_user(&self, _user_id: i128) -            { -                // ... -            } -        } - -        use crate as syrette; - -        declare_interface!(UserManager -> IUserManager); - -        impl Injectable for UserManager -        { -            fn resolve( -                _di_container: &Rc<DIContainer>, -                _dependency_history: Vec<&'static str>, -            ) -> Result<TransientPtr<Self>, crate::errors::injectable::InjectableError> -            where -                Self: Sized, -            { -                Ok(TransientPtr::new(Self::new())) -            } -        } - -        pub trait INumber -        { -            fn get(&self) -> i32; - -            fn set(&mut self, number: i32); -        } - -        impl PartialEq for dyn INumber -        { -            fn eq(&self, other: &Self) -> bool -            { -                self.get() == other.get() -            } -        } - -        impl Debug for dyn INumber -        { -            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result -            { -                f.write_str(format!("{}", self.get()).as_str()) -            } -        } - -        pub struct Number -        { -            pub num: i32, -        } - -        impl Number -        { -            pub fn new() -> Self -            { -                Self { num: 0 } -            } -        } - -        impl INumber for Number -        { -            fn get(&self) -> i32 -            { -                self.num -            } - -            fn set(&mut self, number: i32) -            { -                self.num = number; -            } -        } - -        declare_interface!(Number -> INumber); - -        impl Injectable for Number -        { -            fn resolve( -                _di_container: &Rc<DIContainer>, -                _dependency_history: Vec<&'static str>, -            ) -> Result<TransientPtr<Self>, crate::errors::injectable::InjectableError> -            where -                Self: Sized, -            { -                Ok(TransientPtr::new(Self::new())) -            } -        } -    } - -    #[test] -    fn can_bind_to() -> Result<(), Box<dyn Error>> -    { -        let mut di_container = DIContainer::new(); - -        assert_eq!(di_container.bindings.borrow().count(), 0); - -        di_container -            .bind::<dyn subjects::IUserManager>() -            .to::<subjects::UserManager>()?; - -        assert_eq!(di_container.bindings.borrow().count(), 1); - -        Ok(()) -    } - -    #[test] -    fn can_bind_to_transient() -> Result<(), Box<dyn Error>> -    { -        let mut di_container = DIContainer::new(); - -        assert_eq!(di_container.bindings.borrow().count(), 0); - -        di_container -            .bind::<dyn subjects::IUserManager>() -            .to::<subjects::UserManager>()? -            .in_transient_scope(); - -        assert_eq!(di_container.bindings.borrow().count(), 1); - -        Ok(()) -    } - -    #[test] -    fn can_bind_to_transient_when_named() -> Result<(), Box<dyn Error>> -    { -        let mut di_container = DIContainer::new(); - -        assert_eq!(di_container.bindings.borrow().count(), 0); - -        di_container -            .bind::<dyn subjects::IUserManager>() -            .to::<subjects::UserManager>()? -            .in_transient_scope() -            .when_named("regular")?; - -        assert_eq!(di_container.bindings.borrow().count(), 1); - -        Ok(()) -    } - -    #[test] -    fn can_bind_to_singleton() -> Result<(), Box<dyn Error>> -    { -        let mut di_container = DIContainer::new(); - -        assert_eq!(di_container.bindings.borrow().count(), 0); - -        di_container -            .bind::<dyn subjects::IUserManager>() -            .to::<subjects::UserManager>()? -            .in_singleton_scope()?; - -        assert_eq!(di_container.bindings.borrow().count(), 1); - -        Ok(()) -    } - -    #[test] -    fn can_bind_to_singleton_when_named() -> Result<(), Box<dyn Error>> -    { -        let mut di_container = DIContainer::new(); - -        assert_eq!(di_container.bindings.borrow().count(), 0); - -        di_container -            .bind::<dyn subjects::IUserManager>() -            .to::<subjects::UserManager>()? -            .in_singleton_scope()? -            .when_named("cool")?; - -        assert_eq!(di_container.bindings.borrow().count(), 1); - -        Ok(()) -    } - -    #[test] -    #[cfg(feature = "factory")] -    fn can_bind_to_factory() -> Result<(), Box<dyn Error>> -    { -        use crate as syrette; -        use crate::factory; - -        #[factory] -        type IUserManagerFactory = dyn Fn() -> dyn subjects::IUserManager; - -        let mut di_container = DIContainer::new(); - -        assert_eq!(di_container.bindings.borrow().count(), 0); - -        di_container -            .bind::<IUserManagerFactory>() -            .to_factory(&|_| { -                Box::new(move || { -                    let user_manager: TransientPtr<dyn subjects::IUserManager> = -                        TransientPtr::new(subjects::UserManager::new()); - -                    user_manager -                }) -            })?; - -        assert_eq!(di_container.bindings.borrow().count(), 1); - -        Ok(()) -    } - -    #[test] -    #[cfg(feature = "factory")] -    fn can_bind_to_factory_when_named() -> Result<(), Box<dyn Error>> -    { -        use crate as syrette; -        use crate::factory; - -        #[factory] -        type IUserManagerFactory = dyn Fn() -> dyn subjects::IUserManager; - -        let mut di_container = DIContainer::new(); - -        assert_eq!(di_container.bindings.borrow().count(), 0); - -        di_container -            .bind::<IUserManagerFactory>() -            .to_factory(&|_| { -                Box::new(move || { -                    let user_manager: TransientPtr<dyn subjects::IUserManager> = -                        TransientPtr::new(subjects::UserManager::new()); - -                    user_manager -                }) -            })? -            .when_named("awesome")?; - -        assert_eq!(di_container.bindings.borrow().count(), 1); - -        Ok(()) -    } - -    #[test] -    fn can_get() -> Result<(), Box<dyn Error>> -    { -        mock! { -            Provider {} - -            impl IProvider for Provider -            { -                fn provide( -                    &self, -                    di_container: &Rc<DIContainer>, -                    dependency_history: Vec<&'static str>, -                ) -> Result<Providable, InjectableError>; -            } -        } - -        let di_container = DIContainer::new(); - -        let mut mock_provider = MockProvider::new(); - -        mock_provider.expect_provide().returning(|_, _| { -            Ok(Providable::Transient(TransientPtr::new( -                subjects::UserManager::new(), -            ))) -        }); - -        di_container -            .bindings -            .borrow_mut() -            .set::<dyn subjects::IUserManager>(None, Box::new(mock_provider)); - -        di_container -            .get::<dyn subjects::IUserManager>()? -            .transient()?; - -        Ok(()) -    } - -    #[test] -    fn can_get_named() -> Result<(), Box<dyn Error>> -    { -        mock! { -            Provider {} - -            impl IProvider for Provider -            { -                fn provide( -                    &self, -                    di_container: &Rc<DIContainer>, -                    dependency_history: Vec<&'static str>, -                ) -> Result<Providable, InjectableError>; -            } -        } - -        let di_container = DIContainer::new(); - -        let mut mock_provider = MockProvider::new(); - -        mock_provider.expect_provide().returning(|_, _| { -            Ok(Providable::Transient(TransientPtr::new( -                subjects::UserManager::new(), -            ))) -        }); - -        di_container -            .bindings -            .borrow_mut() -            .set::<dyn subjects::IUserManager>(Some("special"), Box::new(mock_provider)); - -        di_container -            .get_named::<dyn subjects::IUserManager>("special")? -            .transient()?; - -        Ok(()) -    } - -    #[test] -    fn can_get_singleton() -> Result<(), Box<dyn Error>> -    { -        mock! { -            Provider {} - -            impl IProvider for Provider -            { -                fn provide( -                    &self, -                    di_container: &Rc<DIContainer>, -                    dependency_history: Vec<&'static str>, -                ) -> Result<Providable, InjectableError>; -            } -        } - -        let di_container = DIContainer::new(); - -        let mut mock_provider = MockProvider::new(); - -        let mut singleton = SingletonPtr::new(subjects::Number::new()); - -        SingletonPtr::get_mut(&mut singleton).unwrap().num = 2820; - -        mock_provider -            .expect_provide() -            .returning_st(move |_, _| Ok(Providable::Singleton(singleton.clone()))); - -        di_container -            .bindings -            .borrow_mut() -            .set::<dyn subjects::INumber>(None, Box::new(mock_provider)); - -        let first_number_rc = di_container.get::<dyn subjects::INumber>()?.singleton()?; - -        assert_eq!(first_number_rc.get(), 2820); - -        let second_number_rc = -            di_container.get::<dyn subjects::INumber>()?.singleton()?; - -        assert_eq!(first_number_rc.as_ref(), second_number_rc.as_ref()); - -        Ok(()) -    } - -    #[test] -    fn can_get_singleton_named() -> Result<(), Box<dyn Error>> -    { -        mock! { -            Provider {} - -            impl IProvider for Provider -            { -                fn provide( -                    &self, -                    di_container: &Rc<DIContainer>, -                    dependency_history: Vec<&'static str>, -                ) -> Result<Providable, InjectableError>; -            } -        } - -        let di_container = DIContainer::new(); - -        let mut mock_provider = MockProvider::new(); - -        let mut singleton = SingletonPtr::new(subjects::Number::new()); - -        SingletonPtr::get_mut(&mut singleton).unwrap().num = 2820; - -        mock_provider -            .expect_provide() -            .returning_st(move |_, _| Ok(Providable::Singleton(singleton.clone()))); - -        di_container -            .bindings -            .borrow_mut() -            .set::<dyn subjects::INumber>(Some("cool"), Box::new(mock_provider)); - -        let first_number_rc = di_container -            .get_named::<dyn subjects::INumber>("cool")? -            .singleton()?; - -        assert_eq!(first_number_rc.get(), 2820); - -        let second_number_rc = di_container -            .get_named::<dyn subjects::INumber>("cool")? -            .singleton()?; - -        assert_eq!(first_number_rc.as_ref(), second_number_rc.as_ref()); - -        Ok(()) -    } - -    #[test] -    #[cfg(feature = "factory")] -    fn can_get_factory() -> Result<(), Box<dyn Error>> -    { -        use crate::ptr::FactoryPtr; - -        trait IUserManager -        { -            fn add_user(&mut self, user_id: i128); - -            fn remove_user(&mut self, user_id: i128); -        } - -        struct UserManager -        { -            users: Vec<i128>, -        } - -        impl UserManager -        { -            fn new(users: Vec<i128>) -> Self -            { -                Self { users } -            } -        } - -        impl IUserManager for UserManager -        { -            fn add_user(&mut self, user_id: i128) -            { -                self.users.push(user_id); -            } - -            fn remove_user(&mut self, user_id: i128) -            { -                let user_index = -                    self.users.iter().position(|user| *user == user_id).unwrap(); - -                self.users.remove(user_index); -            } -        } - -        type FactoryFunc = dyn Fn< -            (std::rc::Rc<DIContainer>,), -            Output = Box< -                dyn Fn<(Vec<i128>,), Output = crate::ptr::TransientPtr<dyn IUserManager>>, -            >, -        >; - -        use crate as syrette; - -        #[crate::factory] -        type IUserManagerFactory = dyn Fn(Vec<i128>) -> dyn IUserManager; - -        mock! { -            Provider {} - -            impl IProvider for Provider -            { -                fn provide( -                    &self, -                    di_container: &Rc<DIContainer>, -                    dependency_history: Vec<&'static str>, -                ) -> Result<Providable, InjectableError>; -            } -        } - -        let di_container = DIContainer::new(); - -        let factory_func: &'static FactoryFunc = &|_: Rc<DIContainer>| { -            Box::new(move |users| { -                let user_manager: TransientPtr<dyn IUserManager> = -                    TransientPtr::new(UserManager::new(users)); - -                user_manager -            }) -        }; - -        let mut mock_provider = MockProvider::new(); - -        mock_provider.expect_provide().returning_st(|_, _| { -            Ok(Providable::Factory(FactoryPtr::new(CastableFactory::new( -                factory_func, -            )))) -        }); - -        di_container -            .bindings -            .borrow_mut() -            .set::<IUserManagerFactory>(None, Box::new(mock_provider)); - -        di_container.get::<IUserManagerFactory>()?.factory()?; - -        Ok(()) -    } - -    #[test] -    #[cfg(feature = "factory")] -    fn can_get_factory_named() -> Result<(), Box<dyn Error>> -    { -        use crate::ptr::FactoryPtr; - -        trait IUserManager -        { -            fn add_user(&mut self, user_id: i128); - -            fn remove_user(&mut self, user_id: i128); -        } - -        struct UserManager -        { -            users: Vec<i128>, -        } - -        impl UserManager -        { -            fn new(users: Vec<i128>) -> Self -            { -                Self { users } -            } -        } - -        impl IUserManager for UserManager -        { -            fn add_user(&mut self, user_id: i128) -            { -                self.users.push(user_id); -            } - -            fn remove_user(&mut self, user_id: i128) -            { -                let user_index = -                    self.users.iter().position(|user| *user == user_id).unwrap(); - -                self.users.remove(user_index); -            } -        } - -        type FactoryFunc = dyn Fn< -            (std::rc::Rc<DIContainer>,), -            Output = Box< -                dyn Fn<(Vec<i128>,), Output = crate::ptr::TransientPtr<dyn IUserManager>>, -            >, -        >; - -        use crate as syrette; - -        #[crate::factory] -        type IUserManagerFactory = dyn Fn(Vec<i128>) -> dyn IUserManager; - -        mock! { -            Provider {} - -            impl IProvider for Provider -            { -                fn provide( -                    &self, -                    di_container: &Rc<DIContainer>, -                    dependency_history: Vec<&'static str>, -                ) -> Result<Providable, InjectableError>; -            } -        } - -        let di_container = DIContainer::new(); - -        let factory_func: &'static FactoryFunc = &|_: Rc<DIContainer>| { -            Box::new(move |users| { -                let user_manager: TransientPtr<dyn IUserManager> = -                    TransientPtr::new(UserManager::new(users)); - -                user_manager -            }) -        }; - -        let mut mock_provider = MockProvider::new(); - -        mock_provider.expect_provide().returning_st(|_, _| { -            Ok(Providable::Factory(FactoryPtr::new(CastableFactory::new( -                factory_func, -            )))) -        }); - -        di_container -            .bindings -            .borrow_mut() -            .set::<IUserManagerFactory>(Some("special"), Box::new(mock_provider)); - -        di_container -            .get_named::<IUserManagerFactory>("special")? -            .factory()?; - -        Ok(()) -    } -} diff --git a/src/di_container/asynchronous/binding/builder.rs b/src/di_container/asynchronous/binding/builder.rs new file mode 100644 index 0000000..6daba30 --- /dev/null +++ b/src/di_container/asynchronous/binding/builder.rs @@ -0,0 +1,653 @@ +//! Binding builder for types inside of a [`AsyncDIContainer`]. +use std::any::type_name; +use std::marker::PhantomData; +use std::sync::Arc; + +use crate::di_container::asynchronous::binding::scope_configurator::AsyncBindingScopeConfigurator; +#[cfg(feature = "factory")] +use crate::di_container::asynchronous::binding::when_configurator::AsyncBindingWhenConfigurator; +use crate::errors::async_di_container::AsyncBindingBuilderError; +use crate::interfaces::async_injectable::AsyncInjectable; +use crate::AsyncDIContainer; + +/// Alias for a threadsafe boxed function. +#[cfg(feature = "factory")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))] +pub type BoxFn<Args, Return> = Box<(dyn Fn<Args, Output = Return> + Send + Sync)>; + +/// Binding builder for type `Interface` inside a [`AsyncDIContainer`]. +pub struct AsyncBindingBuilder<Interface> +where +    Interface: 'static + ?Sized + Send + Sync, +{ +    di_container: Arc<AsyncDIContainer>, +    interface_phantom: PhantomData<Interface>, +} + +impl<Interface> AsyncBindingBuilder<Interface> +where +    Interface: 'static + ?Sized + Send + Sync, +{ +    pub(crate) fn new(di_container: Arc<AsyncDIContainer>) -> Self +    { +        Self { +            di_container, +            interface_phantom: PhantomData, +        } +    } + +    /// Creates a binding of type `Interface` to type `Implementation` inside of the +    /// associated [`AsyncDIContainer`]. +    /// +    /// The scope of the binding is transient. But that can be changed by using the +    /// returned [`AsyncBindingScopeConfigurator`] +    /// +    /// # Errors +    /// Will return Err if the associated [`AsyncDIContainer`] already have a binding for +    /// the interface. +    /// +    /// # Examples +    /// ``` +    /// # use std::error::Error; +    /// # +    /// # use syrette::{AsyncDIContainer, injectable}; +    /// # +    /// # trait Foo: Send + Sync {} +    /// # +    /// # struct Bar {} +    /// # +    /// # #[injectable(Foo, async = true)] +    /// # impl Bar { +    /// #   fn new() -> Self +    /// #   { +    /// #       Self {} +    /// #   } +    /// # } +    /// # +    /// # impl Foo for Bar {} +    /// # +    /// # #[tokio::main] +    /// # async fn main() -> Result<(), Box<dyn Error>> +    /// # { +    /// # let mut di_container = AsyncDIContainer::new(); +    /// # +    /// di_container.bind::<dyn Foo>().to::<Bar>().await?; +    /// # +    /// # Ok(()) +    /// # } +    /// ``` +    pub async fn to<Implementation>( +        &self, +    ) -> Result< +        AsyncBindingScopeConfigurator<Interface, Implementation>, +        AsyncBindingBuilderError, +    > +    where +        Implementation: AsyncInjectable, +    { +        { +            let bindings_lock = self.di_container.bindings.lock().await; + +            if bindings_lock.has::<Interface>(None) { +                return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< +                    Interface, +                >( +                ))); +            } +        } + +        let binding_scope_configurator = +            AsyncBindingScopeConfigurator::new(self.di_container.clone()); + +        binding_scope_configurator.in_transient_scope().await; + +        Ok(binding_scope_configurator) +    } + +    /// Creates a binding of factory type `Interface` to a factory inside of the +    /// associated [`AsyncDIContainer`]. +    /// +    /// # Errors +    /// Will return Err if the associated [`AsyncDIContainer`] already have a binding +    /// for the interface. +    /// +    /// # Examples +    /// ``` +    /// # use std::error::Error; +    /// # +    /// # use syrette::{AsyncDIContainer, factory}; +    /// # use syrette::ptr::TransientPtr; +    /// # +    /// # trait Foo: Send + Sync {} +    /// # +    /// # struct Bar +    /// # { +    /// #   num: i32, +    /// #   some_str: String +    /// # } +    /// # +    /// # impl Foo for Bar {} +    /// # +    /// # #[factory(threadsafe = true)] +    /// # type FooFactory = dyn Fn(i32, String) -> dyn Foo; +    /// # +    /// # #[tokio::main] +    /// # async fn main() -> Result<(), Box<dyn Error>> +    /// # { +    /// # let mut di_container = AsyncDIContainer::new(); +    /// # +    /// di_container +    ///     .bind::<FooFactory>() +    ///     .to_factory(&|_| { +    ///         Box::new(|num, some_str| { +    ///             let bar = TransientPtr::new(Bar { num, some_str }); +    /// +    ///             bar as TransientPtr<dyn Foo> +    ///         }) +    ///     }) +    ///     .await?; +    /// # +    /// # Ok(()) +    /// # } +    /// ``` +    #[cfg(feature = "factory")] +    #[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))] +    pub async fn to_factory<Args, Return, FactoryFunc>( +        &self, +        factory_func: &'static FactoryFunc, +    ) -> Result<AsyncBindingWhenConfigurator<Interface>, AsyncBindingBuilderError> +    where +        Args: 'static, +        Return: 'static + ?Sized, +        Interface: Fn<Args, Output = Return> + Send + Sync, +        FactoryFunc: +            Fn<(Arc<AsyncDIContainer>,), Output = BoxFn<Args, Return>> + Send + Sync, +    { +        use crate::castable_factory::threadsafe::ThreadsafeCastableFactory; +        use crate::provider::r#async::AsyncFactoryVariant; + +        let mut bindings_lock = self.di_container.bindings.lock().await; + +        if bindings_lock.has::<Interface>(None) { +            return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< +                Interface, +            >( +            ))); +        } + +        let factory_impl = ThreadsafeCastableFactory::new(factory_func); + +        bindings_lock.set::<Interface>( +            None, +            Box::new(crate::provider::r#async::AsyncFactoryProvider::new( +                crate::ptr::ThreadsafeFactoryPtr::new(factory_impl), +                AsyncFactoryVariant::Normal, +            )), +        ); + +        Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) +    } + +    /// Creates a binding of factory type `Interface` to a async factory inside of the +    /// associated [`AsyncDIContainer`]. +    /// +    /// # Errors +    /// Will return Err if the associated [`AsyncDIContainer`] already have a binding +    /// for the interface. +    /// +    /// # Examples +    /// ``` +    /// # use std::error::Error; +    /// # use std::time::Duration; +    /// # +    /// # use syrette::{AsyncDIContainer, factory, async_closure}; +    /// # use syrette::ptr::TransientPtr; +    /// # +    /// # trait Foo: Send + Sync {} +    /// # +    /// # struct Bar +    /// # { +    /// #   num: i32, +    /// #   some_str: String +    /// # } +    /// # +    /// # impl Foo for Bar {} +    /// # +    /// # #[factory(async = true)] +    /// # type FooFactory = dyn Fn(i32, String) -> dyn Foo; +    /// # +    /// # #[tokio::main] +    /// # async fn main() -> Result<(), Box<dyn Error>> +    /// # { +    /// # let mut di_container = AsyncDIContainer::new(); +    /// # +    /// di_container +    ///     .bind::<FooFactory>() +    ///     .to_async_factory(&|_| { +    ///         async_closure!(|num, some_str| { +    ///             let bar = TransientPtr::new(Bar { num, some_str }); +    /// +    ///             tokio::time::sleep(Duration::from_secs(2)).await; +    /// +    ///             bar as TransientPtr<dyn Foo> +    ///         }) +    ///     }) +    ///     .await?; +    /// # +    /// # Ok(()) +    /// # } +    /// ``` +    #[cfg(feature = "factory")] +    #[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))] +    pub async fn to_async_factory<Args, Return, FactoryFunc>( +        &self, +        factory_func: &'static FactoryFunc, +    ) -> Result<AsyncBindingWhenConfigurator<Interface>, AsyncBindingBuilderError> +    where +        Args: 'static, +        Return: 'static + ?Sized, +        Interface: +            Fn<Args, Output = crate::future::BoxFuture<'static, Return>> + Send + Sync, +        FactoryFunc: Fn< +                (Arc<AsyncDIContainer>,), +                Output = BoxFn<Args, crate::future::BoxFuture<'static, Return>>, +            > + Send +            + Sync, +    { +        use crate::castable_factory::threadsafe::ThreadsafeCastableFactory; +        use crate::provider::r#async::AsyncFactoryVariant; + +        let mut bindings_lock = self.di_container.bindings.lock().await; + +        if bindings_lock.has::<Interface>(None) { +            return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< +                Interface, +            >( +            ))); +        } + +        let factory_impl = ThreadsafeCastableFactory::new(factory_func); + +        bindings_lock.set::<Interface>( +            None, +            Box::new(crate::provider::r#async::AsyncFactoryProvider::new( +                crate::ptr::ThreadsafeFactoryPtr::new(factory_impl), +                AsyncFactoryVariant::Normal, +            )), +        ); + +        Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) +    } + +    /// Creates a binding of type `Interface` to a factory that takes no arguments +    /// inside of the associated [`AsyncDIContainer`]. +    /// +    /// # Errors +    /// Will return Err if the associated [`AsyncDIContainer`] already have a binding +    /// for the interface. +    /// +    /// # Examples +    /// ``` +    /// # use std::error::Error; +    /// # +    /// # use syrette::AsyncDIContainer; +    /// # use syrette::ptr::TransientPtr; +    /// # +    /// # trait Foo: Send + Sync {} +    /// # +    /// # struct Bar +    /// # { +    /// #   num: i32, +    /// #   some_str: String +    /// # } +    /// # +    /// # impl Foo for Bar {} +    /// # +    /// # #[tokio::main] +    /// # async fn main() -> Result<(), Box<dyn Error>> +    /// # { +    /// # let mut di_container = AsyncDIContainer::new(); +    /// # +    /// di_container +    ///     .bind::<dyn Foo>() +    ///     .to_default_factory(&|_| { +    ///         Box::new(|| { +    ///             let bar = TransientPtr::new(Bar { +    ///                 num: 42, +    ///                 some_str: "hello".to_string(), +    ///             }); +    /// +    ///             bar as TransientPtr<dyn Foo> +    ///         }) +    ///     }) +    ///     .await?; +    /// # +    /// # Ok(()) +    /// # } +    /// ``` +    #[cfg(feature = "factory")] +    #[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))] +    pub async fn to_default_factory<Return, FactoryFunc>( +        &self, +        factory_func: &'static FactoryFunc, +    ) -> Result<AsyncBindingWhenConfigurator<Interface>, AsyncBindingBuilderError> +    where +        Return: 'static + ?Sized, +        FactoryFunc: Fn< +                (Arc<AsyncDIContainer>,), +                Output = BoxFn<(), crate::ptr::TransientPtr<Return>>, +            > + Send +            + Sync, +    { +        use crate::castable_factory::threadsafe::ThreadsafeCastableFactory; +        use crate::provider::r#async::AsyncFactoryVariant; + +        let mut bindings_lock = self.di_container.bindings.lock().await; + +        if bindings_lock.has::<Interface>(None) { +            return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< +                Interface, +            >( +            ))); +        } + +        let factory_impl = ThreadsafeCastableFactory::new(factory_func); + +        bindings_lock.set::<Interface>( +            None, +            Box::new(crate::provider::r#async::AsyncFactoryProvider::new( +                crate::ptr::ThreadsafeFactoryPtr::new(factory_impl), +                AsyncFactoryVariant::Default, +            )), +        ); + +        Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) +    } + +    /// Creates a binding of factory type `Interface` to a async factory inside of the +    /// associated [`AsyncDIContainer`]. +    /// +    /// # Errors +    /// Will return Err if the associated [`AsyncDIContainer`] already have a binding +    /// for the interface. +    /// +    /// # Examples +    /// ``` +    /// # use std::error::Error; +    /// # use std::time::Duration; +    /// # +    /// # use syrette::{AsyncDIContainer, async_closure}; +    /// # use syrette::ptr::TransientPtr; +    /// # +    /// # trait Foo: Send + Sync {} +    /// # +    /// # struct Bar +    /// # { +    /// #   num: i32, +    /// #   some_str: String +    /// # } +    /// # +    /// # impl Foo for Bar {} +    /// # +    /// # #[tokio::main] +    /// # async fn main() -> Result<(), Box<dyn Error>> +    /// # { +    /// # let mut di_container = AsyncDIContainer::new(); +    /// # +    /// di_container +    ///     .bind::<dyn Foo>() +    ///     .to_async_default_factory(&|_| { +    ///         async_closure!(|| { +    ///             let bar = TransientPtr::new(Bar { +    ///                 num: 42, +    ///                 some_str: "hello".to_string(), +    ///             }); +    /// +    ///             tokio::time::sleep(Duration::from_secs(1)).await; +    /// +    ///             bar as TransientPtr<dyn Foo> +    ///         }) +    ///     }) +    ///     .await?; +    /// # +    /// # Ok(()) +    /// # } +    /// ``` +    #[cfg(feature = "factory")] +    #[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))] +    pub async fn to_async_default_factory<Return, FactoryFunc>( +        &self, +        factory_func: &'static FactoryFunc, +    ) -> Result<AsyncBindingWhenConfigurator<Interface>, AsyncBindingBuilderError> +    where +        Return: 'static + ?Sized, +        FactoryFunc: Fn< +                (Arc<AsyncDIContainer>,), +                Output = BoxFn<(), crate::future::BoxFuture<'static, Return>>, +            > + Send +            + Sync, +    { +        use crate::castable_factory::threadsafe::ThreadsafeCastableFactory; +        use crate::provider::r#async::AsyncFactoryVariant; + +        let mut bindings_lock = self.di_container.bindings.lock().await; + +        if bindings_lock.has::<Interface>(None) { +            return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< +                Interface, +            >( +            ))); +        } + +        let factory_impl = ThreadsafeCastableFactory::new(factory_func); + +        bindings_lock.set::<Interface>( +            None, +            Box::new(crate::provider::r#async::AsyncFactoryProvider::new( +                crate::ptr::ThreadsafeFactoryPtr::new(factory_impl), +                AsyncFactoryVariant::AsyncDefault, +            )), +        ); + +        Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) +    } +} + +#[cfg(test)] +mod tests +{ +    use std::error::Error; + +    use super::*; +    use crate::ptr::TransientPtr; +    use crate::test_utils::subjects_async; + +    #[tokio::test] +    async fn can_bind_to() -> Result<(), Box<dyn Error>> +    { +        let mut di_container = AsyncDIContainer::new(); + +        { +            assert_eq!(di_container.bindings.lock().await.count(), 0); +        } + +        di_container +            .bind::<dyn subjects_async::IUserManager>() +            .to::<subjects_async::UserManager>() +            .await?; + +        { +            assert_eq!(di_container.bindings.lock().await.count(), 1); +        } + +        Ok(()) +    } + +    #[tokio::test] +    async fn can_bind_to_transient() -> Result<(), Box<dyn Error>> +    { +        let mut di_container = AsyncDIContainer::new(); + +        { +            assert_eq!(di_container.bindings.lock().await.count(), 0); +        } + +        di_container +            .bind::<dyn subjects_async::IUserManager>() +            .to::<subjects_async::UserManager>() +            .await? +            .in_transient_scope() +            .await; + +        { +            assert_eq!(di_container.bindings.lock().await.count(), 1); +        } + +        Ok(()) +    } + +    #[tokio::test] +    async fn can_bind_to_transient_when_named() -> Result<(), Box<dyn Error>> +    { +        let mut di_container = AsyncDIContainer::new(); + +        { +            assert_eq!(di_container.bindings.lock().await.count(), 0); +        } + +        di_container +            .bind::<dyn subjects_async::IUserManager>() +            .to::<subjects_async::UserManager>() +            .await? +            .in_transient_scope() +            .await +            .when_named("regular") +            .await?; + +        { +            assert_eq!(di_container.bindings.lock().await.count(), 1); +        } + +        Ok(()) +    } + +    #[tokio::test] +    async fn can_bind_to_singleton() -> Result<(), Box<dyn Error>> +    { +        let mut di_container = AsyncDIContainer::new(); + +        { +            assert_eq!(di_container.bindings.lock().await.count(), 0); +        } + +        di_container +            .bind::<dyn subjects_async::IUserManager>() +            .to::<subjects_async::UserManager>() +            .await? +            .in_singleton_scope() +            .await?; + +        { +            assert_eq!(di_container.bindings.lock().await.count(), 1); +        } + +        Ok(()) +    } + +    #[tokio::test] +    async fn can_bind_to_singleton_when_named() -> Result<(), Box<dyn Error>> +    { +        let mut di_container = AsyncDIContainer::new(); + +        { +            assert_eq!(di_container.bindings.lock().await.count(), 0); +        } + +        di_container +            .bind::<dyn subjects_async::IUserManager>() +            .to::<subjects_async::UserManager>() +            .await? +            .in_singleton_scope() +            .await? +            .when_named("cool") +            .await?; + +        { +            assert_eq!(di_container.bindings.lock().await.count(), 1); +        } + +        Ok(()) +    } + +    #[tokio::test] +    #[cfg(feature = "factory")] +    async fn can_bind_to_factory() -> Result<(), Box<dyn Error>> +    { +        use crate as syrette; +        use crate::factory; + +        #[factory(threadsafe = true)] +        type IUserManagerFactory = dyn Fn() -> dyn subjects_async::IUserManager; + +        let mut di_container = AsyncDIContainer::new(); + +        { +            assert_eq!(di_container.bindings.lock().await.count(), 0); +        } + +        di_container +            .bind::<IUserManagerFactory>() +            .to_factory(&|_| { +                Box::new(|| { +                    let user_manager: TransientPtr<dyn subjects_async::IUserManager> = +                        TransientPtr::new(subjects_async::UserManager::new()); + +                    user_manager +                }) +            }) +            .await?; + +        { +            assert_eq!(di_container.bindings.lock().await.count(), 1); +        } + +        Ok(()) +    } + +    #[tokio::test] +    #[cfg(feature = "factory")] +    async fn can_bind_to_factory_when_named() -> Result<(), Box<dyn Error>> +    { +        use crate as syrette; +        use crate::factory; + +        #[factory(threadsafe = true)] +        type IUserManagerFactory = dyn Fn() -> dyn subjects_async::IUserManager; + +        let mut di_container = AsyncDIContainer::new(); + +        { +            assert_eq!(di_container.bindings.lock().await.count(), 0); +        } + +        di_container +            .bind::<IUserManagerFactory>() +            .to_factory(&|_| { +                Box::new(|| { +                    let user_manager: TransientPtr<dyn subjects_async::IUserManager> = +                        TransientPtr::new(subjects_async::UserManager::new()); + +                    user_manager +                }) +            }) +            .await? +            .when_named("awesome") +            .await?; + +        { +            assert_eq!(di_container.bindings.lock().await.count(), 1); +        } + +        Ok(()) +    } +} diff --git a/src/di_container/asynchronous/binding/mod.rs b/src/di_container/asynchronous/binding/mod.rs new file mode 100644 index 0000000..6a09bff --- /dev/null +++ b/src/di_container/asynchronous/binding/mod.rs @@ -0,0 +1,5 @@ +//! Types for building & configurating DI container bindings. + +pub mod builder; +pub mod scope_configurator; +pub mod when_configurator; diff --git a/src/di_container/asynchronous/binding/scope_configurator.rs b/src/di_container/asynchronous/binding/scope_configurator.rs new file mode 100644 index 0000000..2b0f0b3 --- /dev/null +++ b/src/di_container/asynchronous/binding/scope_configurator.rs @@ -0,0 +1,76 @@ +//! Scope configurator for a binding for types inside of a [`AsyncDIContainer`]. +use std::marker::PhantomData; +use std::sync::Arc; + +use crate::di_container::asynchronous::binding::when_configurator::AsyncBindingWhenConfigurator; +use crate::errors::async_di_container::AsyncBindingScopeConfiguratorError; +use crate::interfaces::async_injectable::AsyncInjectable; +use crate::provider::r#async::{AsyncSingletonProvider, AsyncTransientTypeProvider}; +use crate::ptr::ThreadsafeSingletonPtr; +use crate::AsyncDIContainer; + +/// Scope configurator for a binding for type 'Interface' inside a [`AsyncDIContainer`]. +pub struct AsyncBindingScopeConfigurator<Interface, Implementation> +where +    Interface: 'static + ?Sized + Send + Sync, +    Implementation: AsyncInjectable, +{ +    di_container: Arc<AsyncDIContainer>, +    interface_phantom: PhantomData<Interface>, +    implementation_phantom: PhantomData<Implementation>, +} + +impl<Interface, Implementation> AsyncBindingScopeConfigurator<Interface, Implementation> +where +    Interface: 'static + ?Sized + Send + Sync, +    Implementation: AsyncInjectable, +{ +    pub(crate) fn new(di_container: Arc<AsyncDIContainer>) -> Self +    { +        Self { +            di_container, +            interface_phantom: PhantomData, +            implementation_phantom: PhantomData, +        } +    } + +    /// Configures the binding to be in a transient scope. +    /// +    /// This is the default. +    pub async fn in_transient_scope(&self) -> AsyncBindingWhenConfigurator<Interface> +    { +        let mut bindings_lock = self.di_container.bindings.lock().await; + +        bindings_lock.set::<Interface>( +            None, +            Box::new(AsyncTransientTypeProvider::<Implementation>::new()), +        ); + +        AsyncBindingWhenConfigurator::new(self.di_container.clone()) +    } + +    /// Configures the binding to be in a singleton scope. +    /// +    /// # Errors +    /// Will return Err if resolving the implementation fails. +    pub async fn in_singleton_scope( +        &self, +    ) -> Result<AsyncBindingWhenConfigurator<Interface>, AsyncBindingScopeConfiguratorError> +    { +        let singleton: ThreadsafeSingletonPtr<Implementation> = +            ThreadsafeSingletonPtr::from( +                Implementation::resolve(&self.di_container, Vec::new()) +                    .await +                    .map_err( +                        AsyncBindingScopeConfiguratorError::SingletonResolveFailed, +                    )?, +            ); + +        let mut bindings_lock = self.di_container.bindings.lock().await; + +        bindings_lock +            .set::<Interface>(None, Box::new(AsyncSingletonProvider::new(singleton))); + +        Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) +    } +} diff --git a/src/di_container/asynchronous/binding/when_configurator.rs b/src/di_container/asynchronous/binding/when_configurator.rs new file mode 100644 index 0000000..b245ad8 --- /dev/null +++ b/src/di_container/asynchronous/binding/when_configurator.rs @@ -0,0 +1,54 @@ +//! When configurator for a binding for types inside of a [`AsyncDIContainer`]. +use std::any::type_name; +use std::marker::PhantomData; +use std::sync::Arc; + +use crate::errors::async_di_container::AsyncBindingWhenConfiguratorError; +use crate::AsyncDIContainer; + +/// When configurator for a binding for type 'Interface' inside a [`AsyncDIContainer`]. +pub struct AsyncBindingWhenConfigurator<Interface> +where +    Interface: 'static + ?Sized + Send + Sync, +{ +    di_container: Arc<AsyncDIContainer>, +    interface_phantom: PhantomData<Interface>, +} + +impl<Interface> AsyncBindingWhenConfigurator<Interface> +where +    Interface: 'static + ?Sized + Send + Sync, +{ +    pub(crate) fn new(di_container: Arc<AsyncDIContainer>) -> Self +    { +        Self { +            di_container, +            interface_phantom: PhantomData, +        } +    } + +    /// Configures the binding to have a name. +    /// +    /// # Errors +    /// Will return Err if no binding for the interface already exists. +    pub async fn when_named( +        &self, +        name: &'static str, +    ) -> Result<(), AsyncBindingWhenConfiguratorError> +    { +        let mut bindings_lock = self.di_container.bindings.lock().await; + +        let binding = bindings_lock.remove::<Interface>(None).map_or_else( +            || { +                Err(AsyncBindingWhenConfiguratorError::BindingNotFound( +                    type_name::<Interface>(), +                )) +            }, +            Ok, +        )?; + +        bindings_lock.set::<Interface>(Some(name), binding); + +        Ok(()) +    } +} diff --git a/src/di_container/asynchronous/mod.rs b/src/di_container/asynchronous/mod.rs new file mode 100644 index 0000000..b6653a5 --- /dev/null +++ b/src/di_container/asynchronous/mod.rs @@ -0,0 +1,767 @@ +//! Asynchronous dependency injection container. +//! +//! # Examples +//! ``` +//! use std::collections::HashMap; +//! use std::error::Error; +//! +//! use syrette::{injectable, AsyncDIContainer}; +//! +//! trait IDatabaseService: Send + Sync +//! { +//!     fn get_all_records(&self, table_name: String) -> HashMap<String, String>; +//! } +//! +//! struct DatabaseService {} +//! +//! #[injectable(IDatabaseService, async = true)] +//! impl DatabaseService +//! { +//!     fn new() -> Self +//!     { +//!         Self {} +//!     } +//! } +//! +//! impl IDatabaseService for DatabaseService +//! { +//!     fn get_all_records(&self, table_name: String) -> HashMap<String, String> +//!     { +//!         // Do stuff here +//!         HashMap::<String, String>::new() +//!     } +//! } +//! +//! #[tokio::main] +//! async fn main() -> Result<(), Box<dyn Error>> +//! { +//!     let mut di_container = AsyncDIContainer::new(); +//! +//!     di_container +//!         .bind::<dyn IDatabaseService>() +//!         .to::<DatabaseService>() +//!         .await?; +//! +//!     let database_service = di_container +//!         .get::<dyn IDatabaseService>() +//!         .await? +//!         .transient()?; +//! +//!     Ok(()) +//! } +//! ``` +use std::any::type_name; +use std::sync::Arc; + +use tokio::sync::Mutex; + +use crate::di_container::asynchronous::binding::builder::AsyncBindingBuilder; +use crate::di_container::binding_map::DIContainerBindingMap; +use crate::errors::async_di_container::AsyncDIContainerError; +use crate::future::BoxFuture; +use crate::libs::intertrait::cast::error::CastError; +use crate::libs::intertrait::cast::{CastArc, CastBox}; +use crate::provider::r#async::{AsyncProvidable, IAsyncProvider}; +use crate::ptr::{SomeThreadsafePtr, TransientPtr}; + +pub mod binding; + +/// Dependency injection container. +pub struct AsyncDIContainer +{ +    bindings: Mutex<DIContainerBindingMap<dyn IAsyncProvider>>, +} + +impl AsyncDIContainer +{ +    /// Returns a new `AsyncDIContainer`. +    #[must_use] +    pub fn new() -> Arc<Self> +    { +        Arc::new(Self { +            bindings: Mutex::new(DIContainerBindingMap::new()), +        }) +    } + +    /// Returns a new [`AsyncBindingBuilder`] for the given interface. +    #[must_use] +    pub fn bind<Interface>(self: &mut Arc<Self>) -> AsyncBindingBuilder<Interface> +    where +        Interface: 'static + ?Sized + Send + Sync, +    { +        AsyncBindingBuilder::<Interface>::new(self.clone()) +    } + +    /// Returns the type bound with `Interface`. +    /// +    /// # Errors +    /// Will return `Err` if: +    /// - No binding for `Interface` exists +    /// - Resolving the binding for `Interface` fails +    /// - Casting the binding for `Interface` fails +    pub async fn get<Interface>( +        self: &Arc<Self>, +    ) -> Result<SomeThreadsafePtr<Interface>, AsyncDIContainerError> +    where +        Interface: 'static + ?Sized + Send + Sync, +    { +        self.get_bound::<Interface>(Vec::new(), None).await +    } + +    /// Returns the type bound with `Interface` and the specified name. +    /// +    /// # Errors +    /// Will return `Err` if: +    /// - No binding for `Interface` with name `name` exists +    /// - Resolving the binding for `Interface` fails +    /// - Casting the binding for `Interface` fails +    pub async fn get_named<Interface>( +        self: &Arc<Self>, +        name: &'static str, +    ) -> Result<SomeThreadsafePtr<Interface>, AsyncDIContainerError> +    where +        Interface: 'static + ?Sized + Send + Sync, +    { +        self.get_bound::<Interface>(Vec::new(), Some(name)).await +    } + +    #[doc(hidden)] +    pub async fn get_bound<Interface>( +        self: &Arc<Self>, +        dependency_history: Vec<&'static str>, +        name: Option<&'static str>, +    ) -> Result<SomeThreadsafePtr<Interface>, AsyncDIContainerError> +    where +        Interface: 'static + ?Sized + Send + Sync, +    { +        let binding_providable = self +            .get_binding_providable::<Interface>(name, dependency_history) +            .await?; + +        self.handle_binding_providable(binding_providable).await +    } + +    async fn handle_binding_providable<Interface>( +        self: &Arc<Self>, +        binding_providable: AsyncProvidable, +    ) -> Result<SomeThreadsafePtr<Interface>, AsyncDIContainerError> +    where +        Interface: 'static + ?Sized + Send + Sync, +    { +        match binding_providable { +            AsyncProvidable::Transient(transient_binding) => { +                Ok(SomeThreadsafePtr::Transient( +                    transient_binding.cast::<Interface>().map_err(|_| { +                        AsyncDIContainerError::CastFailed { +                            interface: type_name::<Interface>(), +                            binding_kind: "transient", +                        } +                    })?, +                )) +            } +            AsyncProvidable::Singleton(singleton_binding) => { +                Ok(SomeThreadsafePtr::ThreadsafeSingleton( +                    singleton_binding +                        .cast::<Interface>() +                        .map_err(|err| match err { +                            CastError::NotArcCastable(_) => { +                                AsyncDIContainerError::InterfaceNotAsync(type_name::< +                                    Interface, +                                >( +                                )) +                            } +                            CastError::CastFailed { from: _, to: _ } => { +                                AsyncDIContainerError::CastFailed { +                                    interface: type_name::<Interface>(), +                                    binding_kind: "singleton", +                                } +                            } +                        })?, +                )) +            } +            #[cfg(feature = "factory")] +            AsyncProvidable::Factory(factory_binding) => { +                use crate::interfaces::factory::IThreadsafeFactory; + +                let factory = factory_binding +                    .cast::<dyn IThreadsafeFactory<(Arc<AsyncDIContainer>,), Interface>>() +                    .map_err(|err| match err { +                        CastError::NotArcCastable(_) => { +                            AsyncDIContainerError::InterfaceNotAsync( +                                type_name::<Interface>(), +                            ) +                        } +                        CastError::CastFailed { from: _, to: _ } => { +                            AsyncDIContainerError::CastFailed { +                                interface: type_name::<Interface>(), +                                binding_kind: "factory", +                            } +                        } +                    })?; + +                Ok(SomeThreadsafePtr::ThreadsafeFactory( +                    factory(self.clone()).into(), +                )) +            } +            #[cfg(feature = "factory")] +            AsyncProvidable::DefaultFactory(binding) => { +                use crate::interfaces::factory::IThreadsafeFactory; + +                let default_factory = Self::cast_factory_binding::< +                    dyn IThreadsafeFactory< +                        (Arc<AsyncDIContainer>,), +                        dyn Fn<(), Output = TransientPtr<Interface>> + Send + Sync, +                    >, +                >(binding, "default factory")?; + +                Ok(SomeThreadsafePtr::Transient(default_factory(self.clone())())) +            } +            #[cfg(feature = "factory")] +            AsyncProvidable::AsyncDefaultFactory(binding) => { +                use crate::interfaces::factory::IThreadsafeFactory; + +                let async_default_factory = Self::cast_factory_binding::< +                    dyn IThreadsafeFactory< +                        (Arc<AsyncDIContainer>,), +                        dyn Fn<(), Output = BoxFuture<'static, TransientPtr<Interface>>> +                            + Send +                            + Sync, +                    >, +                >( +                    binding, "async default factory" +                )?; + +                Ok(SomeThreadsafePtr::Transient( +                    async_default_factory(self.clone())().await, +                )) +            } +        } +    } + +    #[cfg(feature = "factory")] +    fn cast_factory_binding<Type: 'static + ?Sized>( +        factory_binding: Arc<dyn crate::interfaces::any_factory::AnyThreadsafeFactory>, +        binding_kind: &'static str, +    ) -> Result<Arc<Type>, AsyncDIContainerError> +    { +        factory_binding.cast::<Type>().map_err(|err| match err { +            CastError::NotArcCastable(_) => { +                AsyncDIContainerError::InterfaceNotAsync(type_name::<Type>()) +            } +            CastError::CastFailed { from: _, to: _ } => { +                AsyncDIContainerError::CastFailed { +                    interface: type_name::<Type>(), +                    binding_kind, +                } +            } +        }) +    } + +    async fn get_binding_providable<Interface>( +        self: &Arc<Self>, +        name: Option<&'static str>, +        dependency_history: Vec<&'static str>, +    ) -> Result<AsyncProvidable, AsyncDIContainerError> +    where +        Interface: 'static + ?Sized + Send + Sync, +    { +        let provider; + +        { +            let bindings_lock = self.bindings.lock().await; + +            provider = bindings_lock +                .get::<Interface>(name) +                .map_or_else( +                    || { +                        Err(AsyncDIContainerError::BindingNotFound { +                            interface: type_name::<Interface>(), +                            name, +                        }) +                    }, +                    Ok, +                )? +                .clone(); +        } + +        provider +            .provide(self, dependency_history) +            .await +            .map_err(|err| AsyncDIContainerError::BindingResolveFailed { +                reason: err, +                interface: type_name::<Interface>(), +            }) +    } +} + +#[cfg(test)] +mod tests +{ +    use std::error::Error; + +    use async_trait::async_trait; +    use mockall::mock; + +    use super::*; +    use crate::errors::injectable::InjectableError; +    use crate::ptr::{ThreadsafeSingletonPtr, TransientPtr}; +    use crate::test_utils::subjects_async; + +    #[tokio::test] +    async fn can_get() -> Result<(), Box<dyn Error>> +    { +        mock! { +            Provider {} + +            #[async_trait] +            impl IAsyncProvider for Provider +            { +                async fn provide( +                    &self, +                    di_container: &Arc<AsyncDIContainer>, +                    dependency_history: Vec<&'static str>, +                ) -> Result<AsyncProvidable, InjectableError>; + +                fn do_clone(&self) -> Box<dyn IAsyncProvider>; +            } +        } + +        let di_container = AsyncDIContainer::new(); + +        let mut mock_provider = MockProvider::new(); + +        mock_provider.expect_do_clone().returning(|| { +            let mut inner_mock_provider = MockProvider::new(); + +            inner_mock_provider.expect_provide().returning(|_, _| { +                Ok(AsyncProvidable::Transient(TransientPtr::new( +                    subjects_async::UserManager::new(), +                ))) +            }); + +            Box::new(inner_mock_provider) +        }); + +        { +            di_container +                .bindings +                .lock() +                .await +                .set::<dyn subjects_async::IUserManager>(None, Box::new(mock_provider)); +        } + +        di_container +            .get::<dyn subjects_async::IUserManager>() +            .await? +            .transient()?; + +        Ok(()) +    } + +    #[tokio::test] +    async fn can_get_named() -> Result<(), Box<dyn Error>> +    { +        mock! { +            Provider {} + +            #[async_trait] +            impl IAsyncProvider for Provider +            { +                async fn provide( +                    &self, +                    di_container: &Arc<AsyncDIContainer>, +                    dependency_history: Vec<&'static str>, +                ) -> Result<AsyncProvidable, InjectableError>; + +                fn do_clone(&self) -> Box<dyn IAsyncProvider>; +            } +        } + +        let di_container = AsyncDIContainer::new(); + +        let mut mock_provider = MockProvider::new(); + +        mock_provider.expect_do_clone().returning(|| { +            let mut inner_mock_provider = MockProvider::new(); + +            inner_mock_provider.expect_provide().returning(|_, _| { +                Ok(AsyncProvidable::Transient(TransientPtr::new( +                    subjects_async::UserManager::new(), +                ))) +            }); + +            Box::new(inner_mock_provider) +        }); + +        { +            di_container +                .bindings +                .lock() +                .await +                .set::<dyn subjects_async::IUserManager>( +                    Some("special"), +                    Box::new(mock_provider), +                ); +        } + +        di_container +            .get_named::<dyn subjects_async::IUserManager>("special") +            .await? +            .transient()?; + +        Ok(()) +    } + +    #[tokio::test] +    async fn can_get_singleton() -> Result<(), Box<dyn Error>> +    { +        mock! { +            Provider {} + +            #[async_trait] +            impl IAsyncProvider for Provider +            { +                async fn provide( +                    &self, +                    di_container: &Arc<AsyncDIContainer>, +                    dependency_history: Vec<&'static str>, +                ) -> Result<AsyncProvidable, InjectableError>; + +                fn do_clone(&self) -> Box<dyn IAsyncProvider>; +            } +        } + +        let di_container = AsyncDIContainer::new(); + +        let mut mock_provider = MockProvider::new(); + +        let mut singleton = ThreadsafeSingletonPtr::new(subjects_async::Number::new()); + +        ThreadsafeSingletonPtr::get_mut(&mut singleton).unwrap().num = 2820; + +        mock_provider.expect_do_clone().returning(move || { +            let mut inner_mock_provider = MockProvider::new(); + +            let singleton_clone = singleton.clone(); + +            inner_mock_provider.expect_provide().returning(move |_, _| { +                Ok(AsyncProvidable::Singleton(singleton_clone.clone())) +            }); + +            Box::new(inner_mock_provider) +        }); + +        { +            di_container +                .bindings +                .lock() +                .await +                .set::<dyn subjects_async::INumber>(None, Box::new(mock_provider)); +        } + +        let first_number_rc = di_container +            .get::<dyn subjects_async::INumber>() +            .await? +            .threadsafe_singleton()?; + +        assert_eq!(first_number_rc.get(), 2820); + +        let second_number_rc = di_container +            .get::<dyn subjects_async::INumber>() +            .await? +            .threadsafe_singleton()?; + +        assert_eq!(first_number_rc.as_ref(), second_number_rc.as_ref()); + +        Ok(()) +    } + +    #[tokio::test] +    async fn can_get_singleton_named() -> Result<(), Box<dyn Error>> +    { +        mock! { +            Provider {} + +            #[async_trait] +            impl IAsyncProvider for Provider +            { +                async fn provide( +                    &self, +                    di_container: &Arc<AsyncDIContainer>, +                    dependency_history: Vec<&'static str>, +                ) -> Result<AsyncProvidable, InjectableError>; + +                fn do_clone(&self) -> Box<dyn IAsyncProvider>; +            } +        } + +        let di_container = AsyncDIContainer::new(); + +        let mut mock_provider = MockProvider::new(); + +        let mut singleton = ThreadsafeSingletonPtr::new(subjects_async::Number::new()); + +        ThreadsafeSingletonPtr::get_mut(&mut singleton).unwrap().num = 2820; + +        mock_provider.expect_do_clone().returning(move || { +            let mut inner_mock_provider = MockProvider::new(); + +            let singleton_clone = singleton.clone(); + +            inner_mock_provider.expect_provide().returning(move |_, _| { +                Ok(AsyncProvidable::Singleton(singleton_clone.clone())) +            }); + +            Box::new(inner_mock_provider) +        }); + +        { +            di_container +                .bindings +                .lock() +                .await +                .set::<dyn subjects_async::INumber>( +                    Some("cool"), +                    Box::new(mock_provider), +                ); +        } + +        let first_number_rc = di_container +            .get_named::<dyn subjects_async::INumber>("cool") +            .await? +            .threadsafe_singleton()?; + +        assert_eq!(first_number_rc.get(), 2820); + +        let second_number_rc = di_container +            .get_named::<dyn subjects_async::INumber>("cool") +            .await? +            .threadsafe_singleton()?; + +        assert_eq!(first_number_rc.as_ref(), second_number_rc.as_ref()); + +        Ok(()) +    } + +    #[tokio::test] +    #[cfg(feature = "factory")] +    async fn can_get_factory() -> Result<(), Box<dyn Error>> +    { +        trait IUserManager: Send + Sync +        { +            fn add_user(&mut self, user_id: i128); + +            fn remove_user(&mut self, user_id: i128); +        } + +        struct UserManager +        { +            users: Vec<i128>, +        } + +        impl UserManager +        { +            fn new(users: Vec<i128>) -> Self +            { +                Self { users } +            } +        } + +        impl IUserManager for UserManager +        { +            fn add_user(&mut self, user_id: i128) +            { +                self.users.push(user_id); +            } + +            fn remove_user(&mut self, user_id: i128) +            { +                let user_index = +                    self.users.iter().position(|user| *user == user_id).unwrap(); + +                self.users.remove(user_index); +            } +        } + +        use crate as syrette; +        use crate::castable_factory::threadsafe::ThreadsafeCastableFactory; + +        #[crate::factory(threadsafe = true)] +        type IUserManagerFactory = dyn Fn(Vec<i128>) -> dyn IUserManager; + +        mock! { +            Provider {} + +            #[async_trait] +            impl IAsyncProvider for Provider +            { +                async fn provide( +                    &self, +                    di_container: &Arc<AsyncDIContainer>, +                    dependency_history: Vec<&'static str>, +                ) -> Result<AsyncProvidable, InjectableError>; + +                fn do_clone(&self) -> Box<dyn IAsyncProvider>; +            } +        } + +        let di_container = AsyncDIContainer::new(); + +        let mut mock_provider = MockProvider::new(); + +        mock_provider.expect_do_clone().returning(|| { +            type FactoryFunc = Box< +                (dyn Fn<(Vec<i128>,), Output = TransientPtr<dyn IUserManager>> + Send + Sync) +            >; + +            let mut inner_mock_provider = MockProvider::new(); + +            let factory_func: &'static (dyn Fn< +                (Arc<AsyncDIContainer>,), +                Output = FactoryFunc> + Send + Sync) = &|_| { +                Box::new(|users| { +                    let user_manager: TransientPtr<dyn IUserManager> = +                        TransientPtr::new(UserManager::new(users)); + +                    user_manager +                }) +            }; + +            inner_mock_provider.expect_provide().returning(|_, _| { +                Ok(AsyncProvidable::Factory( +                    crate::ptr::ThreadsafeFactoryPtr::new( +                        ThreadsafeCastableFactory::new(factory_func), +                    ), +                )) +            }); + +            Box::new(inner_mock_provider) +        }); + +        { +            di_container +                .bindings +                .lock() +                .await +                .set::<IUserManagerFactory>(None, Box::new(mock_provider)); +        } + +        di_container +            .get::<IUserManagerFactory>() +            .await? +            .threadsafe_factory()?; + +        Ok(()) +    } + +    #[tokio::test] +    #[cfg(feature = "factory")] +    async fn can_get_factory_named() -> Result<(), Box<dyn Error>> +    { +        trait IUserManager: Send + Sync +        { +            fn add_user(&mut self, user_id: i128); + +            fn remove_user(&mut self, user_id: i128); +        } + +        struct UserManager +        { +            users: Vec<i128>, +        } + +        impl UserManager +        { +            fn new(users: Vec<i128>) -> Self +            { +                Self { users } +            } +        } + +        impl IUserManager for UserManager +        { +            fn add_user(&mut self, user_id: i128) +            { +                self.users.push(user_id); +            } + +            fn remove_user(&mut self, user_id: i128) +            { +                let user_index = +                    self.users.iter().position(|user| *user == user_id).unwrap(); + +                self.users.remove(user_index); +            } +        } + +        use crate as syrette; +        use crate::castable_factory::threadsafe::ThreadsafeCastableFactory; + +        #[crate::factory(threadsafe = true)] +        type IUserManagerFactory = dyn Fn(Vec<i128>) -> dyn IUserManager; + +        mock! { +            Provider {} + +            #[async_trait] +            impl IAsyncProvider for Provider +            { +                async fn provide( +                    &self, +                    di_container: &Arc<AsyncDIContainer>, +                    dependency_history: Vec<&'static str>, +                ) -> Result<AsyncProvidable, InjectableError>; + +                fn do_clone(&self) -> Box<dyn IAsyncProvider>; +            } +        } + +        let di_container = AsyncDIContainer::new(); + +        let mut mock_provider = MockProvider::new(); + +        mock_provider.expect_do_clone().returning(|| { +            type FactoryFunc = Box< +                (dyn Fn<(Vec<i128>,), Output = TransientPtr<dyn IUserManager>> + Send + Sync) +            >; + +            let mut inner_mock_provider = MockProvider::new(); + +            let factory_func: &'static (dyn Fn< +                (Arc<AsyncDIContainer>,), +                Output = FactoryFunc> + Send + Sync) = &|_| { +                Box::new(|users| { +                    let user_manager: TransientPtr<dyn IUserManager> = +                        TransientPtr::new(UserManager::new(users)); + +                    user_manager +                }) +            }; + +            inner_mock_provider.expect_provide().returning(|_, _| { +                Ok(AsyncProvidable::Factory( +                    crate::ptr::ThreadsafeFactoryPtr::new( +                        ThreadsafeCastableFactory::new(factory_func), +                    ), +                )) +            }); + +            Box::new(inner_mock_provider) +        }); + +        { +            di_container +                .bindings +                .lock() +                .await +                .set::<IUserManagerFactory>(Some("special"), Box::new(mock_provider)); +        } + +        di_container +            .get_named::<IUserManagerFactory>("special") +            .await? +            .threadsafe_factory()?; + +        Ok(()) +    } +} diff --git a/src/di_container_binding_map.rs b/src/di_container/binding_map.rs index eb71ff7..eb71ff7 100644 --- a/src/di_container_binding_map.rs +++ b/src/di_container/binding_map.rs diff --git a/src/di_container/blocking/binding/builder.rs b/src/di_container/blocking/binding/builder.rs new file mode 100644 index 0000000..8e15f0c --- /dev/null +++ b/src/di_container/blocking/binding/builder.rs @@ -0,0 +1,450 @@ +//! Binding builder for types inside of a [`DIContainer`]. +use std::any::type_name; +use std::marker::PhantomData; +use std::rc::Rc; + +use crate::di_container::blocking::binding::scope_configurator::BindingScopeConfigurator; +#[cfg(feature = "factory")] +use crate::di_container::blocking::binding::when_configurator::BindingWhenConfigurator; +use crate::di_container::blocking::DIContainer; +use crate::errors::di_container::BindingBuilderError; +use crate::interfaces::injectable::Injectable; + +/// Binding builder for type `Interface` inside a [`DIContainer`]. +pub struct BindingBuilder<Interface> +where +    Interface: 'static + ?Sized, +{ +    di_container: Rc<DIContainer>, +    interface_phantom: PhantomData<Interface>, +} + +impl<Interface> BindingBuilder<Interface> +where +    Interface: 'static + ?Sized, +{ +    pub(crate) fn new(di_container: Rc<DIContainer>) -> Self +    { +        Self { +            di_container, +            interface_phantom: PhantomData, +        } +    } + +    /// Creates a binding of type `Interface` to type `Implementation` inside of the +    /// associated [`DIContainer`]. +    /// +    /// The scope of the binding is transient. But that can be changed by using the +    /// returned [`BindingScopeConfigurator`] +    /// +    /// # Errors +    /// Will return Err if the associated [`DIContainer`] already have a binding for +    /// the interface. +    /// +    /// # Examples +    /// ``` +    /// # use std::error::Error; +    /// # +    /// # use syrette::{DIContainer, injectable}; +    /// # +    /// # trait Foo {} +    /// # +    /// # struct Bar {} +    /// # +    /// # #[injectable(Foo)] +    /// # impl Bar { +    /// #   fn new() -> Self +    /// #   { +    /// #       Self {} +    /// #   } +    /// # } +    /// # +    /// # impl Foo for Bar {} +    /// # +    /// # fn main() -> Result<(), Box<dyn Error>> +    /// # { +    /// # let mut di_container = DIContainer::new(); +    /// # +    /// di_container.bind::<dyn Foo>().to::<Bar>(); +    /// # +    /// # Ok(()) +    /// # } +    /// ``` +    pub fn to<Implementation>( +        &self, +    ) -> Result<BindingScopeConfigurator<Interface, Implementation>, BindingBuilderError> +    where +        Implementation: Injectable, +    { +        { +            let bindings = self.di_container.bindings.borrow(); + +            if bindings.has::<Interface>(None) { +                return Err(BindingBuilderError::BindingAlreadyExists(type_name::< +                    Interface, +                >( +                ))); +            } +        } + +        let binding_scope_configurator = +            BindingScopeConfigurator::new(self.di_container.clone()); + +        binding_scope_configurator.in_transient_scope(); + +        Ok(binding_scope_configurator) +    } + +    /// Creates a binding of factory type `Interface` to a factory inside of the +    /// associated [`DIContainer`]. +    /// +    /// # Errors +    /// Will return Err if the associated [`DIContainer`] already have a binding for +    /// the interface. +    /// +    /// # Examples +    /// ``` +    /// # use std::error::Error; +    /// # +    /// # use syrette::{DIContainer, factory}; +    /// # use syrette::ptr::TransientPtr; +    /// # +    /// # trait ICustomerID {} +    /// # trait ICustomer {} +    /// # +    /// # struct Customer +    /// # { +    /// #   name: String, +    /// #   id: TransientPtr<dyn ICustomerID> +    /// # } +    /// # +    /// # impl Customer { +    /// #   fn new(name: String, id: TransientPtr<dyn ICustomerID>) -> Self +    /// #   { +    /// #       Self { name, id } +    /// #   } +    /// # } +    /// # +    /// # impl ICustomer for Customer {} +    /// # +    /// # #[factory] +    /// # type ICustomerFactory = dyn Fn(String, u32) -> dyn ICustomer; +    /// # +    /// # #[factory] +    /// # type ICustomerIDFactory = dyn Fn(u32) -> dyn ICustomerID; +    /// # +    /// # fn main() -> Result<(), Box<dyn Error>> +    /// # { +    /// # let mut di_container = DIContainer::new(); +    /// # +    /// di_container +    ///     .bind::<ICustomerFactory>() +    ///     .to_factory(&|context| { +    ///         Box::new(move |name, id| { +    ///             let customer_id_factory = context +    ///                 .get::<ICustomerIDFactory>() +    ///                 .unwrap() +    ///                 .factory() +    ///                 .unwrap(); +    /// +    ///             let customer_id = customer_id_factory(id); +    /// +    ///             let customer = TransientPtr::new(Customer::new(name, customer_id)); +    /// +    ///             customer as TransientPtr<dyn ICustomer> +    ///         }) +    ///     }); +    /// # +    /// # Ok(()) +    /// # } +    /// ``` +    #[cfg(feature = "factory")] +    #[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))] +    pub fn to_factory<Args, Return, Func>( +        &self, +        factory_func: &'static Func, +    ) -> Result<BindingWhenConfigurator<Interface>, BindingBuilderError> +    where +        Args: 'static, +        Return: 'static + ?Sized, +        Interface: Fn<Args, Output = crate::ptr::TransientPtr<Return>>, +        Func: Fn<(std::rc::Rc<DIContainer>,), Output = Box<Interface>>, +    { +        use crate::castable_factory::blocking::CastableFactory; + +        { +            let bindings = self.di_container.bindings.borrow(); + +            if bindings.has::<Interface>(None) { +                return Err(BindingBuilderError::BindingAlreadyExists(type_name::< +                    Interface, +                >( +                ))); +            } +        } + +        let mut bindings_mut = self.di_container.bindings.borrow_mut(); + +        let factory_impl = CastableFactory::new(factory_func); + +        bindings_mut.set::<Interface>( +            None, +            Box::new(crate::provider::blocking::FactoryProvider::new( +                crate::ptr::FactoryPtr::new(factory_impl), +                false, +            )), +        ); + +        Ok(BindingWhenConfigurator::new(self.di_container.clone())) +    } + +    /// Creates a binding of type `Interface` to a factory that takes no arguments +    /// inside of the associated [`DIContainer`]. +    /// +    /// # Errors +    /// Will return Err if the associated [`DIContainer`] already have a binding for +    /// the interface. +    /// +    /// # Examples +    /// ``` +    /// # use std::error::Error; +    /// # +    /// # use syrette::{DIContainer, factory}; +    /// # use syrette::ptr::TransientPtr; +    /// # +    /// # trait IBuffer {} +    /// # +    /// # struct Buffer<const SIZE: usize> +    /// # { +    /// #   buf: [u8; SIZE] +    /// # } +    /// # +    /// # impl<const SIZE: usize> Buffer<SIZE> +    /// # { +    /// #   fn new() -> Self +    /// #   { +    /// #       Self { +    /// #           buf: [0; SIZE] +    /// #       } +    /// #   } +    /// # } +    /// # +    /// # impl<const SIZE: usize> IBuffer for Buffer<SIZE> {} +    /// # +    /// # const BUFFER_SIZE: usize = 12; +    /// # +    /// # fn main() -> Result<(), Box<dyn Error>> +    /// # { +    /// # let mut di_container = DIContainer::new(); +    /// # +    /// di_container.bind::<dyn IBuffer>().to_default_factory(&|_| { +    ///     Box::new(|| { +    ///         let buffer = TransientPtr::new(Buffer::<BUFFER_SIZE>::new()); +    /// +    ///         buffer as TransientPtr<dyn IBuffer> +    ///     }) +    /// }); +    /// # +    /// # Ok(()) +    /// # } +    /// ``` +    #[cfg(feature = "factory")] +    #[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))] +    pub fn to_default_factory<Return, FactoryFunc>( +        &self, +        factory_func: &'static FactoryFunc, +    ) -> Result<BindingWhenConfigurator<Interface>, BindingBuilderError> +    where +        Return: 'static + ?Sized, +        FactoryFunc: Fn< +            (Rc<DIContainer>,), +            Output = crate::ptr::TransientPtr< +                dyn Fn<(), Output = crate::ptr::TransientPtr<Return>>, +            >, +        >, +    { +        use crate::castable_factory::blocking::CastableFactory; + +        { +            let bindings = self.di_container.bindings.borrow(); + +            if bindings.has::<Interface>(None) { +                return Err(BindingBuilderError::BindingAlreadyExists(type_name::< +                    Interface, +                >( +                ))); +            } +        } + +        let mut bindings_mut = self.di_container.bindings.borrow_mut(); + +        let factory_impl = CastableFactory::new(factory_func); + +        bindings_mut.set::<Interface>( +            None, +            Box::new(crate::provider::blocking::FactoryProvider::new( +                crate::ptr::FactoryPtr::new(factory_impl), +                true, +            )), +        ); + +        Ok(BindingWhenConfigurator::new(self.di_container.clone())) +    } +} + +#[cfg(test)] +mod tests +{ +    use std::error::Error; + +    use super::*; +    use crate::ptr::TransientPtr; +    use crate::test_utils::subjects; + +    #[test] +    fn can_bind_to() -> Result<(), Box<dyn Error>> +    { +        let mut di_container = DIContainer::new(); + +        assert_eq!(di_container.bindings.borrow().count(), 0); + +        di_container +            .bind::<dyn subjects::IUserManager>() +            .to::<subjects::UserManager>()?; + +        assert_eq!(di_container.bindings.borrow().count(), 1); + +        Ok(()) +    } + +    #[test] +    fn can_bind_to_transient() -> Result<(), Box<dyn Error>> +    { +        let mut di_container = DIContainer::new(); + +        assert_eq!(di_container.bindings.borrow().count(), 0); + +        di_container +            .bind::<dyn subjects::IUserManager>() +            .to::<subjects::UserManager>()? +            .in_transient_scope(); + +        assert_eq!(di_container.bindings.borrow().count(), 1); + +        Ok(()) +    } + +    #[test] +    fn can_bind_to_transient_when_named() -> Result<(), Box<dyn Error>> +    { +        let mut di_container = DIContainer::new(); + +        assert_eq!(di_container.bindings.borrow().count(), 0); + +        di_container +            .bind::<dyn subjects::IUserManager>() +            .to::<subjects::UserManager>()? +            .in_transient_scope() +            .when_named("regular")?; + +        assert_eq!(di_container.bindings.borrow().count(), 1); + +        Ok(()) +    } + +    #[test] +    fn can_bind_to_singleton() -> Result<(), Box<dyn Error>> +    { +        let mut di_container = DIContainer::new(); + +        assert_eq!(di_container.bindings.borrow().count(), 0); + +        di_container +            .bind::<dyn subjects::IUserManager>() +            .to::<subjects::UserManager>()? +            .in_singleton_scope()?; + +        assert_eq!(di_container.bindings.borrow().count(), 1); + +        Ok(()) +    } + +    #[test] +    fn can_bind_to_singleton_when_named() -> Result<(), Box<dyn Error>> +    { +        let mut di_container = DIContainer::new(); + +        assert_eq!(di_container.bindings.borrow().count(), 0); + +        di_container +            .bind::<dyn subjects::IUserManager>() +            .to::<subjects::UserManager>()? +            .in_singleton_scope()? +            .when_named("cool")?; + +        assert_eq!(di_container.bindings.borrow().count(), 1); + +        Ok(()) +    } + +    #[test] +    #[cfg(feature = "factory")] +    fn can_bind_to_factory() -> Result<(), Box<dyn Error>> +    { +        use crate as syrette; +        use crate::factory; + +        #[factory] +        type IUserManagerFactory = dyn Fn() -> dyn subjects::IUserManager; + +        let mut di_container = DIContainer::new(); + +        assert_eq!(di_container.bindings.borrow().count(), 0); + +        di_container +            .bind::<IUserManagerFactory>() +            .to_factory(&|_| { +                Box::new(move || { +                    let user_manager: TransientPtr<dyn subjects::IUserManager> = +                        TransientPtr::new(subjects::UserManager::new()); + +                    user_manager +                }) +            })?; + +        assert_eq!(di_container.bindings.borrow().count(), 1); + +        Ok(()) +    } + +    #[test] +    #[cfg(feature = "factory")] +    fn can_bind_to_factory_when_named() -> Result<(), Box<dyn Error>> +    { +        use crate as syrette; +        use crate::factory; + +        #[factory] +        type IUserManagerFactory = dyn Fn() -> dyn subjects::IUserManager; + +        let mut di_container = DIContainer::new(); + +        assert_eq!(di_container.bindings.borrow().count(), 0); + +        di_container +            .bind::<IUserManagerFactory>() +            .to_factory(&|_| { +                Box::new(move || { +                    let user_manager: TransientPtr<dyn subjects::IUserManager> = +                        TransientPtr::new(subjects::UserManager::new()); + +                    user_manager +                }) +            })? +            .when_named("awesome")?; + +        assert_eq!(di_container.bindings.borrow().count(), 1); + +        Ok(()) +    } +} diff --git a/src/di_container/blocking/binding/mod.rs b/src/di_container/blocking/binding/mod.rs new file mode 100644 index 0000000..6a09bff --- /dev/null +++ b/src/di_container/blocking/binding/mod.rs @@ -0,0 +1,5 @@ +//! Types for building & configurating DI container bindings. + +pub mod builder; +pub mod scope_configurator; +pub mod when_configurator; diff --git a/src/di_container/blocking/binding/scope_configurator.rs b/src/di_container/blocking/binding/scope_configurator.rs new file mode 100644 index 0000000..09897b6 --- /dev/null +++ b/src/di_container/blocking/binding/scope_configurator.rs @@ -0,0 +1,72 @@ +//! Scope configurator for a binding for types inside of a [`DIContainer`]. +use std::marker::PhantomData; +use std::rc::Rc; + +use crate::di_container::blocking::binding::when_configurator::BindingWhenConfigurator; +use crate::di_container::blocking::DIContainer; +use crate::errors::di_container::BindingScopeConfiguratorError; +use crate::interfaces::injectable::Injectable; +use crate::provider::blocking::{SingletonProvider, TransientTypeProvider}; +use crate::ptr::SingletonPtr; + +/// Scope configurator for a binding for type 'Interface' inside a [`DIContainer`]. +pub struct BindingScopeConfigurator<Interface, Implementation> +where +    Interface: 'static + ?Sized, +    Implementation: Injectable, +{ +    di_container: Rc<DIContainer>, +    interface_phantom: PhantomData<Interface>, +    implementation_phantom: PhantomData<Implementation>, +} + +impl<Interface, Implementation> BindingScopeConfigurator<Interface, Implementation> +where +    Interface: 'static + ?Sized, +    Implementation: Injectable, +{ +    pub(crate) fn new(di_container: Rc<DIContainer>) -> Self +    { +        Self { +            di_container, +            interface_phantom: PhantomData, +            implementation_phantom: PhantomData, +        } +    } + +    /// Configures the binding to be in a transient scope. +    /// +    /// This is the default. +    #[allow(clippy::must_use_candidate)] +    pub fn in_transient_scope(&self) -> BindingWhenConfigurator<Interface> +    { +        let mut bindings_mut = self.di_container.bindings.borrow_mut(); + +        bindings_mut.set::<Interface>( +            None, +            Box::new(TransientTypeProvider::<Implementation>::new()), +        ); + +        BindingWhenConfigurator::new(self.di_container.clone()) +    } + +    /// Configures the binding to be in a singleton scope. +    /// +    /// # Errors +    /// Will return Err if resolving the implementation fails. +    pub fn in_singleton_scope( +        &self, +    ) -> Result<BindingWhenConfigurator<Interface>, BindingScopeConfiguratorError> +    { +        let singleton: SingletonPtr<Implementation> = SingletonPtr::from( +            Implementation::resolve(&self.di_container, Vec::new()) +                .map_err(BindingScopeConfiguratorError::SingletonResolveFailed)?, +        ); + +        let mut bindings_mut = self.di_container.bindings.borrow_mut(); + +        bindings_mut.set::<Interface>(None, Box::new(SingletonProvider::new(singleton))); + +        Ok(BindingWhenConfigurator::new(self.di_container.clone())) +    } +} diff --git a/src/di_container/blocking/binding/when_configurator.rs b/src/di_container/blocking/binding/when_configurator.rs new file mode 100644 index 0000000..9cd9bb6 --- /dev/null +++ b/src/di_container/blocking/binding/when_configurator.rs @@ -0,0 +1,55 @@ +//! When configurator for a binding for types inside of a [`DIContainer`]. +use std::any::type_name; +use std::marker::PhantomData; +use std::rc::Rc; + +use crate::di_container::blocking::DIContainer; +use crate::errors::di_container::BindingWhenConfiguratorError; + +/// When configurator for a binding for type 'Interface' inside a [`DIContainer`]. +pub struct BindingWhenConfigurator<Interface> +where +    Interface: 'static + ?Sized, +{ +    di_container: Rc<DIContainer>, +    interface_phantom: PhantomData<Interface>, +} + +impl<Interface> BindingWhenConfigurator<Interface> +where +    Interface: 'static + ?Sized, +{ +    pub(crate) fn new(di_container: Rc<DIContainer>) -> Self +    { +        Self { +            di_container, +            interface_phantom: PhantomData, +        } +    } + +    /// Configures the binding to have a name. +    /// +    /// # Errors +    /// Will return Err if no binding for the interface already exists. +    pub fn when_named( +        &self, +        name: &'static str, +    ) -> Result<(), BindingWhenConfiguratorError> +    { +        let mut bindings_mut = self.di_container.bindings.borrow_mut(); + +        let binding = bindings_mut.remove::<Interface>(None).map_or_else( +            || { +                Err(BindingWhenConfiguratorError::BindingNotFound(type_name::< +                    Interface, +                >( +                ))) +            }, +            Ok, +        )?; + +        bindings_mut.set::<Interface>(Some(name), binding); + +        Ok(()) +    } +} diff --git a/src/di_container/blocking/mod.rs b/src/di_container/blocking/mod.rs new file mode 100644 index 0000000..f6b64fa --- /dev/null +++ b/src/di_container/blocking/mod.rs @@ -0,0 +1,599 @@ +//! Blocking dependency injection container. +//! +//! # Examples +//! ``` +//! use std::collections::HashMap; +//! use std::error::Error; +//! +//! use syrette::{injectable, DIContainer}; +//! +//! trait IDatabaseService +//! { +//!     fn get_all_records(&self, table_name: String) -> HashMap<String, String>; +//! } +//! +//! struct DatabaseService {} +//! +//! #[injectable(IDatabaseService)] +//! impl DatabaseService +//! { +//!     fn new() -> Self +//!     { +//!         Self {} +//!     } +//! } +//! +//! impl IDatabaseService for DatabaseService +//! { +//!     fn get_all_records(&self, table_name: String) -> HashMap<String, String> +//!     { +//!         // Do stuff here +//!         HashMap::<String, String>::new() +//!     } +//! } +//! +//! fn main() -> Result<(), Box<dyn Error>> +//! { +//!     let mut di_container = DIContainer::new(); +//! +//!     di_container +//!         .bind::<dyn IDatabaseService>() +//!         .to::<DatabaseService>() +//!         .map_err(|err| err.to_string())?; +//! +//!     let database_service = di_container +//!         .get::<dyn IDatabaseService>() +//!         .map_err(|err| err.to_string())? +//!         .transient()?; +//! +//!     Ok(()) +//! } +//! ``` +use std::any::type_name; +use std::cell::RefCell; +use std::rc::Rc; + +use crate::di_container::binding_map::DIContainerBindingMap; +use crate::di_container::blocking::binding::builder::BindingBuilder; +use crate::errors::di_container::DIContainerError; +use crate::libs::intertrait::cast::{CastBox, CastRc}; +use crate::provider::blocking::{IProvider, Providable}; +use crate::ptr::SomePtr; + +pub mod binding; + +/// Dependency injection container. +pub struct DIContainer +{ +    bindings: RefCell<DIContainerBindingMap<dyn IProvider>>, +} + +impl DIContainer +{ +    /// Returns a new `DIContainer`. +    #[must_use] +    pub fn new() -> Rc<Self> +    { +        Rc::new(Self { +            bindings: RefCell::new(DIContainerBindingMap::new()), +        }) +    } + +    /// Returns a new [`BindingBuilder`] for the given interface. +    #[must_use] +    pub fn bind<Interface>(self: &mut Rc<Self>) -> BindingBuilder<Interface> +    where +        Interface: 'static + ?Sized, +    { +        BindingBuilder::<Interface>::new(self.clone()) +    } + +    /// Returns the type bound with `Interface`. +    /// +    /// # Errors +    /// Will return `Err` if: +    /// - No binding for `Interface` exists +    /// - Resolving the binding for `Interface` fails +    /// - Casting the binding for `Interface` fails +    pub fn get<Interface>(self: &Rc<Self>) -> Result<SomePtr<Interface>, DIContainerError> +    where +        Interface: 'static + ?Sized, +    { +        self.get_bound::<Interface>(Vec::new(), None) +    } + +    /// Returns the type bound with `Interface` and the specified name. +    /// +    /// # Errors +    /// Will return `Err` if: +    /// - No binding for `Interface` with name `name` exists +    /// - Resolving the binding for `Interface` fails +    /// - Casting the binding for `Interface` fails +    pub fn get_named<Interface>( +        self: &Rc<Self>, +        name: &'static str, +    ) -> Result<SomePtr<Interface>, DIContainerError> +    where +        Interface: 'static + ?Sized, +    { +        self.get_bound::<Interface>(Vec::new(), Some(name)) +    } + +    #[doc(hidden)] +    pub fn get_bound<Interface>( +        self: &Rc<Self>, +        dependency_history: Vec<&'static str>, +        name: Option<&'static str>, +    ) -> Result<SomePtr<Interface>, DIContainerError> +    where +        Interface: 'static + ?Sized, +    { +        let binding_providable = +            self.get_binding_providable::<Interface>(name, dependency_history)?; + +        self.handle_binding_providable(binding_providable) +    } + +    fn handle_binding_providable<Interface>( +        self: &Rc<Self>, +        binding_providable: Providable, +    ) -> Result<SomePtr<Interface>, DIContainerError> +    where +        Interface: 'static + ?Sized, +    { +        match binding_providable { +            Providable::Transient(transient_binding) => Ok(SomePtr::Transient( +                transient_binding.cast::<Interface>().map_err(|_| { +                    DIContainerError::CastFailed { +                        interface: type_name::<Interface>(), +                        binding_kind: "transient", +                    } +                })?, +            )), +            Providable::Singleton(singleton_binding) => Ok(SomePtr::Singleton( +                singleton_binding.cast::<Interface>().map_err(|_| { +                    DIContainerError::CastFailed { +                        interface: type_name::<Interface>(), +                        binding_kind: "singleton", +                    } +                })?, +            )), +            #[cfg(feature = "factory")] +            Providable::Factory(factory_binding) => { +                use crate::interfaces::factory::IFactory; + +                let factory = factory_binding +                    .cast::<dyn IFactory<(Rc<DIContainer>,), Interface>>() +                    .map_err(|_| DIContainerError::CastFailed { +                        interface: type_name::<Interface>(), +                        binding_kind: "factory", +                    })?; + +                Ok(SomePtr::Factory(factory(self.clone()).into())) +            } +            #[cfg(feature = "factory")] +            Providable::DefaultFactory(factory_binding) => { +                use crate::interfaces::factory::IFactory; +                use crate::ptr::TransientPtr; + +                let default_factory = factory_binding +                    .cast::<dyn IFactory< +                        (Rc<DIContainer>,), +                        dyn Fn<(), Output = TransientPtr<Interface>>, +                    >>() +                    .map_err(|_| DIContainerError::CastFailed { +                        interface: type_name::<Interface>(), +                        binding_kind: "default factory", +                    })?; + +                Ok(SomePtr::Transient(default_factory(self.clone())())) +            } +        } +    } + +    fn get_binding_providable<Interface>( +        self: &Rc<Self>, +        name: Option<&'static str>, +        dependency_history: Vec<&'static str>, +    ) -> Result<Providable, DIContainerError> +    where +        Interface: 'static + ?Sized, +    { +        self.bindings +            .borrow() +            .get::<Interface>(name) +            .map_or_else( +                || { +                    Err(DIContainerError::BindingNotFound { +                        interface: type_name::<Interface>(), +                        name, +                    }) +                }, +                Ok, +            )? +            .provide(self, dependency_history) +            .map_err(|err| DIContainerError::BindingResolveFailed { +                reason: err, +                interface: type_name::<Interface>(), +            }) +    } +} + +#[cfg(test)] +mod tests +{ +    use std::error::Error; + +    use mockall::mock; + +    use super::*; +    use crate::errors::injectable::InjectableError; +    use crate::provider::blocking::IProvider; +    use crate::ptr::{SingletonPtr, TransientPtr}; +    use crate::test_utils::subjects; + +    #[test] +    fn can_get() -> Result<(), Box<dyn Error>> +    { +        mock! { +            Provider {} + +            impl IProvider for Provider +            { +                fn provide( +                    &self, +                    di_container: &Rc<DIContainer>, +                    dependency_history: Vec<&'static str>, +                ) -> Result<Providable, InjectableError>; +            } +        } + +        let di_container = DIContainer::new(); + +        let mut mock_provider = MockProvider::new(); + +        mock_provider.expect_provide().returning(|_, _| { +            Ok(Providable::Transient(TransientPtr::new( +                subjects::UserManager::new(), +            ))) +        }); + +        di_container +            .bindings +            .borrow_mut() +            .set::<dyn subjects::IUserManager>(None, Box::new(mock_provider)); + +        di_container +            .get::<dyn subjects::IUserManager>()? +            .transient()?; + +        Ok(()) +    } + +    #[test] +    fn can_get_named() -> Result<(), Box<dyn Error>> +    { +        mock! { +            Provider {} + +            impl IProvider for Provider +            { +                fn provide( +                    &self, +                    di_container: &Rc<DIContainer>, +                    dependency_history: Vec<&'static str>, +                ) -> Result<Providable, InjectableError>; +            } +        } + +        let di_container = DIContainer::new(); + +        let mut mock_provider = MockProvider::new(); + +        mock_provider.expect_provide().returning(|_, _| { +            Ok(Providable::Transient(TransientPtr::new( +                subjects::UserManager::new(), +            ))) +        }); + +        di_container +            .bindings +            .borrow_mut() +            .set::<dyn subjects::IUserManager>(Some("special"), Box::new(mock_provider)); + +        di_container +            .get_named::<dyn subjects::IUserManager>("special")? +            .transient()?; + +        Ok(()) +    } + +    #[test] +    fn can_get_singleton() -> Result<(), Box<dyn Error>> +    { +        mock! { +            Provider {} + +            impl IProvider for Provider +            { +                fn provide( +                    &self, +                    di_container: &Rc<DIContainer>, +                    dependency_history: Vec<&'static str>, +                ) -> Result<Providable, InjectableError>; +            } +        } + +        let di_container = DIContainer::new(); + +        let mut mock_provider = MockProvider::new(); + +        let mut singleton = SingletonPtr::new(subjects::Number::new()); + +        SingletonPtr::get_mut(&mut singleton).unwrap().num = 2820; + +        mock_provider +            .expect_provide() +            .returning_st(move |_, _| Ok(Providable::Singleton(singleton.clone()))); + +        di_container +            .bindings +            .borrow_mut() +            .set::<dyn subjects::INumber>(None, Box::new(mock_provider)); + +        let first_number_rc = di_container.get::<dyn subjects::INumber>()?.singleton()?; + +        assert_eq!(first_number_rc.get(), 2820); + +        let second_number_rc = +            di_container.get::<dyn subjects::INumber>()?.singleton()?; + +        assert_eq!(first_number_rc.as_ref(), second_number_rc.as_ref()); + +        Ok(()) +    } + +    #[test] +    fn can_get_singleton_named() -> Result<(), Box<dyn Error>> +    { +        mock! { +            Provider {} + +            impl IProvider for Provider +            { +                fn provide( +                    &self, +                    di_container: &Rc<DIContainer>, +                    dependency_history: Vec<&'static str>, +                ) -> Result<Providable, InjectableError>; +            } +        } + +        let di_container = DIContainer::new(); + +        let mut mock_provider = MockProvider::new(); + +        let mut singleton = SingletonPtr::new(subjects::Number::new()); + +        SingletonPtr::get_mut(&mut singleton).unwrap().num = 2820; + +        mock_provider +            .expect_provide() +            .returning_st(move |_, _| Ok(Providable::Singleton(singleton.clone()))); + +        di_container +            .bindings +            .borrow_mut() +            .set::<dyn subjects::INumber>(Some("cool"), Box::new(mock_provider)); + +        let first_number_rc = di_container +            .get_named::<dyn subjects::INumber>("cool")? +            .singleton()?; + +        assert_eq!(first_number_rc.get(), 2820); + +        let second_number_rc = di_container +            .get_named::<dyn subjects::INumber>("cool")? +            .singleton()?; + +        assert_eq!(first_number_rc.as_ref(), second_number_rc.as_ref()); + +        Ok(()) +    } + +    #[test] +    #[cfg(feature = "factory")] +    fn can_get_factory() -> Result<(), Box<dyn Error>> +    { +        use crate::castable_factory::blocking::CastableFactory; +        use crate::ptr::FactoryPtr; + +        trait IUserManager +        { +            fn add_user(&mut self, user_id: i128); + +            fn remove_user(&mut self, user_id: i128); +        } + +        struct UserManager +        { +            users: Vec<i128>, +        } + +        impl UserManager +        { +            fn new(users: Vec<i128>) -> Self +            { +                Self { users } +            } +        } + +        impl IUserManager for UserManager +        { +            fn add_user(&mut self, user_id: i128) +            { +                self.users.push(user_id); +            } + +            fn remove_user(&mut self, user_id: i128) +            { +                let user_index = +                    self.users.iter().position(|user| *user == user_id).unwrap(); + +                self.users.remove(user_index); +            } +        } + +        type FactoryFunc = dyn Fn< +            (std::rc::Rc<DIContainer>,), +            Output = Box< +                dyn Fn<(Vec<i128>,), Output = crate::ptr::TransientPtr<dyn IUserManager>>, +            >, +        >; + +        use crate as syrette; + +        #[crate::factory] +        type IUserManagerFactory = dyn Fn(Vec<i128>) -> dyn IUserManager; + +        mock! { +            Provider {} + +            impl IProvider for Provider +            { +                fn provide( +                    &self, +                    di_container: &Rc<DIContainer>, +                    dependency_history: Vec<&'static str>, +                ) -> Result<Providable, InjectableError>; +            } +        } + +        let di_container = DIContainer::new(); + +        let factory_func: &'static FactoryFunc = &|_: Rc<DIContainer>| { +            Box::new(move |users| { +                let user_manager: TransientPtr<dyn IUserManager> = +                    TransientPtr::new(UserManager::new(users)); + +                user_manager +            }) +        }; + +        let mut mock_provider = MockProvider::new(); + +        mock_provider.expect_provide().returning_st(|_, _| { +            Ok(Providable::Factory(FactoryPtr::new(CastableFactory::new( +                factory_func, +            )))) +        }); + +        di_container +            .bindings +            .borrow_mut() +            .set::<IUserManagerFactory>(None, Box::new(mock_provider)); + +        di_container.get::<IUserManagerFactory>()?.factory()?; + +        Ok(()) +    } + +    #[test] +    #[cfg(feature = "factory")] +    fn can_get_factory_named() -> Result<(), Box<dyn Error>> +    { +        use crate::castable_factory::blocking::CastableFactory; +        use crate::ptr::FactoryPtr; + +        trait IUserManager +        { +            fn add_user(&mut self, user_id: i128); + +            fn remove_user(&mut self, user_id: i128); +        } + +        struct UserManager +        { +            users: Vec<i128>, +        } + +        impl UserManager +        { +            fn new(users: Vec<i128>) -> Self +            { +                Self { users } +            } +        } + +        impl IUserManager for UserManager +        { +            fn add_user(&mut self, user_id: i128) +            { +                self.users.push(user_id); +            } + +            fn remove_user(&mut self, user_id: i128) +            { +                let user_index = +                    self.users.iter().position(|user| *user == user_id).unwrap(); + +                self.users.remove(user_index); +            } +        } + +        type FactoryFunc = dyn Fn< +            (std::rc::Rc<DIContainer>,), +            Output = Box< +                dyn Fn<(Vec<i128>,), Output = crate::ptr::TransientPtr<dyn IUserManager>>, +            >, +        >; + +        use crate as syrette; + +        #[crate::factory] +        type IUserManagerFactory = dyn Fn(Vec<i128>) -> dyn IUserManager; + +        mock! { +            Provider {} + +            impl IProvider for Provider +            { +                fn provide( +                    &self, +                    di_container: &Rc<DIContainer>, +                    dependency_history: Vec<&'static str>, +                ) -> Result<Providable, InjectableError>; +            } +        } + +        let di_container = DIContainer::new(); + +        let factory_func: &'static FactoryFunc = &|_: Rc<DIContainer>| { +            Box::new(move |users| { +                let user_manager: TransientPtr<dyn IUserManager> = +                    TransientPtr::new(UserManager::new(users)); + +                user_manager +            }) +        }; + +        let mut mock_provider = MockProvider::new(); + +        mock_provider.expect_provide().returning_st(|_, _| { +            Ok(Providable::Factory(FactoryPtr::new(CastableFactory::new( +                factory_func, +            )))) +        }); + +        di_container +            .bindings +            .borrow_mut() +            .set::<IUserManagerFactory>(Some("special"), Box::new(mock_provider)); + +        di_container +            .get_named::<IUserManagerFactory>("special")? +            .factory()?; + +        Ok(()) +    } +} diff --git a/src/di_container/mod.rs b/src/di_container/mod.rs new file mode 100644 index 0000000..bf2c7a0 --- /dev/null +++ b/src/di_container/mod.rs @@ -0,0 +1,9 @@ +//! Dependency injection container types. + +#[cfg(feature = "async")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "async")))] +pub mod asynchronous; + +pub mod blocking; + +pub(crate) mod binding_map; diff --git a/src/interfaces/async_injectable.rs b/src/interfaces/async_injectable.rs index 69c2a47..dadb603 100644 --- a/src/interfaces/async_injectable.rs +++ b/src/interfaces/async_injectable.rs @@ -2,11 +2,11 @@  use std::fmt::Debug;  use std::sync::Arc; -use crate::async_di_container::AsyncDIContainer;  use crate::errors::injectable::InjectableError;  use crate::future::BoxFuture;  use crate::libs::intertrait::CastFromSync;  use crate::ptr::TransientPtr; +use crate::AsyncDIContainer;  /// Interface for structs that can be injected into or be injected to.  pub trait AsyncInjectable: CastFromSync @@ -16,16 +16,12 @@ pub mod ptr;  #[cfg(feature = "async")]  #[cfg_attr(doc_cfg, doc(cfg(feature = "async")))] -pub mod async_di_container; - -#[cfg(feature = "async")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "async")))]  pub mod future;  #[cfg(feature = "async")]  #[cfg_attr(doc_cfg, doc(cfg(feature = "async")))] -pub use async_di_container::AsyncDIContainer; -pub use di_container::DIContainer; +pub use di_container::asynchronous::AsyncDIContainer; +pub use di_container::blocking::DIContainer;  #[cfg(feature = "factory")]  #[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))]  pub use syrette_macros::{declare_default_factory, factory}; @@ -42,9 +38,11 @@ pub mod dependency_trace;  pub mod libs;  // Private -mod di_container_binding_map;  mod provider; +#[cfg(test)] +mod test_utils; +  /// Shortcut for creating a DI container binding for a injectable without a declared  /// interface.  /// diff --git a/src/provider/async.rs b/src/provider/async.rs index 9925079..ec05b06 100644 --- a/src/provider/async.rs +++ b/src/provider/async.rs @@ -3,10 +3,10 @@ use std::sync::Arc;  use async_trait::async_trait; -use crate::async_di_container::AsyncDIContainer;  use crate::errors::injectable::InjectableError;  use crate::interfaces::async_injectable::AsyncInjectable;  use crate::ptr::{ThreadsafeSingletonPtr, TransientPtr}; +use crate::AsyncDIContainer;  #[derive(strum_macros::Display, Debug)]  pub enum AsyncProvidable diff --git a/src/test_utils.rs b/src/test_utils.rs new file mode 100644 index 0000000..1fc7015 --- /dev/null +++ b/src/test_utils.rs @@ -0,0 +1,253 @@ +pub mod subjects +{ +    //! Test subjects. + +    use std::fmt::Debug; +    use std::rc::Rc; + +    use syrette_macros::declare_interface; + +    use crate::interfaces::injectable::Injectable; +    use crate::ptr::TransientPtr; +    use crate::DIContainer; + +    pub trait IUserManager +    { +        fn add_user(&self, user_id: i128); + +        fn remove_user(&self, user_id: i128); +    } + +    pub struct UserManager {} + +    impl UserManager +    { +        pub fn new() -> Self +        { +            Self {} +        } +    } + +    impl IUserManager for UserManager +    { +        fn add_user(&self, _user_id: i128) +        { +            // ... +        } + +        fn remove_user(&self, _user_id: i128) +        { +            // ... +        } +    } + +    use crate as syrette; + +    declare_interface!(UserManager -> IUserManager); + +    impl Injectable for UserManager +    { +        fn resolve( +            _di_container: &Rc<DIContainer>, +            _dependency_history: Vec<&'static str>, +        ) -> Result<TransientPtr<Self>, crate::errors::injectable::InjectableError> +        where +            Self: Sized, +        { +            Ok(TransientPtr::new(Self::new())) +        } +    } + +    pub trait INumber +    { +        fn get(&self) -> i32; + +        fn set(&mut self, number: i32); +    } + +    impl PartialEq for dyn INumber +    { +        fn eq(&self, other: &Self) -> bool +        { +            self.get() == other.get() +        } +    } + +    impl Debug for dyn INumber +    { +        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result +        { +            f.write_str(format!("{}", self.get()).as_str()) +        } +    } + +    pub struct Number +    { +        pub num: i32, +    } + +    impl Number +    { +        pub fn new() -> Self +        { +            Self { num: 0 } +        } +    } + +    impl INumber for Number +    { +        fn get(&self) -> i32 +        { +            self.num +        } + +        fn set(&mut self, number: i32) +        { +            self.num = number; +        } +    } + +    declare_interface!(Number -> INumber); + +    impl Injectable for Number +    { +        fn resolve( +            _di_container: &Rc<DIContainer>, +            _dependency_history: Vec<&'static str>, +        ) -> Result<TransientPtr<Self>, crate::errors::injectable::InjectableError> +        where +            Self: Sized, +        { +            Ok(TransientPtr::new(Self::new())) +        } +    } +} + +#[cfg(feature = "async")] +pub mod subjects_async +{ +    //! Test subjects. + +    use std::fmt::Debug; +    use std::sync::Arc; + +    use async_trait::async_trait; +    use syrette_macros::declare_interface; + +    use crate::interfaces::async_injectable::AsyncInjectable; +    use crate::ptr::TransientPtr; +    use crate::AsyncDIContainer; + +    pub trait IUserManager: Send + Sync +    { +        fn add_user(&self, user_id: i128); + +        fn remove_user(&self, user_id: i128); +    } + +    pub struct UserManager {} + +    impl UserManager +    { +        pub fn new() -> Self +        { +            Self {} +        } +    } + +    impl IUserManager for UserManager +    { +        fn add_user(&self, _user_id: i128) +        { +            // ... +        } + +        fn remove_user(&self, _user_id: i128) +        { +            // ... +        } +    } + +    use crate as syrette; + +    declare_interface!(UserManager -> IUserManager); + +    #[async_trait] +    impl AsyncInjectable for UserManager +    { +        async fn resolve( +            _: &Arc<AsyncDIContainer>, +            _dependency_history: Vec<&'static str>, +        ) -> Result<TransientPtr<Self>, crate::errors::injectable::InjectableError> +        where +            Self: Sized, +        { +            Ok(TransientPtr::new(Self::new())) +        } +    } + +    pub trait INumber: Send + Sync +    { +        fn get(&self) -> i32; + +        fn set(&mut self, number: i32); +    } + +    impl PartialEq for dyn INumber +    { +        fn eq(&self, other: &Self) -> bool +        { +            self.get() == other.get() +        } +    } + +    impl Debug for dyn INumber +    { +        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result +        { +            f.write_str(format!("{}", self.get()).as_str()) +        } +    } + +    pub struct Number +    { +        pub num: i32, +    } + +    impl Number +    { +        pub fn new() -> Self +        { +            Self { num: 0 } +        } +    } + +    impl INumber for Number +    { +        fn get(&self) -> i32 +        { +            self.num +        } + +        fn set(&mut self, number: i32) +        { +            self.num = number; +        } +    } + +    declare_interface!(Number -> INumber, async = true); + +    #[async_trait] +    impl AsyncInjectable for Number +    { +        async fn resolve( +            _: &Arc<AsyncDIContainer>, +            _dependency_history: Vec<&'static str>, +        ) -> Result<TransientPtr<Self>, crate::errors::injectable::InjectableError> +        where +            Self: Sized, +        { +            Ok(TransientPtr::new(Self::new())) +        } +    } +}  | 
