aboutsummaryrefslogtreecommitdiff
path: root/src/di_container/blocking
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 /src/di_container/blocking
parent97c789e38bb8e61389a3808d241689e623144344 (diff)
refactor!: reorganize DI containers
BREAKING CHANGE: DIContainer, AsyncDIContainer & the binding structs have been relocated
Diffstat (limited to 'src/di_container/blocking')
-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
5 files changed, 1181 insertions, 0 deletions
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(())
+ }
+}