aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2022-08-03 14:13:55 +0200
committerHampusM <hampus@hampusmat.com>2022-08-21 18:17:17 +0200
commitb54dee1fb52f259de8b485d050d75c6956750b7f (patch)
treec2aa02de55da6c2d4c338982afee18ce42465160
parentc33cf02c9a6fffc6149fd7b59c63ad0d15d61432 (diff)
feat!: prevent binding the same interface more than once
BREAKING CHANGE: The 'to' and 'to_factory' methods of BindingBuilder now return 'Result'
-rw-r--r--examples/basic/bootstrap.rs4
-rw-r--r--examples/factory/bootstrap.rs3
-rw-r--r--src/di_container.rs52
-rw-r--r--src/di_container_binding_map.rs12
-rw-r--r--src/lib.rs2
5 files changed, 56 insertions, 17 deletions
diff --git a/examples/basic/bootstrap.rs b/examples/basic/bootstrap.rs
index 10a7041..4af2d82 100644
--- a/examples/basic/bootstrap.rs
+++ b/examples/basic/bootstrap.rs
@@ -18,8 +18,8 @@ pub fn bootstrap() -> DIContainer
.bind::<dyn IDog>()
.to_singleton::<Dog>()
.unwrap();
- di_container.bind::<dyn ICat>().to::<Cat>();
- di_container.bind::<dyn IHuman>().to::<Human>();
+ di_container.bind::<dyn ICat>().to::<Cat>().unwrap();
+ di_container.bind::<dyn IHuman>().to::<Human>().unwrap();
di_container
}
diff --git a/examples/factory/bootstrap.rs b/examples/factory/bootstrap.rs
index 1967c6a..a44ccfb 100644
--- a/examples/factory/bootstrap.rs
+++ b/examples/factory/bootstrap.rs
@@ -18,7 +18,8 @@ pub fn bootstrap() -> DIContainer
TransientPtr::new(User::new(name, date_of_birth, password));
user
- });
+ })
+ .unwrap();
di_container
}
diff --git a/src/di_container.rs b/src/di_container.rs
index e15d921..c33d339 100644
--- a/src/di_container.rs
+++ b/src/di_container.rs
@@ -92,20 +92,33 @@ where
/// Creates a binding of type `Interface` to type `Implementation` inside of the
/// associated [`DIContainer`].
- pub fn to<Implementation>(&mut self)
+ ///
+ /// # Errors
+ /// Will return Err if the associated [`DIContainer`] already have a binding for
+ /// the interface.
+ pub fn to<Implementation>(&mut self) -> error_stack::Result<(), BindingBuilderError>
where
Implementation: Injectable,
{
self.di_container
.bindings
- .set::<Interface>(Box::new(TransientTypeProvider::<Implementation>::new()));
+ .set::<Interface>(Box::new(TransientTypeProvider::<Implementation>::new()))
+ .ok_or_else(|| {
+ report!(BindingBuilderError).attach_printable(format!(
+ "Binding already exists for interface '{}'",
+ type_name::<Interface>()
+ ))
+ })?;
+
+ Ok(())
}
/// Creates a binding of type `Interface` to a new singleton of type `Implementation`
/// inside of the associated [`DIContainer`].
///
/// # Errors
- /// Will return Err if creating the singleton fails.
+ /// 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,
) -> error_stack::Result<(), BindingBuilderError>
@@ -119,7 +132,13 @@ where
self.di_container
.bindings
- .set::<Interface>(Box::new(SingletonProvider::new(singleton)));
+ .set::<Interface>(Box::new(SingletonProvider::new(singleton)))
+ .ok_or_else(|| {
+ report!(BindingBuilderError).attach_printable(format!(
+ "Binding already exists for interface '{}'",
+ type_name::<Interface>()
+ ))
+ })?;
Ok(())
}
@@ -128,22 +147,35 @@ where
/// associated [`DIContainer`].
///
/// *This function is only available if Syrette is built with the "factory" feature.*
+ ///
+ /// # Errors
+ /// Will return Err if the associated [`DIContainer`] already have a binding for
+ /// the interface.
#[cfg(feature = "factory")]
pub fn to_factory<Args, Return>(
&mut self,
factory_func: &'static dyn Fn<Args, Output = TransientPtr<Return>>,
- ) where
+ ) -> error_stack::Result<(), BindingBuilderError>
+ where
Args: 'static,
Return: 'static + ?Sized,
Interface: crate::interfaces::factory::IFactory<Args, Return>,
{
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,
- )),
- ));
+ self.di_container
+ .bindings
+ .set::<Interface>(Box::new(crate::provider::FactoryProvider::new(
+ crate::ptr::FactoryPtr::new(factory_impl),
+ )))
+ .ok_or_else(|| {
+ report!(BindingBuilderError).attach_printable(format!(
+ "Binding already exists for interface '{}'",
+ type_name::<Interface>()
+ ))
+ })?;
+
+ Ok(())
}
}
diff --git a/src/di_container_binding_map.rs b/src/di_container_binding_map.rs
index b505321..fee33b0 100644
--- a/src/di_container_binding_map.rs
+++ b/src/di_container_binding_map.rs
@@ -30,23 +30,29 @@ impl DIContainerBindingMap
.get(&interface_typeid)
.ok_or_else(|| {
report!(DIContainerError).attach_printable(format!(
- "No binding exists for {}",
+ "No binding exists for interface '{}'",
type_name::<Interface>()
))
})?
.as_ref())
}
- pub fn set<Interface>(&mut self, provider: Box<dyn IProvider>)
+ pub fn set<Interface>(&mut self, provider: Box<dyn IProvider>) -> Option<()>
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);
+
+ Some(())
}
- /// Only used by tests in the ``di_container`` module.
+ /// Only used by tests in the `di_container` module.
#[cfg(test)]
pub fn count(&self) -> usize
{
diff --git a/src/lib.rs b/src/lib.rs
index a03675b..89688f1 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -40,7 +40,7 @@ mod provider;
#[macro_export]
macro_rules! di_container_bind {
($interface: path => $implementation: ty, $di_container: ident) => {
- $di_container.bind::<dyn $interface>().to::<$implementation>();
+ $di_container.bind::<dyn $interface>().to::<$implementation>().unwrap();
syrette::declare_interface!($implementation -> $interface);
};