From 5b0c6a52022e67a2d9cee251b3d08b9cb2b5f6cb Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 9 Oct 2022 12:05:24 +0200 Subject: refactor!: reorganize DI containers BREAKING CHANGE: DIContainer, AsyncDIContainer & the binding structs have been relocated --- src/di_container/blocking/mod.rs | 599 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 599 insertions(+) create mode 100644 src/di_container/blocking/mod.rs (limited to 'src/di_container/blocking/mod.rs') 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; +//! } +//! +//! struct DatabaseService {} +//! +//! #[injectable(IDatabaseService)] +//! impl DatabaseService +//! { +//! fn new() -> Self +//! { +//! Self {} +//! } +//! } +//! +//! impl IDatabaseService for DatabaseService +//! { +//! fn get_all_records(&self, table_name: String) -> HashMap +//! { +//! // Do stuff here +//! HashMap::::new() +//! } +//! } +//! +//! fn main() -> Result<(), Box> +//! { +//! let mut di_container = DIContainer::new(); +//! +//! di_container +//! .bind::() +//! .to::() +//! .map_err(|err| err.to_string())?; +//! +//! let database_service = di_container +//! .get::() +//! .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>, +} + +impl DIContainer +{ + /// Returns a new `DIContainer`. + #[must_use] + pub fn new() -> Rc + { + Rc::new(Self { + bindings: RefCell::new(DIContainerBindingMap::new()), + }) + } + + /// Returns a new [`BindingBuilder`] for the given interface. + #[must_use] + pub fn bind(self: &mut Rc) -> BindingBuilder + where + Interface: 'static + ?Sized, + { + BindingBuilder::::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(self: &Rc) -> Result, DIContainerError> + where + Interface: 'static + ?Sized, + { + self.get_bound::(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( + self: &Rc, + name: &'static str, + ) -> Result, DIContainerError> + where + Interface: 'static + ?Sized, + { + self.get_bound::(Vec::new(), Some(name)) + } + + #[doc(hidden)] + pub fn get_bound( + self: &Rc, + dependency_history: Vec<&'static str>, + name: Option<&'static str>, + ) -> Result, DIContainerError> + where + Interface: 'static + ?Sized, + { + let binding_providable = + self.get_binding_providable::(name, dependency_history)?; + + self.handle_binding_providable(binding_providable) + } + + fn handle_binding_providable( + self: &Rc, + binding_providable: Providable, + ) -> Result, DIContainerError> + where + Interface: 'static + ?Sized, + { + match binding_providable { + Providable::Transient(transient_binding) => Ok(SomePtr::Transient( + transient_binding.cast::().map_err(|_| { + DIContainerError::CastFailed { + interface: type_name::(), + binding_kind: "transient", + } + })?, + )), + Providable::Singleton(singleton_binding) => Ok(SomePtr::Singleton( + singleton_binding.cast::().map_err(|_| { + DIContainerError::CastFailed { + interface: type_name::(), + binding_kind: "singleton", + } + })?, + )), + #[cfg(feature = "factory")] + Providable::Factory(factory_binding) => { + use crate::interfaces::factory::IFactory; + + let factory = factory_binding + .cast::,), Interface>>() + .map_err(|_| DIContainerError::CastFailed { + interface: type_name::(), + 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 Fn<(), Output = TransientPtr>, + >>() + .map_err(|_| DIContainerError::CastFailed { + interface: type_name::(), + binding_kind: "default factory", + })?; + + Ok(SomePtr::Transient(default_factory(self.clone())())) + } + } + } + + fn get_binding_providable( + self: &Rc, + name: Option<&'static str>, + dependency_history: Vec<&'static str>, + ) -> Result + where + Interface: 'static + ?Sized, + { + self.bindings + .borrow() + .get::(name) + .map_or_else( + || { + Err(DIContainerError::BindingNotFound { + interface: type_name::(), + name, + }) + }, + Ok, + )? + .provide(self, dependency_history) + .map_err(|err| DIContainerError::BindingResolveFailed { + reason: err, + interface: type_name::(), + }) + } +} + +#[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> + { + mock! { + Provider {} + + impl IProvider for Provider + { + fn provide( + &self, + di_container: &Rc, + dependency_history: Vec<&'static str>, + ) -> Result; + } + } + + 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::(None, Box::new(mock_provider)); + + di_container + .get::()? + .transient()?; + + Ok(()) + } + + #[test] + fn can_get_named() -> Result<(), Box> + { + mock! { + Provider {} + + impl IProvider for Provider + { + fn provide( + &self, + di_container: &Rc, + dependency_history: Vec<&'static str>, + ) -> Result; + } + } + + 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::(Some("special"), Box::new(mock_provider)); + + di_container + .get_named::("special")? + .transient()?; + + Ok(()) + } + + #[test] + fn can_get_singleton() -> Result<(), Box> + { + mock! { + Provider {} + + impl IProvider for Provider + { + fn provide( + &self, + di_container: &Rc, + dependency_history: Vec<&'static str>, + ) -> Result; + } + } + + 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::(None, Box::new(mock_provider)); + + let first_number_rc = di_container.get::()?.singleton()?; + + assert_eq!(first_number_rc.get(), 2820); + + let second_number_rc = + di_container.get::()?.singleton()?; + + assert_eq!(first_number_rc.as_ref(), second_number_rc.as_ref()); + + Ok(()) + } + + #[test] + fn can_get_singleton_named() -> Result<(), Box> + { + mock! { + Provider {} + + impl IProvider for Provider + { + fn provide( + &self, + di_container: &Rc, + dependency_history: Vec<&'static str>, + ) -> Result; + } + } + + 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::(Some("cool"), Box::new(mock_provider)); + + let first_number_rc = di_container + .get_named::("cool")? + .singleton()?; + + assert_eq!(first_number_rc.get(), 2820); + + let second_number_rc = di_container + .get_named::("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> + { + 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, + } + + impl UserManager + { + fn new(users: Vec) -> 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,), + Output = Box< + dyn Fn<(Vec,), Output = crate::ptr::TransientPtr>, + >, + >; + + use crate as syrette; + + #[crate::factory] + type IUserManagerFactory = dyn Fn(Vec) -> dyn IUserManager; + + mock! { + Provider {} + + impl IProvider for Provider + { + fn provide( + &self, + di_container: &Rc, + dependency_history: Vec<&'static str>, + ) -> Result; + } + } + + let di_container = DIContainer::new(); + + let factory_func: &'static FactoryFunc = &|_: Rc| { + Box::new(move |users| { + let user_manager: TransientPtr = + 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::(None, Box::new(mock_provider)); + + di_container.get::()?.factory()?; + + Ok(()) + } + + #[test] + #[cfg(feature = "factory")] + fn can_get_factory_named() -> Result<(), Box> + { + 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, + } + + impl UserManager + { + fn new(users: Vec) -> 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,), + Output = Box< + dyn Fn<(Vec,), Output = crate::ptr::TransientPtr>, + >, + >; + + use crate as syrette; + + #[crate::factory] + type IUserManagerFactory = dyn Fn(Vec) -> dyn IUserManager; + + mock! { + Provider {} + + impl IProvider for Provider + { + fn provide( + &self, + di_container: &Rc, + dependency_history: Vec<&'static str>, + ) -> Result; + } + } + + let di_container = DIContainer::new(); + + let factory_func: &'static FactoryFunc = &|_: Rc| { + Box::new(move |users| { + let user_manager: TransientPtr = + 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::(Some("special"), Box::new(mock_provider)); + + di_container + .get_named::("special")? + .factory()?; + + Ok(()) + } +} -- cgit v1.2.3-18-g5258