diff options
author | HampusM <hampus@hampusmat.com> | 2022-10-23 18:12:23 +0200 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2022-10-23 18:12:23 +0200 |
commit | 9e01cdf341a7866180b3a63d745f3b2d7578d28a (patch) | |
tree | 0c036b7b4a68e44b6eb2221bf7beb3c34fe9c1c8 /src/di_container | |
parent | 740ef47d49e02ae2f2184f4c347d8eba8aee38fd (diff) |
refactor!: reduce DI container coupling
BREAKING CHANGE: You now have to import the DI containers's interfaces to use the DI containers's methods
Diffstat (limited to 'src/di_container')
-rw-r--r-- | src/di_container/asynchronous/binding/builder.rs | 355 | ||||
-rw-r--r-- | src/di_container/asynchronous/binding/scope_configurator.rs | 58 | ||||
-rw-r--r-- | src/di_container/asynchronous/binding/when_configurator.rs | 46 | ||||
-rw-r--r-- | src/di_container/asynchronous/mod.rs | 217 | ||||
-rw-r--r-- | src/di_container/asynchronous/prelude.rs | 3 | ||||
-rw-r--r-- | src/di_container/binding_map.rs | 7 | ||||
-rw-r--r-- | src/di_container/blocking/binding/builder.rs | 273 | ||||
-rw-r--r-- | src/di_container/blocking/binding/scope_configurator.rs | 45 | ||||
-rw-r--r-- | src/di_container/blocking/binding/when_configurator.rs | 46 | ||||
-rw-r--r-- | src/di_container/blocking/mod.rs | 166 | ||||
-rw-r--r-- | src/di_container/blocking/prelude.rs | 2 |
11 files changed, 670 insertions, 548 deletions
diff --git a/src/di_container/asynchronous/binding/builder.rs b/src/di_container/asynchronous/binding/builder.rs index 6daba30..d9ad0e6 100644 --- a/src/di_container/asynchronous/binding/builder.rs +++ b/src/di_container/asynchronous/binding/builder.rs @@ -1,4 +1,6 @@ -//! Binding builder for types inside of a [`AsyncDIContainer`]. +//! Binding builder for types inside of a [`IAsyncDIContainer`]. +//! +//! [`IAsyncDIContainer`]: crate::di_container::asynchronous::IAsyncDIContainer use std::any::type_name; use std::marker::PhantomData; use std::sync::Arc; @@ -6,29 +8,33 @@ 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::di_container::asynchronous::IAsyncDIContainer; 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> +/// Binding builder for type `Interface` inside a [`IAsyncDIContainer`]. +/// +/// [`IAsyncDIContainer`]: crate::di_container::asynchronous::IAsyncDIContainer +pub struct AsyncBindingBuilder<Interface, DIContainerType> where Interface: 'static + ?Sized + Send + Sync, + DIContainerType: IAsyncDIContainer, { - di_container: Arc<AsyncDIContainer>, + di_container: Arc<DIContainerType>, interface_phantom: PhantomData<Interface>, } -impl<Interface> AsyncBindingBuilder<Interface> +impl<Interface, DIContainerType> AsyncBindingBuilder<Interface, DIContainerType> where Interface: 'static + ?Sized + Send + Sync, + DIContainerType: IAsyncDIContainer, { - pub(crate) fn new(di_container: Arc<AsyncDIContainer>) -> Self + pub(crate) fn new(di_container: Arc<DIContainerType>) -> Self { Self { di_container, @@ -37,20 +43,21 @@ where } /// Creates a binding of type `Interface` to type `Implementation` inside of the - /// associated [`AsyncDIContainer`]. + /// associated [`IAsyncDIContainer`]. /// /// 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 + /// Will return Err if the associated [`IAsyncDIContainer`] already have a binding for /// the interface. /// /// # Examples /// ``` /// # use std::error::Error; /// # - /// # use syrette::{AsyncDIContainer, injectable}; + /// # use syrette::injectable; + /// # use syrette::di_container::asynchronous::prelude::*; /// # /// # trait Foo: Send + Sync {} /// # @@ -76,24 +83,22 @@ where /// # Ok(()) /// # } /// ``` + /// + /// [`IAsyncDIContainer`]: crate::di_container::asynchronous::IAsyncDIContainer pub async fn to<Implementation>( &self, ) -> Result< - AsyncBindingScopeConfigurator<Interface, Implementation>, + AsyncBindingScopeConfigurator<Interface, Implementation, DIContainerType>, AsyncBindingBuilderError, > where - Implementation: AsyncInjectable, + Implementation: AsyncInjectable<DIContainerType>, { - { - let bindings_lock = self.di_container.bindings.lock().await; - - if bindings_lock.has::<Interface>(None) { - return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< - Interface, - >( - ))); - } + if self.di_container.has_binding::<Interface>(None).await { + return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< + Interface, + >( + ))); } let binding_scope_configurator = @@ -105,17 +110,18 @@ where } /// Creates a binding of factory type `Interface` to a factory inside of the - /// associated [`AsyncDIContainer`]. + /// associated [`IAsyncDIContainer`]. /// /// # Errors - /// Will return Err if the associated [`AsyncDIContainer`] already have a binding + /// Will return Err if the associated [`IAsyncDIContainer`] already have a binding /// for the interface. /// /// # Examples /// ``` /// # use std::error::Error; /// # - /// # use syrette::{AsyncDIContainer, factory}; + /// # use syrette::{factory}; + /// # use syrette::di_container::asynchronous::prelude::*; /// # use syrette::ptr::TransientPtr; /// # /// # trait Foo: Send + Sync {} @@ -150,25 +156,28 @@ where /// # Ok(()) /// # } /// ``` + /// + /// [`IAsyncDIContainer`]: crate::di_container::asynchronous::IAsyncDIContainer #[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> + ) -> Result< + AsyncBindingWhenConfigurator<Interface, DIContainerType>, + AsyncBindingBuilderError, + > where Args: 'static, Return: 'static + ?Sized, Interface: Fn<Args, Output = Return> + Send + Sync, FactoryFunc: - Fn<(Arc<AsyncDIContainer>,), Output = BoxFn<Args, Return>> + Send + Sync, + Fn<(Arc<DIContainerType>,), 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) { + if self.di_container.has_binding::<Interface>(None).await { return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< Interface, >( @@ -177,22 +186,24 @@ where 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, - )), - ); + self.di_container + .set_binding::<Interface>( + None, + Box::new(crate::provider::r#async::AsyncFactoryProvider::new( + crate::ptr::ThreadsafeFactoryPtr::new(factory_impl), + AsyncFactoryVariant::Normal, + )), + ) + .await; Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) } /// Creates a binding of factory type `Interface` to a async factory inside of the - /// associated [`AsyncDIContainer`]. + /// associated [`IAsyncDIContainer`]. /// /// # Errors - /// Will return Err if the associated [`AsyncDIContainer`] already have a binding + /// Will return Err if the associated [`IAsyncDIContainer`] already have a binding /// for the interface. /// /// # Examples @@ -200,7 +211,8 @@ where /// # use std::error::Error; /// # use std::time::Duration; /// # - /// # use syrette::{AsyncDIContainer, factory, async_closure}; + /// # use syrette::{factory, async_closure}; + /// # use syrette::di_container::asynchronous::prelude::*; /// # use syrette::ptr::TransientPtr; /// # /// # trait Foo: Send + Sync {} @@ -237,19 +249,24 @@ where /// # Ok(()) /// # } /// ``` + /// + /// [`IAsyncDIContainer`]: crate::di_container::asynchronous::IAsyncDIContainer #[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> + ) -> Result< + AsyncBindingWhenConfigurator<Interface, DIContainerType>, + AsyncBindingBuilderError, + > where Args: 'static, Return: 'static + ?Sized, Interface: Fn<Args, Output = crate::future::BoxFuture<'static, Return>> + Send + Sync, FactoryFunc: Fn< - (Arc<AsyncDIContainer>,), + (Arc<DIContainerType>,), Output = BoxFn<Args, crate::future::BoxFuture<'static, Return>>, > + Send + Sync, @@ -257,9 +274,7 @@ where 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) { + if self.di_container.has_binding::<Interface>(None).await { return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< Interface, >( @@ -268,29 +283,31 @@ where 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, - )), - ); + self.di_container + .set_binding::<Interface>( + None, + Box::new(crate::provider::r#async::AsyncFactoryProvider::new( + crate::ptr::ThreadsafeFactoryPtr::new(factory_impl), + AsyncFactoryVariant::Normal, + )), + ) + .await; 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`]. + /// inside of the associated [`IAsyncDIContainer`]. /// /// # Errors - /// Will return Err if the associated [`AsyncDIContainer`] already have a binding + /// Will return Err if the associated [`IAsyncDIContainer`] already have a binding /// for the interface. /// /// # Examples /// ``` /// # use std::error::Error; /// # - /// # use syrette::AsyncDIContainer; + /// # use syrette::di_container::asynchronous::prelude::*; /// # use syrette::ptr::TransientPtr; /// # /// # trait Foo: Send + Sync {} @@ -325,16 +342,21 @@ where /// # Ok(()) /// # } /// ``` + /// + /// [`IAsyncDIContainer`]: crate::di_container::asynchronous::IAsyncDIContainer #[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> + ) -> Result< + AsyncBindingWhenConfigurator<Interface, DIContainerType>, + AsyncBindingBuilderError, + > where Return: 'static + ?Sized, FactoryFunc: Fn< - (Arc<AsyncDIContainer>,), + (Arc<DIContainerType>,), Output = BoxFn<(), crate::ptr::TransientPtr<Return>>, > + Send + Sync, @@ -342,9 +364,7 @@ where 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) { + if self.di_container.has_binding::<Interface>(None).await { return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< Interface, >( @@ -353,22 +373,24 @@ where 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, - )), - ); + self.di_container + .set_binding::<Interface>( + None, + Box::new(crate::provider::r#async::AsyncFactoryProvider::new( + crate::ptr::ThreadsafeFactoryPtr::new(factory_impl), + AsyncFactoryVariant::Default, + )), + ) + .await; Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) } /// Creates a binding of factory type `Interface` to a async factory inside of the - /// associated [`AsyncDIContainer`]. + /// associated [`IAsyncDIContainer`]. /// /// # Errors - /// Will return Err if the associated [`AsyncDIContainer`] already have a binding + /// Will return Err if the associated [`IAsyncDIContainer`] already have a binding /// for the interface. /// /// # Examples @@ -376,7 +398,8 @@ where /// # use std::error::Error; /// # use std::time::Duration; /// # - /// # use syrette::{AsyncDIContainer, async_closure}; + /// # use syrette::async_closure; + /// # use syrette::di_container::asynchronous::prelude::*; /// # use syrette::ptr::TransientPtr; /// # /// # trait Foo: Send + Sync {} @@ -413,16 +436,21 @@ where /// # Ok(()) /// # } /// ``` + /// + /// [`IAsyncDIContainer`]: crate::di_container::asynchronous::IAsyncDIContainer #[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> + ) -> Result< + AsyncBindingWhenConfigurator<Interface, DIContainerType>, + AsyncBindingBuilderError, + > where Return: 'static + ?Sized, FactoryFunc: Fn< - (Arc<AsyncDIContainer>,), + (Arc<DIContainerType>,), Output = BoxFn<(), crate::future::BoxFuture<'static, Return>>, > + Send + Sync, @@ -430,9 +458,7 @@ where 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) { + if self.di_container.has_binding::<Interface>(None).await { return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< Interface, >( @@ -441,13 +467,15 @@ where 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, - )), - ); + self.di_container + .set_binding::<Interface>( + None, + Box::new(crate::provider::r#async::AsyncFactoryProvider::new( + crate::ptr::ThreadsafeFactoryPtr::new(factory_impl), + AsyncFactoryVariant::AsyncDefault, + )), + ) + .await; Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) } @@ -458,127 +486,40 @@ mod tests { use std::error::Error; + use mockall::predicate::eq; + use super::*; - use crate::ptr::TransientPtr; + use crate::test_utils::mocks::async_di_container::MockAsyncDIContainer; use crate::test_utils::subjects_async; #[tokio::test] async fn can_bind_to() -> Result<(), Box<dyn Error>> { - let mut di_container = AsyncDIContainer::new(); + let mut di_container_mock = MockAsyncDIContainer::new(); - { - assert_eq!(di_container.bindings.lock().await.count(), 0); - } + di_container_mock + .expect_has_binding::<dyn subjects_async::IUserManager>() + .with(eq(None)) + .return_once(|_name| false) + .once(); - di_container - .bind::<dyn subjects_async::IUserManager>() - .to::<subjects_async::UserManager>() - .await?; + di_container_mock + .expect_set_binding::<dyn subjects_async::IUserManager>() + .withf(|name, _provider| name.is_none()) + .return_once(|_name, _provider| ()) + .once(); - { - 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); - } + let binding_builder = AsyncBindingBuilder::< + dyn subjects_async::IUserManager, + MockAsyncDIContainer, + >::new(Arc::new(di_container_mock)); - 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); - } + binding_builder.to::<subjects_async::UserManager>().await?; Ok(()) } + /* #[tokio::test] #[cfg(feature = "factory")] async fn can_bind_to_factory() -> Result<(), Box<dyn Error>> @@ -613,41 +554,5 @@ mod tests 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/scope_configurator.rs b/src/di_container/asynchronous/binding/scope_configurator.rs index 2b0f0b3..fd42fea 100644 --- a/src/di_container/asynchronous/binding/scope_configurator.rs +++ b/src/di_container/asynchronous/binding/scope_configurator.rs @@ -1,31 +1,38 @@ -//! Scope configurator for a binding for types inside of a [`AsyncDIContainer`]. +//! Scope configurator for a binding for types inside of a [`IAsyncDIContainer`]. +//! +//! [`IAsyncDIContainer`]: crate::di_container::asynchronous::IAsyncDIContainer use std::marker::PhantomData; use std::sync::Arc; use crate::di_container::asynchronous::binding::when_configurator::AsyncBindingWhenConfigurator; +use crate::di_container::asynchronous::IAsyncDIContainer; 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> +/// Scope configurator for a binding for type 'Interface' inside a [`IAsyncDIContainer`]. +/// +/// [`IAsyncDIContainer`]: crate::di_container::asynchronous::IAsyncDIContainer +pub struct AsyncBindingScopeConfigurator<Interface, Implementation, DIContainerType> where Interface: 'static + ?Sized + Send + Sync, - Implementation: AsyncInjectable, + Implementation: AsyncInjectable<DIContainerType>, + DIContainerType: IAsyncDIContainer, { - di_container: Arc<AsyncDIContainer>, + di_container: Arc<DIContainerType>, interface_phantom: PhantomData<Interface>, implementation_phantom: PhantomData<Implementation>, } -impl<Interface, Implementation> AsyncBindingScopeConfigurator<Interface, Implementation> +impl<Interface, Implementation, DIContainerType> + AsyncBindingScopeConfigurator<Interface, Implementation, DIContainerType> where Interface: 'static + ?Sized + Send + Sync, - Implementation: AsyncInjectable, + Implementation: AsyncInjectable<DIContainerType>, + DIContainerType: IAsyncDIContainer, { - pub(crate) fn new(di_container: Arc<AsyncDIContainer>) -> Self + pub(crate) fn new(di_container: Arc<DIContainerType>) -> Self { Self { di_container, @@ -37,14 +44,18 @@ where /// Configures the binding to be in a transient scope. /// /// This is the default. - pub async fn in_transient_scope(&self) -> AsyncBindingWhenConfigurator<Interface> + pub async fn in_transient_scope( + &self, + ) -> AsyncBindingWhenConfigurator<Interface, DIContainerType> { - let mut bindings_lock = self.di_container.bindings.lock().await; - - bindings_lock.set::<Interface>( - None, - Box::new(AsyncTransientTypeProvider::<Implementation>::new()), - ); + self.di_container + .set_binding::<Interface>( + None, + Box::new( + AsyncTransientTypeProvider::<Implementation, DIContainerType>::new(), + ), + ) + .await; AsyncBindingWhenConfigurator::new(self.di_container.clone()) } @@ -55,7 +66,10 @@ where /// Will return Err if resolving the implementation fails. pub async fn in_singleton_scope( &self, - ) -> Result<AsyncBindingWhenConfigurator<Interface>, AsyncBindingScopeConfiguratorError> + ) -> Result< + AsyncBindingWhenConfigurator<Interface, DIContainerType>, + AsyncBindingScopeConfiguratorError, + > { let singleton: ThreadsafeSingletonPtr<Implementation> = ThreadsafeSingletonPtr::from( @@ -66,10 +80,12 @@ where )?, ); - let mut bindings_lock = self.di_container.bindings.lock().await; - - bindings_lock - .set::<Interface>(None, Box::new(AsyncSingletonProvider::new(singleton))); + self.di_container + .set_binding::<Interface>( + None, + Box::new(AsyncSingletonProvider::new(singleton)), + ) + .await; 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 index b245ad8..3175420 100644 --- a/src/di_container/asynchronous/binding/when_configurator.rs +++ b/src/di_container/asynchronous/binding/when_configurator.rs @@ -1,25 +1,31 @@ -//! When configurator for a binding for types inside of a [`AsyncDIContainer`]. +//! When configurator for a binding for types inside of a [`IAsyncDIContainer`]. +//! +//! [`IAsyncDIContainer`]: crate::di_container::asynchronous::IAsyncDIContainer use std::any::type_name; use std::marker::PhantomData; use std::sync::Arc; +use crate::di_container::asynchronous::IAsyncDIContainer; 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> +/// When configurator for a binding for type 'Interface' inside a [`IAsyncDIContainer`]. +/// +/// [`IAsyncDIContainer`]: crate::di_container::asynchronous::IAsyncDIContainer +pub struct AsyncBindingWhenConfigurator<Interface, DIContainerType> where Interface: 'static + ?Sized + Send + Sync, + DIContainerType: IAsyncDIContainer, { - di_container: Arc<AsyncDIContainer>, + di_container: Arc<DIContainerType>, interface_phantom: PhantomData<Interface>, } -impl<Interface> AsyncBindingWhenConfigurator<Interface> +impl<Interface, DIContainerType> AsyncBindingWhenConfigurator<Interface, DIContainerType> where Interface: 'static + ?Sized + Send + Sync, + DIContainerType: IAsyncDIContainer, { - pub(crate) fn new(di_container: Arc<AsyncDIContainer>) -> Self + pub(crate) fn new(di_container: Arc<DIContainerType>) -> Self { Self { di_container, @@ -36,18 +42,22 @@ where name: &'static str, ) -> Result<(), AsyncBindingWhenConfiguratorError> { - let mut bindings_lock = self.di_container.bindings.lock().await; + let binding = self + .di_container + .remove_binding::<Interface>(None) + .await + .map_or_else( + || { + Err(AsyncBindingWhenConfiguratorError::BindingNotFound( + type_name::<Interface>(), + )) + }, + Ok, + )?; - let binding = bindings_lock.remove::<Interface>(None).map_or_else( - || { - Err(AsyncBindingWhenConfiguratorError::BindingNotFound( - type_name::<Interface>(), - )) - }, - Ok, - )?; - - bindings_lock.set::<Interface>(Some(name), binding); + self.di_container + .set_binding::<Interface>(Some(name), binding) + .await; Ok(()) } diff --git a/src/di_container/asynchronous/mod.rs b/src/di_container/asynchronous/mod.rs index b6653a5..14e6abe 100644 --- a/src/di_container/asynchronous/mod.rs +++ b/src/di_container/asynchronous/mod.rs @@ -5,7 +5,8 @@ //! use std::collections::HashMap; //! use std::error::Error; //! -//! use syrette::{injectable, AsyncDIContainer}; +//! use syrette::di_container::asynchronous::prelude::*; +//! use syrette::injectable; //! //! trait IDatabaseService: Send + Sync //! { @@ -53,6 +54,7 @@ use std::any::type_name; use std::sync::Arc; +use async_trait::async_trait; use tokio::sync::Mutex; use crate::di_container::asynchronous::binding::builder::AsyncBindingBuilder; @@ -65,11 +67,64 @@ use crate::provider::r#async::{AsyncProvidable, IAsyncProvider}; use crate::ptr::{SomeThreadsafePtr, TransientPtr}; pub mod binding; +pub mod prelude; + +/// Dependency injection container interface. +#[async_trait] +pub trait IAsyncDIContainer: + Sized + 'static + Send + Sync + details::DIContainerInternals +{ + /// Returns a new [`AsyncBindingBuilder`] for the given interface. + #[must_use] + fn bind<Interface>(self: &mut Arc<Self>) -> AsyncBindingBuilder<Interface, Self> + where + Interface: 'static + ?Sized + Send + Sync; + + /// 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 + fn get<'a, 'b, Interface>( + self: &'a Arc<Self>, + ) -> BoxFuture<'b, Result<SomeThreadsafePtr<Interface>, AsyncDIContainerError>> + where + Interface: 'static + 'b + ?Sized + Send + Sync, + 'a: 'b, + Self: 'b; + + /// 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 + fn get_named<'a, 'b, Interface>( + self: &'a Arc<Self>, + name: &'static str, + ) -> BoxFuture<'b, Result<SomeThreadsafePtr<Interface>, AsyncDIContainerError>> + where + Interface: 'static + 'b + ?Sized + Send + Sync, + 'a: 'b, + Self: 'b; + + #[doc(hidden)] + 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; +} /// Dependency injection container. pub struct AsyncDIContainer { - bindings: Mutex<DIContainerBindingMap<dyn IAsyncProvider>>, + bindings: Mutex<DIContainerBindingMap<dyn IAsyncProvider<Self>>>, } impl AsyncDIContainer @@ -82,51 +137,43 @@ impl AsyncDIContainer bindings: Mutex::new(DIContainerBindingMap::new()), }) } +} - /// Returns a new [`AsyncBindingBuilder`] for the given interface. +#[async_trait] +impl IAsyncDIContainer for AsyncDIContainer +{ #[must_use] - pub fn bind<Interface>(self: &mut Arc<Self>) -> AsyncBindingBuilder<Interface> + fn bind<Interface>(self: &mut Arc<Self>) -> AsyncBindingBuilder<Interface, Self> where Interface: 'static + ?Sized + Send + Sync, { - AsyncBindingBuilder::<Interface>::new(self.clone()) + AsyncBindingBuilder::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> + fn get<'a, 'b, Interface>( + self: &'a Arc<Self>, + ) -> BoxFuture<'b, Result<SomeThreadsafePtr<Interface>, AsyncDIContainerError>> where - Interface: 'static + ?Sized + Send + Sync, + Interface: 'static + 'b + ?Sized + Send + Sync, + 'a: 'b, + Self: 'b, { - self.get_bound::<Interface>(Vec::new(), None).await + Box::pin(async { 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>, + fn get_named<'a, 'b, Interface>( + self: &'a Arc<Self>, name: &'static str, - ) -> Result<SomeThreadsafePtr<Interface>, AsyncDIContainerError> + ) -> BoxFuture<'b, Result<SomeThreadsafePtr<Interface>, AsyncDIContainerError>> where - Interface: 'static + ?Sized + Send + Sync, + Interface: 'static + 'b + ?Sized + Send + Sync, + 'a: 'b, + Self: 'b, { - self.get_bound::<Interface>(Vec::new(), Some(name)).await + Box::pin(async { self.get_bound::<Interface>(Vec::new(), Some(name)).await }) } - #[doc(hidden)] - pub async fn get_bound<Interface>( + async fn get_bound<Interface>( self: &Arc<Self>, dependency_history: Vec<&'static str>, name: Option<&'static str>, @@ -140,10 +187,44 @@ impl AsyncDIContainer self.handle_binding_providable(binding_providable).await } +} + +#[async_trait] +impl details::DIContainerInternals for AsyncDIContainer +{ + async fn has_binding<Interface>(self: &Arc<Self>, name: Option<&'static str>) -> bool + where + Interface: ?Sized + 'static, + { + self.bindings.lock().await.has::<Interface>(name) + } + + async fn set_binding<Interface>( + self: &Arc<Self>, + name: Option<&'static str>, + provider: Box<dyn IAsyncProvider<Self>>, + ) where + Interface: 'static + ?Sized, + { + self.bindings.lock().await.set::<Interface>(name, provider); + } + async fn remove_binding<Interface>( + self: &Arc<Self>, + name: Option<&'static str>, + ) -> Option<Box<dyn IAsyncProvider<Self>>> + where + Interface: 'static + ?Sized, + { + self.bindings.lock().await.remove::<Interface>(name) + } +} + +impl AsyncDIContainer +{ async fn handle_binding_providable<Interface>( self: &Arc<Self>, - binding_providable: AsyncProvidable, + binding_providable: AsyncProvidable<Self>, ) -> Result<SomeThreadsafePtr<Interface>, AsyncDIContainerError> where Interface: 'static + ?Sized + Send + Sync, @@ -261,7 +342,7 @@ impl AsyncDIContainer self: &Arc<Self>, name: Option<&'static str>, dependency_history: Vec<&'static str>, - ) -> Result<AsyncProvidable, AsyncDIContainerError> + ) -> Result<AsyncProvidable<Self>, AsyncDIContainerError> where Interface: 'static + ?Sized + Send + Sync, { @@ -294,6 +375,40 @@ impl AsyncDIContainer } } +pub(crate) mod details +{ + use std::sync::Arc; + + use async_trait::async_trait; + + use crate::provider::r#async::IAsyncProvider; + + #[async_trait] + pub trait DIContainerInternals + { + async fn has_binding<Interface>( + self: &Arc<Self>, + name: Option<&'static str>, + ) -> bool + where + Interface: ?Sized + 'static; + + async fn set_binding<Interface>( + self: &Arc<Self>, + name: Option<&'static str>, + provider: Box<dyn IAsyncProvider<Self>>, + ) where + Interface: 'static + ?Sized; + + async fn remove_binding<Interface>( + self: &Arc<Self>, + name: Option<&'static str>, + ) -> Option<Box<dyn IAsyncProvider<Self>>> + where + Interface: 'static + ?Sized; + } +} + #[cfg(test)] mod tests { @@ -314,15 +429,15 @@ mod tests Provider {} #[async_trait] - impl IAsyncProvider for Provider + impl IAsyncProvider<AsyncDIContainer> for Provider { async fn provide( &self, di_container: &Arc<AsyncDIContainer>, dependency_history: Vec<&'static str>, - ) -> Result<AsyncProvidable, InjectableError>; + ) -> Result<AsyncProvidable<AsyncDIContainer>, InjectableError>; - fn do_clone(&self) -> Box<dyn IAsyncProvider>; + fn do_clone(&self) -> Box<dyn IAsyncProvider<AsyncDIContainer>>; } } @@ -365,15 +480,15 @@ mod tests Provider {} #[async_trait] - impl IAsyncProvider for Provider + impl IAsyncProvider<AsyncDIContainer> for Provider { async fn provide( &self, di_container: &Arc<AsyncDIContainer>, dependency_history: Vec<&'static str>, - ) -> Result<AsyncProvidable, InjectableError>; + ) -> Result<AsyncProvidable<AsyncDIContainer>, InjectableError>; - fn do_clone(&self) -> Box<dyn IAsyncProvider>; + fn do_clone(&self) -> Box<dyn IAsyncProvider<AsyncDIContainer>>; } } @@ -419,15 +534,15 @@ mod tests Provider {} #[async_trait] - impl IAsyncProvider for Provider + impl IAsyncProvider<AsyncDIContainer> for Provider { async fn provide( &self, di_container: &Arc<AsyncDIContainer>, dependency_history: Vec<&'static str>, - ) -> Result<AsyncProvidable, InjectableError>; + ) -> Result<AsyncProvidable<AsyncDIContainer>, InjectableError>; - fn do_clone(&self) -> Box<dyn IAsyncProvider>; + fn do_clone(&self) -> Box<dyn IAsyncProvider<AsyncDIContainer>>; } } @@ -483,15 +598,15 @@ mod tests Provider {} #[async_trait] - impl IAsyncProvider for Provider + impl IAsyncProvider<AsyncDIContainer> for Provider { async fn provide( &self, di_container: &Arc<AsyncDIContainer>, dependency_history: Vec<&'static str>, - ) -> Result<AsyncProvidable, InjectableError>; + ) -> Result<AsyncProvidable<AsyncDIContainer>, InjectableError>; - fn do_clone(&self) -> Box<dyn IAsyncProvider>; + fn do_clone(&self) -> Box<dyn IAsyncProvider<AsyncDIContainer>>; } } @@ -593,15 +708,15 @@ mod tests Provider {} #[async_trait] - impl IAsyncProvider for Provider + impl IAsyncProvider<AsyncDIContainer> for Provider { async fn provide( &self, di_container: &Arc<AsyncDIContainer>, dependency_history: Vec<&'static str>, - ) -> Result<AsyncProvidable, InjectableError>; + ) -> Result<AsyncProvidable<AsyncDIContainer>, InjectableError>; - fn do_clone(&self) -> Box<dyn IAsyncProvider>; + fn do_clone(&self) -> Box<dyn IAsyncProvider<AsyncDIContainer>>; } } @@ -704,15 +819,15 @@ mod tests Provider {} #[async_trait] - impl IAsyncProvider for Provider + impl IAsyncProvider<AsyncDIContainer> for Provider { async fn provide( &self, di_container: &Arc<AsyncDIContainer>, dependency_history: Vec<&'static str>, - ) -> Result<AsyncProvidable, InjectableError>; + ) -> Result<AsyncProvidable<AsyncDIContainer>, InjectableError>; - fn do_clone(&self) -> Box<dyn IAsyncProvider>; + fn do_clone(&self) -> Box<dyn IAsyncProvider<AsyncDIContainer>>; } } diff --git a/src/di_container/asynchronous/prelude.rs b/src/di_container/asynchronous/prelude.rs new file mode 100644 index 0000000..50fc42b --- /dev/null +++ b/src/di_container/asynchronous/prelude.rs @@ -0,0 +1,3 @@ +//! Commonly used types. + +pub use crate::di_container::asynchronous::{AsyncDIContainer, IAsyncDIContainer}; diff --git a/src/di_container/binding_map.rs b/src/di_container/binding_map.rs index 6ecd34c..3a73f7a 100644 --- a/src/di_container/binding_map.rs +++ b/src/di_container/binding_map.rs @@ -81,13 +81,6 @@ where name, }) } - - /// Only used by tests in the `di_container` module. - #[cfg(test)] - pub fn count(&self) -> usize - { - self.bindings.len() - } } #[cfg(test)] diff --git a/src/di_container/blocking/binding/builder.rs b/src/di_container/blocking/binding/builder.rs index 8e15f0c..e1c1561 100644 --- a/src/di_container/blocking/binding/builder.rs +++ b/src/di_container/blocking/binding/builder.rs @@ -1,4 +1,6 @@ -//! Binding builder for types inside of a [`DIContainer`]. +//! Binding builder for types inside of a [`IDIContainer`]. +//! +//! [`IDIContainer`]: crate::di_container::blocking::IDIContainer use std::any::type_name; use std::marker::PhantomData; use std::rc::Rc; @@ -6,24 +8,28 @@ 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::di_container::blocking::IDIContainer; use crate::errors::di_container::BindingBuilderError; use crate::interfaces::injectable::Injectable; -/// Binding builder for type `Interface` inside a [`DIContainer`]. -pub struct BindingBuilder<Interface> +/// Binding builder for type `Interface` inside a [`IDIContainer`]. +/// +/// [`IDIContainer`]: crate::di_container::blocking::IDIContainer +pub struct BindingBuilder<Interface, DIContainerType> where Interface: 'static + ?Sized, + DIContainerType: IDIContainer, { - di_container: Rc<DIContainer>, + di_container: Rc<DIContainerType>, interface_phantom: PhantomData<Interface>, } -impl<Interface> BindingBuilder<Interface> +impl<Interface, DIContainerType> BindingBuilder<Interface, DIContainerType> where Interface: 'static + ?Sized, + DIContainerType: IDIContainer, { - pub(crate) fn new(di_container: Rc<DIContainer>) -> Self + pub(crate) fn new(di_container: Rc<DIContainerType>) -> Self { Self { di_container, @@ -32,13 +38,13 @@ where } /// Creates a binding of type `Interface` to type `Implementation` inside of the - /// associated [`DIContainer`]. + /// associated [`IDIContainer`]. /// /// 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 + /// Will return Err if the associated [`IDIContainer`] already have a binding for /// the interface. /// /// # Examples @@ -46,6 +52,7 @@ where /// # use std::error::Error; /// # /// # use syrette::{DIContainer, injectable}; + /// # use syrette::di_container::blocking::IDIContainer; /// # /// # trait Foo {} /// # @@ -70,16 +77,19 @@ where /// # Ok(()) /// # } /// ``` + /// + /// [`IDIContainer`]: crate::di_container::blocking::IDIContainer pub fn to<Implementation>( &self, - ) -> Result<BindingScopeConfigurator<Interface, Implementation>, BindingBuilderError> + ) -> Result< + BindingScopeConfigurator<Interface, Implementation, DIContainerType>, + BindingBuilderError, + > where - Implementation: Injectable, + Implementation: Injectable<DIContainerType>, { { - let bindings = self.di_container.bindings.borrow(); - - if bindings.has::<Interface>(None) { + if self.di_container.has_binding::<Interface>(None) { return Err(BindingBuilderError::BindingAlreadyExists(type_name::< Interface, >( @@ -96,10 +106,10 @@ where } /// Creates a binding of factory type `Interface` to a factory inside of the - /// associated [`DIContainer`]. + /// associated [`IDIContainer`]. /// /// # Errors - /// Will return Err if the associated [`DIContainer`] already have a binding for + /// Will return Err if the associated [`IDIContainer`] already have a binding for /// the interface. /// /// # Examples @@ -108,6 +118,7 @@ where /// # /// # use syrette::{DIContainer, factory}; /// # use syrette::ptr::TransientPtr; + /// # use syrette::di_container::blocking::IDIContainer; /// # /// # trait ICustomerID {} /// # trait ICustomer {} @@ -158,36 +169,31 @@ where /// # Ok(()) /// # } /// ``` + /// + /// [`IDIContainer`]: crate::di_container::blocking::IDIContainer #[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> + ) -> Result<BindingWhenConfigurator<Interface, DIContainerType>, BindingBuilderError> where Args: 'static, Return: 'static + ?Sized, Interface: Fn<Args, Output = crate::ptr::TransientPtr<Return>>, - Func: Fn<(std::rc::Rc<DIContainer>,), Output = Box<Interface>>, + Func: Fn<(std::rc::Rc<DIContainerType>,), 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, - >( - ))); - } + if self.di_container.has_binding::<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>( + self.di_container.set_binding::<Interface>( None, Box::new(crate::provider::blocking::FactoryProvider::new( crate::ptr::FactoryPtr::new(factory_impl), @@ -199,10 +205,10 @@ where } /// Creates a binding of type `Interface` to a factory that takes no arguments - /// inside of the associated [`DIContainer`]. + /// inside of the associated [`IDIContainer`]. /// /// # Errors - /// Will return Err if the associated [`DIContainer`] already have a binding for + /// Will return Err if the associated [`IDIContainer`] already have a binding for /// the interface. /// /// # Examples @@ -211,6 +217,7 @@ where /// # /// # use syrette::{DIContainer, factory}; /// # use syrette::ptr::TransientPtr; + /// # use syrette::di_container::blocking::IDIContainer; /// # /// # trait IBuffer {} /// # @@ -248,16 +255,18 @@ where /// # Ok(()) /// # } /// ``` + /// + /// [`IDIContainer`]: crate::di_container::blocking::IDIContainer #[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> + ) -> Result<BindingWhenConfigurator<Interface, DIContainerType>, BindingBuilderError> where Return: 'static + ?Sized, FactoryFunc: Fn< - (Rc<DIContainer>,), + (Rc<DIContainerType>,), Output = crate::ptr::TransientPtr< dyn Fn<(), Output = crate::ptr::TransientPtr<Return>>, >, @@ -265,22 +274,15 @@ where { 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, - >( - ))); - } + if self.di_container.has_binding::<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>( + self.di_container.set_binding::<Interface>( None, Box::new(crate::provider::blocking::FactoryProvider::new( crate::ptr::FactoryPtr::new(factory_impl), @@ -297,92 +299,34 @@ mod tests { use std::error::Error; + use mockall::predicate::eq; + use super::*; - use crate::ptr::TransientPtr; - use crate::test_utils::subjects; + use crate::test_utils::{mocks, subjects}; #[test] fn can_bind_to() -> Result<(), Box<dyn Error>> { - let mut di_container = DIContainer::new(); + let mut mock_di_container = mocks::blocking_di_container::MockDIContainer::new(); - assert_eq!(di_container.bindings.borrow().count(), 0); + mock_di_container + .expect_has_binding::<dyn subjects::INumber>() + .with(eq(None)) + .return_once(|_name| false) + .once(); - di_container - .bind::<dyn subjects::IUserManager>() - .to::<subjects::UserManager>()?; + mock_di_container + .expect_set_binding::<dyn subjects::INumber>() + .withf(|name, _provider| name.is_none()) + .return_once(|_name, _provider| ()) + .once(); - assert_eq!(di_container.bindings.borrow().count(), 1); + let binding_builder = BindingBuilder::< + dyn subjects::INumber, + mocks::blocking_di_container::MockDIContainer, + >::new(Rc::new(mock_di_container)); - 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); + binding_builder.to::<subjects::Number>()?; Ok(()) } @@ -393,57 +337,80 @@ mod tests { use crate as syrette; use crate::factory; + use crate::ptr::TransientPtr; #[factory] - type IUserManagerFactory = dyn Fn() -> dyn subjects::IUserManager; + type IUserManagerFactory = dyn Fn(i32, String) -> dyn subjects::IUserManager; - let mut di_container = DIContainer::new(); + let mut mock_di_container = mocks::blocking_di_container::MockDIContainer::new(); - assert_eq!(di_container.bindings.borrow().count(), 0); + mock_di_container + .expect_has_binding::<IUserManagerFactory>() + .with(eq(None)) + .return_once(|_name| false) + .once(); - di_container - .bind::<IUserManagerFactory>() - .to_factory(&|_| { - Box::new(move || { - let user_manager: TransientPtr<dyn subjects::IUserManager> = - TransientPtr::new(subjects::UserManager::new()); + mock_di_container + .expect_set_binding::<IUserManagerFactory>() + .withf(|name, _provider| name.is_none()) + .return_once(|_name, _provider| ()) + .once(); - user_manager - }) - })?; + let binding_builder = BindingBuilder::< + IUserManagerFactory, + mocks::blocking_di_container::MockDIContainer, + >::new(Rc::new(mock_di_container)); - assert_eq!(di_container.bindings.borrow().count(), 1); + binding_builder.to_factory(&|_| { + Box::new(move |_num, _text| { + let user_manager: TransientPtr<dyn subjects::IUserManager> = + TransientPtr::new(subjects::UserManager::new()); + + user_manager + }) + })?; Ok(()) } #[test] #[cfg(feature = "factory")] - fn can_bind_to_factory_when_named() -> Result<(), Box<dyn Error>> + fn can_bind_to_default_factory() -> Result<(), Box<dyn Error>> { + use syrette_macros::declare_default_factory; + use crate as syrette; - use crate::factory; + use crate::ptr::TransientPtr; - #[factory] - type IUserManagerFactory = dyn Fn() -> dyn subjects::IUserManager; + declare_default_factory!(dyn subjects::IUserManager); + + let mut mock_di_container = mocks::blocking_di_container::MockDIContainer::new(); - let mut di_container = DIContainer::new(); + mock_di_container + .expect_has_binding::<dyn subjects::IUserManager>() + .with(eq(None)) + .return_once(|_name| false) + .once(); - assert_eq!(di_container.bindings.borrow().count(), 0); + mock_di_container + .expect_set_binding::<dyn subjects::IUserManager>() + .withf(|name, _provider| name.is_none()) + .return_once(|_name, _provider| ()) + .once(); - di_container - .bind::<IUserManagerFactory>() - .to_factory(&|_| { - Box::new(move || { - let user_manager: TransientPtr<dyn subjects::IUserManager> = - TransientPtr::new(subjects::UserManager::new()); + let binding_builder = BindingBuilder::< + dyn subjects::IUserManager, + mocks::blocking_di_container::MockDIContainer, + >::new(Rc::new(mock_di_container)); - user_manager - }) - })? - .when_named("awesome")?; + binding_builder.to_default_factory(&|_| { + Box::new(move || { + let user_manager: TransientPtr<dyn subjects::IUserManager> = + TransientPtr::new(subjects::UserManager::new()); - assert_eq!(di_container.bindings.borrow().count(), 1); + user_manager + }) + })?; Ok(()) } diff --git a/src/di_container/blocking/binding/scope_configurator.rs b/src/di_container/blocking/binding/scope_configurator.rs index 09897b6..5553415 100644 --- a/src/di_container/blocking/binding/scope_configurator.rs +++ b/src/di_container/blocking/binding/scope_configurator.rs @@ -1,31 +1,38 @@ -//! Scope configurator for a binding for types inside of a [`DIContainer`]. +//! Scope configurator for a binding for types inside of a [`IDIContainer`]. +//! +//! [`IDIContainer`]: crate::di_container::blocking::IDIContainer 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::di_container::blocking::IDIContainer; 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> +/// Scope configurator for a binding for type 'Interface' inside a [`IDIContainer`]. +/// +/// [`IDIContainer`]: crate::di_container::blocking::IDIContainer +pub struct BindingScopeConfigurator<Interface, Implementation, DIContainerType> where Interface: 'static + ?Sized, - Implementation: Injectable, + Implementation: Injectable<DIContainerType>, + DIContainerType: IDIContainer, { - di_container: Rc<DIContainer>, + di_container: Rc<DIContainerType>, interface_phantom: PhantomData<Interface>, implementation_phantom: PhantomData<Implementation>, } -impl<Interface, Implementation> BindingScopeConfigurator<Interface, Implementation> +impl<Interface, Implementation, DIContainerType> + BindingScopeConfigurator<Interface, Implementation, DIContainerType> where Interface: 'static + ?Sized, - Implementation: Injectable, + Implementation: Injectable<DIContainerType>, + DIContainerType: IDIContainer, { - pub(crate) fn new(di_container: Rc<DIContainer>) -> Self + pub(crate) fn new(di_container: Rc<DIContainerType>) -> Self { Self { di_container, @@ -38,13 +45,13 @@ where /// /// This is the default. #[allow(clippy::must_use_candidate)] - pub fn in_transient_scope(&self) -> BindingWhenConfigurator<Interface> + pub fn in_transient_scope( + &self, + ) -> BindingWhenConfigurator<Interface, DIContainerType> { - let mut bindings_mut = self.di_container.bindings.borrow_mut(); - - bindings_mut.set::<Interface>( + self.di_container.set_binding::<Interface>( None, - Box::new(TransientTypeProvider::<Implementation>::new()), + Box::new(TransientTypeProvider::<Implementation, DIContainerType>::new()), ); BindingWhenConfigurator::new(self.di_container.clone()) @@ -56,16 +63,18 @@ where /// Will return Err if resolving the implementation fails. pub fn in_singleton_scope( &self, - ) -> Result<BindingWhenConfigurator<Interface>, BindingScopeConfiguratorError> + ) -> Result< + BindingWhenConfigurator<Interface, DIContainerType>, + 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))); + self.di_container + .set_binding::<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 index 9cd9bb6..5b9a8c0 100644 --- a/src/di_container/blocking/binding/when_configurator.rs +++ b/src/di_container/blocking/binding/when_configurator.rs @@ -1,25 +1,31 @@ -//! When configurator for a binding for types inside of a [`DIContainer`]. +//! When configurator for a binding for types inside of a [`IDIContainer`]. +//! +//! [`IDIContainer`]: crate::di_container::blocking::IDIContainer use std::any::type_name; use std::marker::PhantomData; use std::rc::Rc; -use crate::di_container::blocking::DIContainer; +use crate::di_container::blocking::IDIContainer; use crate::errors::di_container::BindingWhenConfiguratorError; -/// When configurator for a binding for type 'Interface' inside a [`DIContainer`]. -pub struct BindingWhenConfigurator<Interface> +/// When configurator for a binding for type 'Interface' inside a [`IDIContainer`]. +/// +/// [`IDIContainer`]: crate::di_container::blocking::IDIContainer +pub struct BindingWhenConfigurator<Interface, DIContainerType> where Interface: 'static + ?Sized, + DIContainerType: IDIContainer, { - di_container: Rc<DIContainer>, + di_container: Rc<DIContainerType>, interface_phantom: PhantomData<Interface>, } -impl<Interface> BindingWhenConfigurator<Interface> +impl<Interface, DIContainerType> BindingWhenConfigurator<Interface, DIContainerType> where Interface: 'static + ?Sized, + DIContainerType: IDIContainer, { - pub(crate) fn new(di_container: Rc<DIContainer>) -> Self + pub(crate) fn new(di_container: Rc<DIContainerType>) -> Self { Self { di_container, @@ -36,19 +42,21 @@ where name: &'static str, ) -> Result<(), BindingWhenConfiguratorError> { - let mut bindings_mut = self.di_container.bindings.borrow_mut(); + let binding = self + .di_container + .remove_binding::<Interface>(None) + .map_or_else( + || { + Err(BindingWhenConfiguratorError::BindingNotFound(type_name::< + Interface, + >( + ))) + }, + Ok, + )?; - let binding = bindings_mut.remove::<Interface>(None).map_or_else( - || { - Err(BindingWhenConfiguratorError::BindingNotFound(type_name::< - Interface, - >( - ))) - }, - Ok, - )?; - - bindings_mut.set::<Interface>(Some(name), binding); + self.di_container + .set_binding::<Interface>(Some(name), binding); Ok(()) } diff --git a/src/di_container/blocking/mod.rs b/src/di_container/blocking/mod.rs index f6b64fa..3b9c16e 100644 --- a/src/di_container/blocking/mod.rs +++ b/src/di_container/blocking/mod.rs @@ -5,6 +5,7 @@ //! use std::collections::HashMap; //! use std::error::Error; //! +//! use syrette::di_container::blocking::IDIContainer; //! use syrette::{injectable, DIContainer}; //! //! trait IDatabaseService @@ -61,11 +62,55 @@ use crate::provider::blocking::{IProvider, Providable}; use crate::ptr::SomePtr; pub mod binding; +pub mod prelude; -/// Dependency injection container. +/// Blocking dependency injection container interface. +pub trait IDIContainer: Sized + 'static + details::DIContainerInternals +{ + /// Returns a new [`BindingBuilder`] for the given interface. + fn bind<Interface>(self: &mut Rc<Self>) -> BindingBuilder<Interface, Self> + where + Interface: 'static + ?Sized; + + /// 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 + fn get<Interface>(self: &Rc<Self>) -> Result<SomePtr<Interface>, DIContainerError> + where + Interface: 'static + ?Sized; + + /// 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 + fn get_named<Interface>( + self: &Rc<Self>, + name: &'static str, + ) -> Result<SomePtr<Interface>, DIContainerError> + where + Interface: 'static + ?Sized; + + #[doc(hidden)] + fn get_bound<Interface>( + self: &Rc<Self>, + dependency_history: Vec<&'static str>, + name: Option<&'static str>, + ) -> Result<SomePtr<Interface>, DIContainerError> + where + Interface: 'static + ?Sized; +} + +/// Blocking dependency injection container. pub struct DIContainer { - bindings: RefCell<DIContainerBindingMap<dyn IProvider>>, + bindings: RefCell<DIContainerBindingMap<dyn IProvider<Self>>>, } impl DIContainer @@ -78,38 +123,26 @@ impl DIContainer bindings: RefCell::new(DIContainerBindingMap::new()), }) } +} - /// Returns a new [`BindingBuilder`] for the given interface. +impl IDIContainer for DIContainer +{ #[must_use] - pub fn bind<Interface>(self: &mut Rc<Self>) -> BindingBuilder<Interface> + fn bind<Interface>(self: &mut Rc<Self>) -> BindingBuilder<Interface, Self> where Interface: 'static + ?Sized, { - BindingBuilder::<Interface>::new(self.clone()) + BindingBuilder::<Interface, Self>::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> + 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>( + fn get_named<Interface>( self: &Rc<Self>, name: &'static str, ) -> Result<SomePtr<Interface>, DIContainerError> @@ -120,7 +153,7 @@ impl DIContainer } #[doc(hidden)] - pub fn get_bound<Interface>( + fn get_bound<Interface>( self: &Rc<Self>, dependency_history: Vec<&'static str>, name: Option<&'static str>, @@ -133,10 +166,43 @@ impl DIContainer self.handle_binding_providable(binding_providable) } +} + +impl details::DIContainerInternals for DIContainer +{ + fn has_binding<Interface>(self: &Rc<Self>, name: Option<&'static str>) -> bool + where + Interface: ?Sized + 'static, + { + self.bindings.borrow().has::<Interface>(name) + } + + fn set_binding<Interface>( + self: &Rc<Self>, + name: Option<&'static str>, + provider: Box<dyn IProvider<Self>>, + ) where + Interface: 'static + ?Sized, + { + self.bindings.borrow_mut().set::<Interface>(name, provider); + } + + fn remove_binding<Interface>( + self: &Rc<Self>, + name: Option<&'static str>, + ) -> Option<Box<dyn IProvider<Self>>> + where + Interface: 'static + ?Sized, + { + self.bindings.borrow_mut().remove::<Interface>(name) + } +} +impl DIContainer +{ fn handle_binding_providable<Interface>( self: &Rc<Self>, - binding_providable: Providable, + binding_providable: Providable<Self>, ) -> Result<SomePtr<Interface>, DIContainerError> where Interface: 'static + ?Sized, @@ -195,7 +261,7 @@ impl DIContainer self: &Rc<Self>, name: Option<&'static str>, dependency_history: Vec<&'static str>, - ) -> Result<Providable, DIContainerError> + ) -> Result<Providable<Self>, DIContainerError> where Interface: 'static + ?Sized, { @@ -219,6 +285,34 @@ impl DIContainer } } +pub(crate) mod details +{ + use std::rc::Rc; + + use crate::provider::blocking::IProvider; + + pub trait DIContainerInternals + { + fn has_binding<Interface>(self: &Rc<Self>, name: Option<&'static str>) -> bool + where + Interface: ?Sized + 'static; + + fn set_binding<Interface>( + self: &Rc<Self>, + name: Option<&'static str>, + provider: Box<dyn IProvider<Self>>, + ) where + Interface: 'static + ?Sized; + + fn remove_binding<Interface>( + self: &Rc<Self>, + name: Option<&'static str>, + ) -> Option<Box<dyn IProvider<Self>>> + where + Interface: 'static + ?Sized; + } +} + #[cfg(test)] mod tests { @@ -238,13 +332,13 @@ mod tests mock! { Provider {} - impl IProvider for Provider + impl IProvider<DIContainer> for Provider { fn provide( &self, di_container: &Rc<DIContainer>, dependency_history: Vec<&'static str>, - ) -> Result<Providable, InjectableError>; + ) -> Result<Providable<DIContainer>, InjectableError>; } } @@ -276,13 +370,13 @@ mod tests mock! { Provider {} - impl IProvider for Provider + impl IProvider<DIContainer> for Provider { fn provide( &self, di_container: &Rc<DIContainer>, dependency_history: Vec<&'static str>, - ) -> Result<Providable, InjectableError>; + ) -> Result<Providable<DIContainer>, InjectableError>; } } @@ -314,13 +408,13 @@ mod tests mock! { Provider {} - impl IProvider for Provider + impl IProvider<DIContainer> for Provider { fn provide( &self, di_container: &Rc<DIContainer>, dependency_history: Vec<&'static str>, - ) -> Result<Providable, InjectableError>; + ) -> Result<Providable<DIContainer>, InjectableError>; } } @@ -359,13 +453,13 @@ mod tests mock! { Provider {} - impl IProvider for Provider + impl IProvider<DIContainer> for Provider { fn provide( &self, di_container: &Rc<DIContainer>, dependency_history: Vec<&'static str>, - ) -> Result<Providable, InjectableError>; + ) -> Result<Providable<DIContainer>, InjectableError>; } } @@ -459,13 +553,13 @@ mod tests mock! { Provider {} - impl IProvider for Provider + impl IProvider<DIContainer> for Provider { fn provide( &self, di_container: &Rc<DIContainer>, dependency_history: Vec<&'static str>, - ) -> Result<Providable, InjectableError>; + ) -> Result<Providable<DIContainer>, InjectableError>; } } @@ -556,13 +650,13 @@ mod tests mock! { Provider {} - impl IProvider for Provider + impl IProvider<DIContainer> for Provider { fn provide( &self, di_container: &Rc<DIContainer>, dependency_history: Vec<&'static str>, - ) -> Result<Providable, InjectableError>; + ) -> Result<Providable<DIContainer>, InjectableError>; } } diff --git a/src/di_container/blocking/prelude.rs b/src/di_container/blocking/prelude.rs new file mode 100644 index 0000000..82db5e3 --- /dev/null +++ b/src/di_container/blocking/prelude.rs @@ -0,0 +1,2 @@ +//! Commonly used types. +pub use crate::di_container::blocking::{DIContainer, IDIContainer}; |