use std::any::type_name;
use std::marker::PhantomData;
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::BindingOptions;
use crate::errors::di_container::BindingBuilderError;
use crate::interfaces::injectable::Injectable;
use crate::util::use_double;
use_double!(crate::dependency_history::DependencyHistory);
use_double!(crate::di_container::blocking::DIContainer);
#[must_use = "No binding will be created if you don't use the binding builder"]
pub struct BindingBuilder<'di_container, Interface>
where
Interface: 'static + ?Sized,
{
di_container: &'di_container DIContainer,
dependency_history_factory: fn() -> DependencyHistory,
interface_phantom: PhantomData<Interface>,
}
impl<'di_container, Interface> BindingBuilder<'di_container, Interface>
where
Interface: 'static + ?Sized,
{
pub(crate) fn new(
di_container: &'di_container DIContainer,
dependency_history_factory: fn() -> DependencyHistory,
) -> Self
{
Self {
di_container,
dependency_history_factory,
interface_phantom: PhantomData,
}
}
pub fn to<Implementation>(
self,
) -> Result<
BindingScopeConfigurator<'di_container, Interface, Implementation>,
BindingBuilderError,
>
where
Implementation: Injectable<DIContainer>,
{
if self
.di_container
.has_binding::<Interface>(BindingOptions::new())
{
return Err(BindingBuilderError::BindingAlreadyExists(type_name::<
Interface,
>()));
}
let binding_scope_configurator = BindingScopeConfigurator::new(
self.di_container,
self.dependency_history_factory,
);
binding_scope_configurator.set_in_transient_scope();
Ok(binding_scope_configurator)
}
#[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<'di_container, Interface>, BindingBuilderError>
where
Args: std::marker::Tuple + 'static,
Return: 'static + ?Sized,
Interface: Fn<Args, Output = crate::ptr::TransientPtr<Return>>,
Func: Fn(&DIContainer) -> Box<Interface>,
{
use crate::private::castable_factory::CastableFactory;
if self
.di_container
.has_binding::<Interface>(BindingOptions::new())
{
return Err(BindingBuilderError::BindingAlreadyExists(type_name::<
Interface,
>()));
}
let factory_impl = CastableFactory::new(factory_func);
self.di_container.set_binding::<Interface>(
BindingOptions::new(),
Box::new(crate::provider::blocking::FactoryProvider::new(
crate::ptr::FactoryPtr::new(factory_impl),
false,
)),
);
Ok(BindingWhenConfigurator::new(self.di_container))
}
#[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<'di_container, Interface>, BindingBuilderError>
where
Return: 'static + ?Sized,
FactoryFunc: Fn(
&DIContainer,
) -> crate::ptr::TransientPtr<
dyn Fn<(), Output = crate::ptr::TransientPtr<Return>>,
>,
{
use crate::private::castable_factory::CastableFactory;
if self
.di_container
.has_binding::<Interface>(BindingOptions::new())
{
return Err(BindingBuilderError::BindingAlreadyExists(type_name::<
Interface,
>()));
}
let factory_impl = CastableFactory::new(factory_func);
self.di_container.set_binding::<Interface>(
BindingOptions::new(),
Box::new(crate::provider::blocking::FactoryProvider::new(
crate::ptr::FactoryPtr::new(factory_impl),
true,
)),
);
Ok(BindingWhenConfigurator::new(self.di_container))
}
}
#[cfg(test)]
mod tests
{
use mockall::predicate::eq;
use super::*;
use crate::dependency_history::MockDependencyHistory;
use crate::di_container::blocking::MockDIContainer;
use crate::test_utils::subjects;
#[test]
fn can_bind_to()
{
let mut mock_di_container = MockDIContainer::new();
mock_di_container
.expect_has_binding::<dyn subjects::INumber>()
.with(eq(BindingOptions::new()))
.return_once(|_options| false)
.once();
mock_di_container
.expect_set_binding::<dyn subjects::INumber>()
.withf(|options, _provider| options.name.is_none())
.return_once(|_options, _provider| ())
.once();
let binding_builder = BindingBuilder::<dyn subjects::INumber>::new(
&mock_di_container,
MockDependencyHistory::new,
);
binding_builder.to::<subjects::Number>().unwrap();
}
#[test]
#[cfg(feature = "factory")]
fn can_bind_to_factory()
{
use crate as syrette;
use crate::factory;
use crate::ptr::TransientPtr;
#[factory]
type IUserManagerFactory =
dyn Fn(i32, String) -> TransientPtr<dyn subjects::IUserManager>;
let mut mock_di_container = MockDIContainer::new();
mock_di_container
.expect_has_binding::<IUserManagerFactory>()
.with(eq(BindingOptions::new()))
.return_once(|_| false)
.once();
mock_di_container
.expect_set_binding::<IUserManagerFactory>()
.withf(|options, _provider| options.name.is_none())
.return_once(|_, _provider| ())
.once();
let binding_builder = BindingBuilder::<IUserManagerFactory>::new(
&mock_di_container,
MockDependencyHistory::new,
);
binding_builder
.to_factory(&|_| {
Box::new(move |_num, _text| {
let user_manager: TransientPtr<dyn subjects::IUserManager> =
TransientPtr::new(subjects::UserManager::new());
user_manager
})
})
.unwrap();
}
#[test]
#[cfg(feature = "factory")]
fn can_bind_to_default_factory()
{
use syrette_macros::declare_default_factory;
use crate as syrette;
use crate::ptr::TransientPtr;
declare_default_factory!(dyn subjects::IUserManager);
let mut mock_di_container = MockDIContainer::new();
mock_di_container
.expect_has_binding::<dyn subjects::IUserManager>()
.with(eq(BindingOptions::new()))
.return_once(|_| false)
.once();
mock_di_container
.expect_set_binding::<dyn subjects::IUserManager>()
.withf(|options, _provider| options.name.is_none())
.return_once(|_, _provider| ())
.once();
let binding_builder = BindingBuilder::<dyn subjects::IUserManager>::new(
&mock_di_container,
MockDependencyHistory::new,
);
binding_builder
.to_default_factory(&|_| {
Box::new(move || {
let user_manager: TransientPtr<dyn subjects::IUserManager> =
TransientPtr::new(subjects::UserManager::new());
user_manager
})
})
.unwrap();
}
}