use std::any::type_name;
use std::cell::RefCell;
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>;
#[derive(Default)]
pub struct DIContainer
{
binding_storage: RefCell<DIContainerBindingStorage<dyn IProvider<Self>>>,
}
impl DIContainer
{
#[must_use]
pub fn new() -> Self
{
Self {
binding_storage: RefCell::new(DIContainerBindingStorage::new()),
}
}
}
#[cfg_attr(test, mockall::automock)]
impl DIContainer
{
#[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)
}
pub fn get<Interface>(&self) -> Result<SomePtr<Interface>, DIContainerError>
where
Interface: 'static + ?Sized,
{
self.get_bound::<Interface>(DependencyHistory::new(), BindingOptions::new())
}
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),
)
}
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::private::factory::IFactory;
let factory = factory_binding
.cast::<dyn IFactory<Interface, Self>>()
.map_err(|_| 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::private::factory::IFactory;
use crate::ptr::TransientPtr;
type DefaultFactoryFn<Interface> = dyn IFactory<
dyn Fn<(), Output = TransientPtr<Interface>>,
DIContainer,
>;
let default_factory = factory_binding
.cast::<DefaultFactoryFn<Interface>>()
.map_err(|_| 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
.borrow()
.has::<Interface>(binding_options)
}
fn set_binding<Interface>(
&self,
binding_options: BindingOptions<'static>,
provider: Box<dyn IProvider<Self>>,
) where
Interface: 'static + ?Sized,
{
self.binding_storage
.borrow_mut()
.set::<Interface>(binding_options, provider);
}
fn remove_binding<Interface>(
&self,
binding_options: BindingOptions<'static>,
) -> Option<Box<dyn IProvider<Self>>>
where
Interface: 'static + ?Sized,
{
self.binding_storage
.borrow_mut()
.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
.borrow()
.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 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
.borrow_mut()
.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 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
.borrow_mut()
.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 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
.borrow_mut()
.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 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
.borrow_mut()
.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::private::castable_factory::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);
}
}
use crate as syrette;
#[crate::factory]
type IUserManagerFactory = dyn Fn(Vec<i128>) -> TransientPtr<dyn IUserManager>;
let 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(CastableFactory::new(
factory_func,
))))
});
di_container
.binding_storage
.borrow_mut()
.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::private::castable_factory::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);
}
}
use crate as syrette;
#[crate::factory]
type IUserManagerFactory = dyn Fn(Vec<i128>) -> TransientPtr<dyn IUserManager>;
let 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(CastableFactory::new(
factory_func,
))))
});
di_container
.binding_storage
.borrow_mut()
.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 di_container = DIContainer::new();
assert!(!di_container.has_binding::<subjects::Ninja>(BindingOptions::new()));
di_container
.binding_storage
.borrow_mut()
.set::<subjects::Ninja>(
BindingOptions::new(),
Box::new(MockIProvider::new()),
);
assert!(di_container.has_binding::<subjects::Ninja>(BindingOptions::new()));
}
#[test]
fn set_binding_works()
{
let di_container = DIContainer::new();
di_container.set_binding::<subjects::Ninja>(
BindingOptions::new(),
Box::new(MockIProvider::new()),
);
assert!(di_container
.binding_storage
.borrow_mut()
.has::<subjects::Ninja>(BindingOptions::new()));
}
#[test]
fn remove_binding_works()
{
let di_container = DIContainer::new();
di_container
.binding_storage
.borrow_mut()
.set::<subjects::Ninja>(
BindingOptions::new(),
Box::new(MockIProvider::new()),
);
assert!(
di_container
.remove_binding::<subjects::Ninja>(BindingOptions::new())
.is_some()
);
assert!(
!di_container
.binding_storage
.borrow_mut()
.has::<subjects::Ninja>(BindingOptions::new())
);
}
}