From b54dee1fb52f259de8b485d050d75c6956750b7f Mon Sep 17 00:00:00 2001 From: HampusM Date: Wed, 3 Aug 2022 14:13:55 +0200 Subject: feat!: prevent binding the same interface more than once BREAKING CHANGE: The 'to' and 'to_factory' methods of BindingBuilder now return 'Result' --- examples/basic/bootstrap.rs | 4 ++-- examples/factory/bootstrap.rs | 3 ++- src/di_container.rs | 52 +++++++++++++++++++++++++++++++++-------- src/di_container_binding_map.rs | 12 +++++++--- src/lib.rs | 2 +- 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::() .to_singleton::() .unwrap(); - di_container.bind::().to::(); - di_container.bind::().to::(); + di_container.bind::().to::().unwrap(); + di_container.bind::().to::().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(&mut self) + /// + /// # Errors + /// Will return Err if the associated [`DIContainer`] already have a binding for + /// the interface. + pub fn to(&mut self) -> error_stack::Result<(), BindingBuilderError> where Implementation: Injectable, { self.di_container .bindings - .set::(Box::new(TransientTypeProvider::::new())); + .set::(Box::new(TransientTypeProvider::::new())) + .ok_or_else(|| { + report!(BindingBuilderError).attach_printable(format!( + "Binding already exists for interface '{}'", + type_name::() + )) + })?; + + 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( &mut self, ) -> error_stack::Result<(), BindingBuilderError> @@ -119,7 +132,13 @@ where self.di_container .bindings - .set::(Box::new(SingletonProvider::new(singleton))); + .set::(Box::new(SingletonProvider::new(singleton))) + .ok_or_else(|| { + report!(BindingBuilderError).attach_printable(format!( + "Binding already exists for interface '{}'", + type_name::() + )) + })?; 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( &mut self, factory_func: &'static dyn Fn>, - ) where + ) -> error_stack::Result<(), BindingBuilderError> + where Args: 'static, Return: 'static + ?Sized, Interface: crate::interfaces::factory::IFactory, { let factory_impl = CastableFactory::new(factory_func); - self.di_container.bindings.set::(Box::new( - crate::provider::FactoryProvider::new(crate::ptr::FactoryPtr::new( - factory_impl, - )), - )); + self.di_container + .bindings + .set::(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::() + )) + })?; + + 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::() )) })? .as_ref()) } - pub fn set(&mut self, provider: Box) + pub fn set(&mut self, provider: Box) -> Option<()> where Interface: 'static + ?Sized, { let interface_typeid = TypeId::of::(); + 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::().to::<$implementation>(); + $di_container.bind::().to::<$implementation>().unwrap(); syrette::declare_interface!($implementation -> $interface); }; -- cgit v1.2.3-18-g5258