From b31422d48a600ccccb682567f5eb11fc0bca547c Mon Sep 17 00:00:00 2001 From: HampusM Date: Sat, 20 Aug 2022 17:08:58 +0200 Subject: feat: allow bind interface to default factory This commit will allow interface traits to be bound to default factories. Default factories being factories that doesn't take any arguments --- src/di_container.rs | 44 +++++++++++++++++++++++++++++++++++ src/lib.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) (limited to 'src') diff --git a/src/di_container.rs b/src/di_container.rs index eaa0366..84fb9e0 100644 --- a/src/di_container.rs +++ b/src/di_container.rs @@ -181,6 +181,39 @@ where Ok(()) } + + /// Creates a binding of type `Interface` to a factory that takes no arguments + /// inside of the 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_default_factory( + &mut self, + factory_func: &'static dyn Fn<(), Output = TransientPtr>, + ) -> error_stack::Result<(), BindingBuilderError> + where + Return: 'static + ?Sized, + { + let factory_impl = CastableFactory::new(factory_func); + + 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(()) + } } /// Dependency injection container. @@ -290,6 +323,17 @@ impl DIContainer .map_err(unable_to_cast_binding::); } + #[cfg(feature = "factory")] + if let Providable::Factory(binding_factory) = binding_providable { + use crate::interfaces::factory::IFactory; + + let factory = binding_factory + .cast::>() + .map_err(unable_to_cast_binding::)?; + + return Ok(factory()); + } + Err(report!(DIContainerError).attach_printable(format!( "Binding for interface '{}' is not transient", type_name::() diff --git a/src/lib.rs b/src/lib.rs index 89688f1..78506bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,3 +45,70 @@ macro_rules! di_container_bind { syrette::declare_interface!($implementation -> $interface); }; } + +/// Shortcut for declaring a default factory. +/// +/// A default factory is a factory that doesn't take any arguments. +/// +/// More tedious ways to accomplish what this macro does would either be by using +/// the [`factory`] macro or by manually declaring the interfaces +/// with the [`declare_interface`] macro. +/// +/// *This macro is only available if Syrette is built with the "factory" feature.* +/// +/// # Arguments +/// - Interface trait +/// +/// # Examples +/// ``` +/// use syrette::declare_default_factory; +/// +/// trait IParser { +/// // Methods and etc here... +/// } +/// +/// declare_default_factory!(IParser); +/// ``` +/// +/// The expanded equivelent of this would be +/// +/// ``` +/// use syrette::declare_default_factory; +/// +/// trait IParser { +/// // Methods and etc here... +/// } +/// +/// syrette::declare_interface!( +/// syrette::castable_factory::CastableFactory< +/// (), +/// dyn IParser, +/// > -> syrette::interfaces::factory::IFactory<(), dyn IParser> +/// ); +/// +/// syrette::declare_interface!( +/// syrette::castable_factory::CastableFactory< +/// (), +/// dyn IParser, +/// > -> syrette::interfaces::any_factory::AnyFactory +/// ); +/// ``` +#[macro_export] +#[cfg(feature = "factory")] +macro_rules! declare_default_factory { + ($interface: path) => { + syrette::declare_interface!( + syrette::castable_factory::CastableFactory< + (), + dyn $interface, + > -> syrette::interfaces::factory::IFactory<(), dyn $interface> + ); + + syrette::declare_interface!( + syrette::castable_factory::CastableFactory< + (), + dyn $interface, + > -> syrette::interfaces::any_factory::AnyFactory + ); + } +} -- cgit v1.2.3-18-g5258