aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/basic/bootstrap.rs15
-rw-r--r--examples/basic/main.rs12
-rw-r--r--examples/unbound/bootstrap.rs15
-rw-r--r--examples/unbound/main.rs4
-rw-r--r--src/di_container.rs150
-rw-r--r--src/di_container_binding_map.rs15
-rw-r--r--src/errors/di_container.rs7
7 files changed, 143 insertions, 75 deletions
diff --git a/examples/basic/bootstrap.rs b/examples/basic/bootstrap.rs
index 4af2d82..4e02dc3 100644
--- a/examples/basic/bootstrap.rs
+++ b/examples/basic/bootstrap.rs
@@ -1,3 +1,5 @@
+use std::error::Error;
+
use syrette::DIContainer;
// Concrete implementations
@@ -10,16 +12,17 @@ use crate::interfaces::cat::ICat;
use crate::interfaces::dog::IDog;
use crate::interfaces::human::IHuman;
-pub fn bootstrap() -> DIContainer
+pub fn bootstrap() -> Result<DIContainer, Box<dyn Error>>
{
let mut di_container: DIContainer = DIContainer::new();
di_container
.bind::<dyn IDog>()
- .to_singleton::<Dog>()
- .unwrap();
- di_container.bind::<dyn ICat>().to::<Cat>().unwrap();
- di_container.bind::<dyn IHuman>().to::<Human>().unwrap();
+ .to::<Dog>()?
+ .in_singleton_scope()?;
- di_container
+ di_container.bind::<dyn ICat>().to::<Cat>()?;
+ di_container.bind::<dyn IHuman>().to::<Human>()?;
+
+ Ok(di_container)
}
diff --git a/examples/basic/main.rs b/examples/basic/main.rs
index 3a937c3..72f07c2 100644
--- a/examples/basic/main.rs
+++ b/examples/basic/main.rs
@@ -2,6 +2,8 @@
#![deny(clippy::pedantic)]
#![allow(clippy::module_name_repetitions)]
+use std::error::Error;
+
mod animals;
mod bootstrap;
mod interfaces;
@@ -10,17 +12,19 @@ use bootstrap::bootstrap;
use interfaces::dog::IDog;
use interfaces::human::IHuman;
-fn main()
+fn main() -> Result<(), Box<dyn Error>>
{
println!("Hello, world!");
- let di_container = bootstrap();
+ let di_container = bootstrap()?;
- let dog = di_container.get_singleton::<dyn IDog>().unwrap();
+ let dog = di_container.get_singleton::<dyn IDog>()?;
dog.woof();
- let human = di_container.get::<dyn IHuman>().unwrap();
+ let human = di_container.get::<dyn IHuman>()?;
human.make_pets_make_sounds();
+
+ Ok(())
}
diff --git a/examples/unbound/bootstrap.rs b/examples/unbound/bootstrap.rs
index dc8468c..f59bc83 100644
--- a/examples/unbound/bootstrap.rs
+++ b/examples/unbound/bootstrap.rs
@@ -1,3 +1,5 @@
+use std::error::Error;
+
use syrette::DIContainer;
// Concrete implementations
@@ -10,21 +12,20 @@ use crate::interfaces::animal_store::IAnimalStore;
use crate::interfaces::dog::IDog;
use crate::interfaces::human::IHuman;
-pub fn bootstrap() -> DIContainer
+pub fn bootstrap() -> Result<DIContainer, Box<dyn Error>>
{
let mut di_container: DIContainer = DIContainer::new();
di_container
.bind::<dyn IDog>()
- .to_singleton::<Dog>()
- .unwrap();
+ .to::<Dog>()?
+ .in_singleton_scope()?;
- di_container.bind::<dyn IHuman>().to::<Human>().unwrap();
+ di_container.bind::<dyn IHuman>().to::<Human>()?;
di_container
.bind::<dyn IAnimalStore>()
- .to::<AnimalStore>()
- .unwrap();
+ .to::<AnimalStore>()?;
- di_container
+ Ok(di_container)
}
diff --git a/examples/unbound/main.rs b/examples/unbound/main.rs
index 47629e4..031a691 100644
--- a/examples/unbound/main.rs
+++ b/examples/unbound/main.rs
@@ -17,9 +17,9 @@ fn main() -> Result<(), Box<dyn Error>>
{
println!("Hello, world!");
- let di_container = bootstrap();
+ let di_container = bootstrap()?;
- let dog = di_container.get_singleton::<dyn IDog>().unwrap();
+ let dog = di_container.get_singleton::<dyn IDog>()?;
dog.woof();
diff --git a/src/di_container.rs b/src/di_container.rs
index c698fac..cc2a930 100644
--- a/src/di_container.rs
+++ b/src/di_container.rs
@@ -53,77 +53,118 @@ use std::marker::PhantomData;
#[cfg(feature = "factory")]
use crate::castable_factory::CastableFactory;
use crate::di_container_binding_map::DIContainerBindingMap;
-use crate::errors::di_container::{BindingBuilderError, DIContainerError};
+use crate::errors::di_container::{
+ BindingBuilderError, BindingScopeConfiguratorError, DIContainerError,
+};
use crate::interfaces::injectable::Injectable;
use crate::libs::intertrait::cast::{CastBox, CastRc};
use crate::provider::{Providable, SingletonProvider, TransientTypeProvider};
use crate::ptr::{SingletonPtr, TransientPtr};
-/// Binding builder for type `Interface` inside a [`DIContainer`].
-pub struct BindingBuilder<'di_container_lt, Interface>
+/// Scope configurator for a binding for type 'Interface' inside a [`DIContainer`].
+pub struct BindingScopeConfigurator<'di_container, Interface, Implementation>
where
Interface: 'static + ?Sized,
+ Implementation: Injectable,
{
- di_container: &'di_container_lt mut DIContainer,
+ di_container: &'di_container mut DIContainer,
interface_phantom: PhantomData<Interface>,
+ implementation_phantom: PhantomData<Implementation>,
}
-impl<'di_container_lt, Interface> BindingBuilder<'di_container_lt, Interface>
+impl<'di_container, Interface, Implementation>
+ BindingScopeConfigurator<'di_container, Interface, Implementation>
where
Interface: 'static + ?Sized,
+ Implementation: Injectable,
{
- fn new(di_container: &'di_container_lt mut DIContainer) -> Self
+ fn new(di_container: &'di_container mut DIContainer) -> Self
{
Self {
di_container,
interface_phantom: PhantomData,
+ implementation_phantom: PhantomData,
}
}
- /// Creates a binding of type `Interface` to type `Implementation` inside of the
- /// associated [`DIContainer`].
+ /// Configures the binding to be in a transient scope.
///
- /// # Errors
- /// Will return Err if the associated [`DIContainer`] already have a binding for
- /// the interface.
- pub fn to<Implementation>(&mut self) -> Result<(), BindingBuilderError>
- where
- Implementation: Injectable,
+ /// This is the default.
+ pub fn in_transient_scope(&mut self)
{
self.di_container
.bindings
- .set::<Interface>(Box::new(TransientTypeProvider::<Implementation>::new()))
- .ok_or_else(|| {
- BindingBuilderError::BindingAlreadyExists(type_name::<Interface>())
- })?;
-
- Ok(())
+ .set::<Interface>(Box::new(TransientTypeProvider::<Implementation>::new()));
}
- /// Creates a binding of type `Interface` to a new singleton of type `Implementation`
- /// inside of the associated [`DIContainer`].
+ /// Configures the binding to be in a singleton scope.
///
/// # Errors
- /// Will return Err if creating the singleton fails or if the
- /// associated [`DIContainer`] already have a binding for the interface.
- pub fn to_singleton<Implementation>(&mut self) -> Result<(), BindingBuilderError>
- where
- Implementation: Injectable,
+ /// Will return Err if resolving the implementation fails.
+ pub fn in_singleton_scope(&mut self) -> Result<(), BindingScopeConfiguratorError>
{
let singleton: SingletonPtr<Implementation> = SingletonPtr::from(
Implementation::resolve(self.di_container, Vec::new())
- .map_err(BindingBuilderError::SingletonResolveFailed)?,
+ .map_err(BindingScopeConfiguratorError::SingletonResolveFailed)?,
);
self.di_container
.bindings
- .set::<Interface>(Box::new(SingletonProvider::new(singleton)))
- .ok_or_else(|| {
- BindingBuilderError::BindingAlreadyExists(type_name::<Interface>())
- })?;
+ .set::<Interface>(Box::new(SingletonProvider::new(singleton)));
Ok(())
}
+}
+
+/// Binding builder for type `Interface` inside a [`DIContainer`].
+pub struct BindingBuilder<'di_container, Interface>
+where
+ Interface: 'static + ?Sized,
+{
+ di_container: &'di_container mut DIContainer,
+ interface_phantom: PhantomData<Interface>,
+}
+
+impl<'di_container, Interface> BindingBuilder<'di_container, Interface>
+where
+ Interface: 'static + ?Sized,
+{
+ fn new(di_container: &'di_container mut DIContainer) -> Self
+ {
+ Self {
+ di_container,
+ interface_phantom: PhantomData,
+ }
+ }
+
+ /// Creates a binding of type `Interface` to type `Implementation` inside of the
+ /// associated [`DIContainer`].
+ ///
+ /// The scope of the binding is transient. But that can be changed by using the
+ /// returned [`BindingScopeConfigurator`]
+ ///
+ /// # Errors
+ /// Will return Err if the associated [`DIContainer`] already have a binding for
+ /// the interface.
+ pub fn to<Implementation>(
+ &mut self,
+ ) -> Result<BindingScopeConfigurator<Interface, Implementation>, BindingBuilderError>
+ where
+ Implementation: Injectable,
+ {
+ if self.di_container.bindings.has::<Interface>() {
+ return Err(BindingBuilderError::BindingAlreadyExists(type_name::<
+ Interface,
+ >()));
+ }
+
+ let mut binding_scope_configurator =
+ BindingScopeConfigurator::new(self.di_container);
+
+ binding_scope_configurator.in_transient_scope();
+
+ Ok(binding_scope_configurator)
+ }
/// Creates a binding of factory type `Interface` to a factory inside of the
/// associated [`DIContainer`].
@@ -143,16 +184,19 @@ where
Return: 'static + ?Sized,
Interface: crate::interfaces::factory::IFactory<Args, Return>,
{
+ if self.di_container.bindings.has::<Interface>() {
+ return Err(BindingBuilderError::BindingAlreadyExists(type_name::<
+ Interface,
+ >()));
+ }
+
let factory_impl = CastableFactory::new(factory_func);
- self.di_container
- .bindings
- .set::<Interface>(Box::new(crate::provider::FactoryProvider::new(
- crate::ptr::FactoryPtr::new(factory_impl),
- )))
- .ok_or_else(|| {
- BindingBuilderError::BindingAlreadyExists(type_name::<Interface>())
- })?;
+ self.di_container.bindings.set::<Interface>(Box::new(
+ crate::provider::FactoryProvider::new(crate::ptr::FactoryPtr::new(
+ factory_impl,
+ )),
+ ));
Ok(())
}
@@ -173,16 +217,19 @@ where
where
Return: 'static + ?Sized,
{
+ if self.di_container.bindings.has::<Interface>() {
+ return Err(BindingBuilderError::BindingAlreadyExists(type_name::<
+ Interface,
+ >()));
+ }
+
let factory_impl = CastableFactory::new(factory_func);
- self.di_container
- .bindings
- .set::<Interface>(Box::new(crate::provider::FactoryProvider::new(
- crate::ptr::FactoryPtr::new(factory_impl),
- )))
- .ok_or_else(|| {
- BindingBuilderError::BindingAlreadyExists(type_name::<Interface>())
- })?;
+ self.di_container.bindings.set::<Interface>(Box::new(
+ crate::provider::FactoryProvider::new(crate::ptr::FactoryPtr::new(
+ factory_impl,
+ )),
+ ));
Ok(())
}
@@ -364,6 +411,8 @@ impl Default for DIContainer
#[cfg(test)]
mod tests
{
+ use std::error::Error;
+
use mockall::mock;
use super::*;
@@ -512,7 +561,7 @@ mod tests
}
#[test]
- fn can_bind_to_singleton() -> Result<(), BindingBuilderError>
+ fn can_bind_to_singleton() -> Result<(), Box<dyn Error>>
{
let mut di_container: DIContainer = DIContainer::new();
@@ -520,7 +569,8 @@ mod tests
di_container
.bind::<dyn subjects::IUserManager>()
- .to_singleton::<subjects::UserManager>()?;
+ .to::<subjects::UserManager>()?
+ .in_singleton_scope()?;
assert_eq!(di_container.bindings.count(), 1);
diff --git a/src/di_container_binding_map.rs b/src/di_container_binding_map.rs
index e64ff17..20d040f 100644
--- a/src/di_container_binding_map.rs
+++ b/src/di_container_binding_map.rs
@@ -31,19 +31,22 @@ impl DIContainerBindingMap
.as_ref())
}
- pub fn set<Interface>(&mut self, provider: Box<dyn IProvider>) -> Option<()>
+ pub fn set<Interface>(&mut self, provider: Box<dyn IProvider>)
where
Interface: 'static + ?Sized,
{
let interface_typeid = TypeId::of::<Interface>();
- if self.bindings.contains_key(&interface_typeid) {
- return None;
- }
-
self.bindings.insert(interface_typeid, provider);
+ }
+
+ pub fn has<Interface>(&self) -> bool
+ where
+ Interface: 'static + ?Sized,
+ {
+ let interface_typeid = TypeId::of::<Interface>();
- Some(())
+ self.bindings.contains_key(&interface_typeid)
}
/// Only used by tests in the `di_container` module.
diff --git a/src/errors/di_container.rs b/src/errors/di_container.rs
index ed05a5e..98c2be4 100644
--- a/src/errors/di_container.rs
+++ b/src/errors/di_container.rs
@@ -54,7 +54,14 @@ pub enum BindingBuilderError
/// A binding already exists for a interface.
#[error("Binding already exists for interface '{0}'")]
BindingAlreadyExists(&'static str),
+}
+/// Error type for [`BindingScopeConfigurator`].
+///
+/// [`BindingBuilder`]: crate::di_container::BindingScopeConfigurator
+#[derive(thiserror::Error, Debug)]
+pub enum BindingScopeConfiguratorError
+{
/// Resolving a singleton failed.
#[error("Resolving the given singleton failed")]
SingletonResolveFailed(#[from] InjectableError),