use std::marker::PhantomData;
use crate::errors::injectable::InjectableError;
use crate::interfaces::injectable::Injectable;
use crate::ptr::{SingletonPtr, TransientPtr};
use crate::util::use_double;
use_double!(crate::dependency_history::DependencyHistory);
#[derive(strum_macros::Display, Debug)]
pub enum Providable<DIContainerType>
{
Transient(TransientPtr<dyn Injectable<DIContainerType>>),
Singleton(SingletonPtr<dyn Injectable<DIContainerType>>),
#[cfg(feature = "factory")]
Factory(crate::ptr::FactoryPtr<dyn crate::private::any_factory::AnyFactory>),
#[cfg(feature = "factory")]
DefaultFactory(crate::ptr::FactoryPtr<dyn crate::private::any_factory::AnyFactory>),
}
#[cfg_attr(test, mockall::automock)]
pub trait IProvider<DIContainerType>
{
fn provide(
&self,
di_container: &DIContainerType,
dependency_history: DependencyHistory,
) -> Result<Providable<DIContainerType>, InjectableError>;
}
pub struct TransientTypeProvider<InjectableType, DIContainerType>
where
InjectableType: Injectable<DIContainerType>,
{
injectable_phantom: PhantomData<InjectableType>,
di_container_phantom: PhantomData<DIContainerType>,
}
impl<InjectableType, DIContainerType>
TransientTypeProvider<InjectableType, DIContainerType>
where
InjectableType: Injectable<DIContainerType>,
{
pub fn new() -> Self
{
Self {
injectable_phantom: PhantomData,
di_container_phantom: PhantomData,
}
}
}
impl<InjectableType, DIContainerType> IProvider<DIContainerType>
for TransientTypeProvider<InjectableType, DIContainerType>
where
InjectableType: Injectable<DIContainerType>,
{
fn provide(
&self,
di_container: &DIContainerType,
dependency_history: DependencyHistory,
) -> Result<Providable<DIContainerType>, InjectableError>
{
Ok(Providable::Transient(InjectableType::resolve(
di_container,
dependency_history,
)?))
}
}
pub struct SingletonProvider<InjectableType, DIContainerType>
where
InjectableType: Injectable<DIContainerType>,
{
singleton: SingletonPtr<InjectableType>,
di_container_phantom: PhantomData<DIContainerType>,
}
impl<InjectableType, DIContainerType> SingletonProvider<InjectableType, DIContainerType>
where
InjectableType: Injectable<DIContainerType>,
{
pub fn new(singleton: SingletonPtr<InjectableType>) -> Self
{
Self {
singleton,
di_container_phantom: PhantomData,
}
}
}
impl<InjectableType, DIContainerType> IProvider<DIContainerType>
for SingletonProvider<InjectableType, DIContainerType>
where
InjectableType: Injectable<DIContainerType>,
{
fn provide(
&self,
_di_container: &DIContainerType,
_dependency_history: DependencyHistory,
) -> Result<Providable<DIContainerType>, InjectableError>
{
Ok(Providable::Singleton(self.singleton.clone()))
}
}
#[cfg(feature = "factory")]
pub struct FactoryProvider
{
factory: crate::ptr::FactoryPtr<dyn crate::private::any_factory::AnyFactory>,
is_default_factory: bool,
}
#[cfg(feature = "factory")]
impl FactoryProvider
{
pub fn new(
factory: crate::ptr::FactoryPtr<dyn crate::private::any_factory::AnyFactory>,
is_default_factory: bool,
) -> Self
{
Self {
factory,
is_default_factory,
}
}
}
#[cfg(feature = "factory")]
impl<DIContainerType> IProvider<DIContainerType> for FactoryProvider
{
fn provide(
&self,
_di_container: &DIContainerType,
_dependency_history: DependencyHistory,
) -> Result<Providable<DIContainerType>, InjectableError>
{
Ok(if self.is_default_factory {
Providable::DefaultFactory(self.factory.clone())
} else {
Providable::Factory(self.factory.clone())
})
}
}
#[cfg(test)]
mod tests
{
use super::*;
use crate::dependency_history::MockDependencyHistory;
use crate::di_container::blocking::MockDIContainer;
use crate::test_utils::subjects;
#[test]
fn transient_type_provider_works()
{
let transient_type_provider =
TransientTypeProvider::<subjects::UserManager, MockDIContainer>::new();
let di_container = MockDIContainer::new();
let dependency_history_mock = MockDependencyHistory::new();
assert!(
matches!(
transient_type_provider.provide(&di_container, dependency_history_mock),
Ok(Providable::Transient(_))
),
"The provided type is not transient"
);
}
#[test]
fn singleton_provider_works()
{
let singleton_provider =
SingletonProvider::<subjects::UserManager, MockDIContainer>::new(
SingletonPtr::new(subjects::UserManager {}),
);
let di_container = MockDIContainer::new();
assert!(
matches!(
singleton_provider
.provide(&di_container, MockDependencyHistory::new())
.unwrap(),
Providable::Singleton(_)
),
"The provided type is not a singleton"
);
}
#[test]
#[cfg(feature = "factory")]
fn factory_provider_works()
{
use crate::private::any_factory::AnyFactory;
use crate::ptr::FactoryPtr;
#[derive(Debug)]
struct FooFactory;
impl AnyFactory for FooFactory {}
let factory_provider = FactoryProvider::new(FactoryPtr::new(FooFactory), false);
let default_factory_provider =
FactoryProvider::new(FactoryPtr::new(FooFactory), true);
let di_container = MockDIContainer::new();
assert!(
matches!(
factory_provider.provide(&di_container, MockDependencyHistory::new()),
Ok(Providable::Factory(_))
),
"The provided type is not a factory"
);
assert!(
matches!(
default_factory_provider
.provide(&di_container, MockDependencyHistory::new()),
Ok(Providable::DefaultFactory(_))
),
"The provided type is not a default factory"
);
}
}