diff options
author | HampusM <hampus@hampusmat.com> | 2024-07-13 16:26:28 +0200 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2024-07-13 16:26:28 +0200 |
commit | ca86952c1486b7f2313fef62e6cacf36e248efd2 (patch) | |
tree | 774175aa8723328c1b0dbd3faa0e67d9106b3ba7 /src/di_container/blocking.rs | |
parent | 20d37eb93060e51970d3791c6c173e07ef5ad489 (diff) |
refactor: move & rename all mod.rs files
Diffstat (limited to 'src/di_container/blocking.rs')
-rw-r--r-- | src/di_container/blocking.rs | 722 |
1 files changed, 722 insertions, 0 deletions
diff --git a/src/di_container/blocking.rs b/src/di_container/blocking.rs new file mode 100644 index 0000000..d8b0d59 --- /dev/null +++ b/src/di_container/blocking.rs @@ -0,0 +1,722 @@ +//! 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 crate::di_container::binding_storage::DIContainerBindingStorage; +use crate::di_container::blocking::binding::builder::BindingBuilder; +use crate::di_container::BindingOptions; +use crate::errors::di_container::DIContainerError; +use crate::private::cast::boxed::CastBox; +use crate::private::cast::rc::CastRc; +use crate::provider::blocking::{IProvider, Providable}; +use crate::ptr::SomePtr; +use crate::util::use_double; + +use_double!(crate::dependency_history::DependencyHistory); + +pub mod binding; + +#[cfg(not(test))] +pub(crate) type BindingOptionsWithLt<'a> = BindingOptions<'a>; + +#[cfg(test)] +pub(crate) type BindingOptionsWithLt = BindingOptions<'static>; + +/// Blocking dependency injection container. +#[derive(Default)] +pub struct DIContainer +{ + binding_storage: DIContainerBindingStorage<dyn IProvider<Self>>, +} + +impl DIContainer +{ + /// Returns a new `DIContainer`. + #[must_use] + pub fn new() -> Self + { + Self { + binding_storage: DIContainerBindingStorage::new(), + } + } +} + +#[cfg_attr(test, mockall::automock)] +impl DIContainer +{ + /// Returns a new [`BindingBuilder`] for the given interface. + /// + /// # Examples + /// ``` + /// # use syrette::{DIContainer, injectable}; + /// # + /// # struct DiskWriter {} + /// # + /// # #[injectable] + /// # impl DiskWriter + /// # { + /// # fn new() -> Self + /// # { + /// # Self {} + /// # } + /// # } + /// # + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// let mut di_container = DIContainer::new(); + /// + /// di_container.bind::<DiskWriter>().to::<DiskWriter>()?; + /// # + /// # Ok(()) + /// # } + /// ``` + #[allow(clippy::missing_panics_doc)] + pub fn bind<Interface>(&mut self) -> BindingBuilder<'_, Interface> + where + Interface: 'static + ?Sized, + { + #[cfg(test)] + panic!("Nope"); + + #[cfg(not(test))] + BindingBuilder::new(self, DependencyHistory::new) + } + + /// 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 + /// + /// # Examples + /// ``` + /// # use syrette::{DIContainer, injectable}; + /// # + /// # struct DeviceManager {} + /// # + /// # #[injectable] + /// # impl DeviceManager + /// # { + /// # fn new() -> Self + /// # { + /// # Self {} + /// # } + /// # } + /// # + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// let mut di_container = DIContainer::new(); + /// + /// di_container.bind::<DeviceManager>().to::<DeviceManager>()?; + /// + /// let device_manager = di_container.get::<DeviceManager>()?.transient(); + /// # + /// # Ok(()) + /// # } + /// ``` + pub fn get<Interface>(&self) -> Result<SomePtr<Interface>, DIContainerError> + where + Interface: 'static + ?Sized, + { + self.get_bound::<Interface>(DependencyHistory::new(), BindingOptions::new()) + } + + /// 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 + /// + /// # Examples + /// ``` + /// # use syrette::{DIContainer, injectable}; + /// # + /// # struct DeviceManager {} + /// # + /// # #[injectable] + /// # impl DeviceManager + /// # { + /// # fn new() -> Self + /// # { + /// # Self {} + /// # } + /// # } + /// # + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// let mut di_container = DIContainer::new(); + /// + /// di_container + /// .bind::<DeviceManager>() + /// .to::<DeviceManager>()? + /// .in_transient_scope() + /// .when_named("usb")?; + /// + /// let device_manager = di_container.get_named::<DeviceManager>("usb")?.transient(); + /// # + /// # Ok(()) + /// # } + /// ``` + pub fn get_named<Interface>( + &self, + name: &'static str, + ) -> Result<SomePtr<Interface>, DIContainerError> + where + Interface: 'static + ?Sized, + { + self.get_bound::<Interface>( + DependencyHistory::new(), + BindingOptions::new().name(name), + ) + } + + /// Returns the type bound with `Interface` where the binding has the specified + /// options. + /// + /// `dependency_history` is passed to the bound type when it is being resolved. + /// + /// # Errors + /// Will return `Err` if: + /// - No binding for `Interface` exists + /// - Resolving the binding for `Interface` fails + /// - Casting the binding for `Interface` fails + /// + /// # Examples + /// ```no_run + /// # use syrette::di_container::blocking::DIContainer; + /// # use syrette::dependency_history::DependencyHistory; + /// # use syrette::di_container::BindingOptions; + /// # + /// # struct EventHandler {} + /// # struct Button {} + /// # + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// # let di_container = DIContainer::new(); + /// # + /// let mut dependency_history = DependencyHistory::new(); + /// + /// dependency_history.push::<EventHandler>(); + /// + /// di_container.get_bound::<Button>( + /// dependency_history, + /// BindingOptions::new().name("huge_red"), + /// )?; + /// # + /// # Ok(()) + /// # } + /// ``` + pub fn get_bound<Interface>( + &self, + dependency_history: DependencyHistory, + binding_options: BindingOptionsWithLt, + ) -> Result<SomePtr<Interface>, DIContainerError> + where + Interface: 'static + ?Sized, + { + let binding_providable = self + .get_binding_providable::<Interface>(binding_options, dependency_history)?; + + 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::castable_function::CastableFunction; + + let factory = factory_binding + .as_any() + .downcast_ref::<CastableFunction<Interface, Self>>() + .ok_or_else(|| DIContainerError::CastFailed { + interface: type_name::<Interface>(), + binding_kind: "factory", + })?; + + Ok(SomePtr::Factory(factory.call(self).into())) + } + #[cfg(feature = "factory")] + Providable::DefaultFactory(factory_binding) => { + use crate::castable_function::CastableFunction; + use crate::ptr::TransientPtr; + + type DefaultFactoryFn<Interface> = + CastableFunction<dyn Fn() -> TransientPtr<Interface>, DIContainer>; + + let default_factory = factory_binding + .as_any() + .downcast_ref::<DefaultFactoryFn<Interface>>() + .ok_or_else(|| DIContainerError::CastFailed { + interface: type_name::<Interface>(), + binding_kind: "default factory", + })?; + + Ok(SomePtr::Transient(default_factory.call(self)())) + } + } + } + + fn has_binding<Interface>(&self, binding_options: BindingOptionsWithLt) -> bool + where + Interface: ?Sized + 'static, + { + self.binding_storage.has::<Interface>(binding_options) + } + + fn set_binding<Interface>( + &mut self, + binding_options: BindingOptions<'static>, + provider: Box<dyn IProvider<Self>>, + ) where + Interface: 'static + ?Sized, + { + self.binding_storage + .set::<Interface>(binding_options, provider); + } + + fn remove_binding<Interface>( + &mut self, + binding_options: BindingOptions<'static>, + ) -> Option<Box<dyn IProvider<Self>>> + where + Interface: 'static + ?Sized, + { + self.binding_storage.remove::<Interface>(binding_options) + } +} + +impl DIContainer +{ + fn get_binding_providable<Interface>( + &self, + binding_options: BindingOptionsWithLt, + dependency_history: DependencyHistory, + ) -> Result<Providable<Self>, DIContainerError> + where + Interface: 'static + ?Sized, + { + let name = binding_options.name; + + self.binding_storage + .get::<Interface>(binding_options) + .map_or_else( + || { + Err(DIContainerError::BindingNotFound { + interface: type_name::<Interface>(), + name: name.as_ref().map(ToString::to_string), + }) + }, + Ok, + )? + .provide(self, dependency_history) + .map_err(|err| DIContainerError::BindingResolveFailed { + reason: err, + interface: type_name::<Interface>(), + }) + } +} + +#[cfg(test)] +mod tests +{ + use super::*; + use crate::provider::blocking::MockIProvider; + use crate::ptr::{SingletonPtr, TransientPtr}; + use crate::test_utils::subjects; + + #[test] + fn can_get() + { + let mut di_container = DIContainer::new(); + + let mut mock_provider = MockIProvider::new(); + + mock_provider.expect_provide().returning(|_, _| { + Ok(Providable::Transient(TransientPtr::new( + subjects::UserManager::new(), + ))) + }); + + di_container + .binding_storage + .set::<dyn subjects::IUserManager>( + BindingOptions::new(), + Box::new(mock_provider), + ); + + di_container + .get::<dyn subjects::IUserManager>() + .unwrap() + .transient() + .unwrap(); + } + + #[test] + fn can_get_named() + { + let mut di_container = DIContainer::new(); + + let mut mock_provider = MockIProvider::new(); + + mock_provider.expect_provide().returning(|_, _| { + Ok(Providable::Transient(TransientPtr::new( + subjects::UserManager::new(), + ))) + }); + + di_container + .binding_storage + .set::<dyn subjects::IUserManager>( + BindingOptions::new().name("special"), + Box::new(mock_provider), + ); + + di_container + .get_named::<dyn subjects::IUserManager>("special") + .unwrap() + .transient() + .unwrap(); + } + + #[test] + fn can_get_singleton() + { + let mut di_container = DIContainer::new(); + + let mut mock_provider = MockIProvider::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 + .binding_storage + .set::<dyn subjects::INumber>(BindingOptions::new(), Box::new(mock_provider)); + + let first_number_rc = di_container + .get::<dyn subjects::INumber>() + .unwrap() + .singleton() + .unwrap(); + + assert_eq!(first_number_rc.get(), 2820); + + let second_number_rc = di_container + .get::<dyn subjects::INumber>() + .unwrap() + .singleton() + .unwrap(); + + assert_eq!(first_number_rc.as_ref(), second_number_rc.as_ref()); + } + + #[test] + fn can_get_singleton_named() + { + let mut di_container = DIContainer::new(); + + let mut mock_provider = MockIProvider::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.binding_storage.set::<dyn subjects::INumber>( + BindingOptions::new().name("cool"), + Box::new(mock_provider), + ); + + let first_number_rc = di_container + .get_named::<dyn subjects::INumber>("cool") + .unwrap() + .singleton() + .unwrap(); + + assert_eq!(first_number_rc.get(), 2820); + + let second_number_rc = di_container + .get_named::<dyn subjects::INumber>("cool") + .unwrap() + .singleton() + .unwrap(); + + assert_eq!(first_number_rc.as_ref(), second_number_rc.as_ref()); + } + + #[test] + #[cfg(feature = "factory")] + fn can_get_factory() + { + use crate::castable_function::CastableFunction; + 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 IUserManagerFactory = dyn Fn(Vec<i128>) -> TransientPtr<dyn IUserManager>; + + let mut di_container = DIContainer::new(); + + let factory_func: &dyn Fn(&DIContainer) -> Box<IUserManagerFactory> = &|_| { + Box::new(move |users| { + let user_manager: TransientPtr<dyn IUserManager> = + TransientPtr::new(UserManager::new(users)); + + user_manager + }) + }; + + let mut mock_provider = MockIProvider::new(); + + mock_provider.expect_provide().returning_st(|_, _| { + Ok(Providable::Factory(FactoryPtr::new(CastableFunction::new( + factory_func, + )))) + }); + + di_container + .binding_storage + .set::<IUserManagerFactory>(BindingOptions::new(), Box::new(mock_provider)); + + di_container + .get::<IUserManagerFactory>() + .unwrap() + .factory() + .unwrap(); + } + + #[test] + #[cfg(feature = "factory")] + fn can_get_factory_named() + { + use crate::castable_function::CastableFunction; + 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 IUserManagerFactory = dyn Fn(Vec<i128>) -> TransientPtr<dyn IUserManager>; + + let mut di_container = DIContainer::new(); + + let factory_func: &dyn Fn(&DIContainer) -> Box<IUserManagerFactory> = &|_| { + Box::new(move |users| { + let user_manager: TransientPtr<dyn IUserManager> = + TransientPtr::new(UserManager::new(users)); + + user_manager + }) + }; + + let mut mock_provider = MockIProvider::new(); + + mock_provider.expect_provide().returning_st(|_, _| { + Ok(Providable::Factory(FactoryPtr::new(CastableFunction::new( + factory_func, + )))) + }); + + di_container.binding_storage.set::<IUserManagerFactory>( + BindingOptions::new().name("special"), + Box::new(mock_provider), + ); + + di_container + .get_named::<IUserManagerFactory>("special") + .unwrap() + .factory() + .unwrap(); + } + + #[test] + fn has_binding_works() + { + let mut di_container = DIContainer::new(); + + // No binding is present yet + assert!(!di_container.has_binding::<subjects::Ninja>(BindingOptions::new())); + + di_container.binding_storage.set::<subjects::Ninja>( + BindingOptions::new(), + Box::new(MockIProvider::new()), + ); + + assert!(di_container.has_binding::<subjects::Ninja>(BindingOptions::new())); + } + + #[test] + fn set_binding_works() + { + let mut di_container = DIContainer::new(); + + di_container.set_binding::<subjects::Ninja>( + BindingOptions::new(), + Box::new(MockIProvider::new()), + ); + + assert!(di_container + .binding_storage + .has::<subjects::Ninja>(BindingOptions::new())); + } + + #[test] + fn remove_binding_works() + { + let mut di_container = DIContainer::new(); + + di_container.binding_storage.set::<subjects::Ninja>( + BindingOptions::new(), + Box::new(MockIProvider::new()), + ); + + assert!( + // Formatting is weird without this comment + di_container + .remove_binding::<subjects::Ninja>(BindingOptions::new()) + .is_some() + ); + + assert!( + // Formatting is weird without this comment + !di_container + .binding_storage + .has::<subjects::Ninja>(BindingOptions::new()) + ); + } +} |