aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2022-10-09 12:05:24 +0200
committerHampusM <hampus@hampusmat.com>2022-10-09 17:03:01 +0200
commit5b0c6a52022e67a2d9cee251b3d08b9cb2b5f6cb (patch)
treec33f06eaab96ec43e477ea5ecd2af93e9d739097
parent97c789e38bb8e61389a3808d241689e623144344 (diff)
refactor!: reorganize DI containers
BREAKING CHANGE: DIContainer, AsyncDIContainer & the binding structs have been relocated
-rw-r--r--examples/async/bootstrap.rs3
-rw-r--r--macros/src/factory/build_declare_interfaces.rs12
-rw-r--r--macros/src/injectable/implementation.rs2
-rw-r--r--src/async_di_container.rs1641
-rw-r--r--src/di_container.rs1267
-rw-r--r--src/di_container/asynchronous/binding/builder.rs653
-rw-r--r--src/di_container/asynchronous/binding/mod.rs5
-rw-r--r--src/di_container/asynchronous/binding/scope_configurator.rs76
-rw-r--r--src/di_container/asynchronous/binding/when_configurator.rs54
-rw-r--r--src/di_container/asynchronous/mod.rs767
-rw-r--r--src/di_container/binding_map.rs (renamed from src/di_container_binding_map.rs)0
-rw-r--r--src/di_container/blocking/binding/builder.rs450
-rw-r--r--src/di_container/blocking/binding/mod.rs5
-rw-r--r--src/di_container/blocking/binding/scope_configurator.rs72
-rw-r--r--src/di_container/blocking/binding/when_configurator.rs55
-rw-r--r--src/di_container/blocking/mod.rs599
-rw-r--r--src/di_container/mod.rs9
-rw-r--r--src/interfaces/async_injectable.rs2
-rw-r--r--src/lib.rs12
-rw-r--r--src/provider/async.rs2
-rw-r--r--src/test_utils.rs253
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
diff --git a/src/lib.rs b/src/lib.rs
index a462c21..d01ecc2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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()))
+ }
+ }
+}