diff options
Diffstat (limited to 'syrette/src')
-rw-r--r-- | syrette/src/castable_factory.rs | 72 | ||||
-rw-r--r-- | syrette/src/di_container.rs | 132 | ||||
-rw-r--r-- | syrette/src/interfaces/factory.rs | 7 | ||||
-rw-r--r-- | syrette/src/interfaces/mod.rs | 1 | ||||
-rw-r--r-- | syrette/src/lib.rs | 3 | ||||
-rw-r--r-- | syrette/src/libs/intertrait/cast_rc.rs | 34 | ||||
-rw-r--r-- | syrette/src/libs/intertrait/mod.rs | 1 | ||||
-rw-r--r-- | syrette/src/provider.rs | 48 |
8 files changed, 261 insertions, 37 deletions
diff --git a/syrette/src/castable_factory.rs b/syrette/src/castable_factory.rs new file mode 100644 index 0000000..8713ec4 --- /dev/null +++ b/syrette/src/castable_factory.rs @@ -0,0 +1,72 @@ +use crate::interfaces::factory::IFactory; +use crate::libs::intertrait::CastFrom; + +pub trait AnyFactory: CastFrom {} + +pub struct CastableFactory<Args, Return> +where + Args: 'static, + Return: 'static + ?Sized, +{ + _func: &'static dyn Fn<Args, Output = Box<Return>>, +} + +impl<Args, Return> CastableFactory<Args, Return> +where + Args: 'static, + Return: 'static + ?Sized, +{ + pub fn new(func: &'static dyn Fn<Args, Output = Box<Return>>) -> Self + { + Self { _func: func } + } +} + +impl<Args, Return> IFactory<Args, Return> for CastableFactory<Args, Return> +where + Args: 'static, + Return: 'static + ?Sized, +{ +} + +impl<Args, Return> Fn<Args> for CastableFactory<Args, Return> +where + Args: 'static, + Return: 'static + ?Sized, +{ + extern "rust-call" fn call(&self, args: Args) -> Self::Output + { + self._func.call(args) + } +} + +impl<Args, Return> FnMut<Args> for CastableFactory<Args, Return> +where + Args: 'static, + Return: 'static + ?Sized, +{ + extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output + { + self.call(args) + } +} + +impl<Args, Return> FnOnce<Args> for CastableFactory<Args, Return> +where + Args: 'static, + Return: 'static + ?Sized, +{ + type Output = Box<Return>; + + extern "rust-call" fn call_once(self, args: Args) -> Self::Output + { + self.call(args) + } +} + +impl<Args, Return> AnyFactory for CastableFactory<Args, Return> +where + Args: 'static, + Return: 'static + ?Sized, +{ +} diff --git a/syrette/src/di_container.rs b/syrette/src/di_container.rs index 32d53f2..53c4287 100644 --- a/syrette/src/di_container.rs +++ b/syrette/src/di_container.rs @@ -5,23 +5,26 @@ use std::rc::Rc; use error_stack::{Report, ResultExt}; +use crate::castable_factory::CastableFactory; use crate::errors::di_container::DIContainerError; +use crate::interfaces::factory::IFactory; use crate::interfaces::injectable::Injectable; use crate::libs::intertrait::cast_box::CastBox; -use crate::provider::{IInjectableTypeProvider, InjectableTypeProvider}; +use crate::libs::intertrait::cast_rc::CastRc; +use crate::provider::{FactoryProvider, IProvider, InjectableTypeProvider, Providable}; -/// Binding builder for `InterfaceTrait` in a [`DIContainer`]. -pub struct BindingBuilder<'a, InterfaceTrait> +/// Binding builder for type `Interface` inside a [`DIContainer`]. +pub struct BindingBuilder<'a, Interface> where - InterfaceTrait: 'static + ?Sized, + Interface: 'static + ?Sized, { _di_container: &'a mut DIContainer, - _phantom_data: PhantomData<InterfaceTrait>, + _phantom_data: PhantomData<Interface>, } -impl<'a, InterfaceTrait> BindingBuilder<'a, InterfaceTrait> +impl<'a, Interface> BindingBuilder<'a, Interface> where - InterfaceTrait: 'static + ?Sized, + Interface: 'static + ?Sized, { fn new(di_container: &'a mut DIContainer) -> Self { @@ -31,19 +34,39 @@ where } } - /// Creates a binding of `InterfaceTrait` to type `Implementation` inside of the + /// Creates a binding of type `Interface` to type `Implementation` inside of the /// associated [`DIContainer`]. pub fn to<Implementation>(&mut self) where Implementation: Injectable, { - let interface_typeid = TypeId::of::<InterfaceTrait>(); + let interface_typeid = TypeId::of::<Interface>(); self._di_container._bindings.insert( interface_typeid, Rc::new(InjectableTypeProvider::<Implementation>::new()), ); } + + /// Creates a binding of factory type `Interface` to a factory inside of the + /// associated [`DIContainer`]. + pub fn to_factory<Args, Return>( + &mut self, + factory_func: &'static dyn Fn<Args, Output = Box<Return>>, + ) where + Args: 'static, + Return: 'static + ?Sized, + Interface: IFactory<Args, Return>, + { + let interface_typeid = TypeId::of::<Interface>(); + + let factory_impl = CastableFactory::new(factory_func); + + self._di_container._bindings.insert( + interface_typeid, + Rc::new(FactoryProvider::new(Rc::new(factory_impl))), + ); + } } /// Dependency injection container. @@ -56,7 +79,7 @@ where /// ``` pub struct DIContainer { - _bindings: HashMap<TypeId, Rc<dyn IInjectableTypeProvider>>, + _bindings: HashMap<TypeId, Rc<dyn IProvider>>, } impl<'a> DIContainer @@ -69,46 +92,95 @@ impl<'a> DIContainer } } - /// Returns a new [`BindingBuilder`] for the given interface trait. - pub fn bind<InterfaceTrait>(&'a mut self) -> BindingBuilder<InterfaceTrait> + /// Returns a new [`BindingBuilder`] for the given interface. + pub fn bind<Interface>(&'a mut self) -> BindingBuilder<Interface> where - InterfaceTrait: 'static + ?Sized, + Interface: 'static + ?Sized, { - BindingBuilder::<InterfaceTrait>::new(self) + BindingBuilder::<Interface>::new(self) } - /// Returns the value bound with `InterfaceTrait`. - pub fn get<InterfaceTrait>( - &self, - ) -> error_stack::Result<Box<InterfaceTrait>, DIContainerError> + /// Returns a new instance of the type bound with `Interface`. + pub fn get<Interface>(&self) -> error_stack::Result<Box<Interface>, DIContainerError> where - InterfaceTrait: 'static + ?Sized, + Interface: 'static + ?Sized, { - let interface_typeid = TypeId::of::<InterfaceTrait>(); + let interface_typeid = TypeId::of::<Interface>(); - let interface_name = type_name::<InterfaceTrait>(); + let interface_name = type_name::<Interface>(); let binding = self._bindings.get(&interface_typeid).ok_or_else(|| { Report::new(DIContainerError) .attach_printable(format!("No binding exists for {}", interface_name)) })?; - let binding_injectable = binding + let binding_providable = binding .provide(self) .change_context(DIContainerError) .attach_printable(format!( - "Failed to resolve interface {}", + "Failed to resolve binding for interface {}", interface_name ))?; - let interface_box_result = binding_injectable.cast::<InterfaceTrait>(); + match binding_providable { + Providable::Injectable(binding_injectable) => { + let interface_box_result = binding_injectable.cast::<Interface>(); + + match interface_box_result { + Ok(interface_box) => Ok(interface_box), + Err(_) => Err(Report::new(DIContainerError).attach_printable( + format!("Unable to cast binding for {}", interface_name), + )), + } + } + Providable::Factory(_) => Err(Report::new(DIContainerError) + .attach_printable(format!( + "Binding for {} is not injectable", + interface_name + ))), + } + } + + /// Returns the factory bound with factory type `Interface`. + pub fn get_factory<Interface>( + &self, + ) -> error_stack::Result<Rc<Interface>, DIContainerError> + where + Interface: 'static + ?Sized, + { + let interface_typeid = TypeId::of::<Interface>(); + + let interface_name = type_name::<Interface>(); + + let binding = self._bindings.get(&interface_typeid).ok_or_else(|| { + Report::new(DIContainerError) + .attach_printable(format!("No binding exists for {}", interface_name)) + })?; + + let binding_providable = binding + .provide(self) + .change_context(DIContainerError) + .attach_printable(format!( + "Failed to resolve binding for interface {}", + interface_name + ))?; - match interface_box_result { - Ok(interface_box) => Ok(interface_box), - Err(_) => Err(Report::new(DIContainerError).attach_printable(format!( - "Unable to cast binding for {}", - interface_name - ))), + match binding_providable { + Providable::Factory(binding_factory) => { + let factory_box_result = binding_factory.cast::<Interface>(); + + match factory_box_result { + Ok(interface_box) => Ok(interface_box), + Err(_) => Err(Report::new(DIContainerError).attach_printable( + format!("Unable to cast binding for {}", interface_name), + )), + } + } + Providable::Injectable(_) => Err(Report::new(DIContainerError) + .attach_printable(format!( + "Binding for {} is not a factory", + interface_name + ))), } } } diff --git a/syrette/src/interfaces/factory.rs b/syrette/src/interfaces/factory.rs new file mode 100644 index 0000000..ed03cce --- /dev/null +++ b/syrette/src/interfaces/factory.rs @@ -0,0 +1,7 @@ +use crate::libs::intertrait::CastFrom; + +pub trait IFactory<Args, Return>: Fn<Args, Output = Box<Return>> + CastFrom +where + Return: 'static + ?Sized, +{ +} diff --git a/syrette/src/interfaces/mod.rs b/syrette/src/interfaces/mod.rs index 31e53af..921bb9c 100644 --- a/syrette/src/interfaces/mod.rs +++ b/syrette/src/interfaces/mod.rs @@ -1 +1,2 @@ +pub mod factory; pub mod injectable; diff --git a/syrette/src/lib.rs b/syrette/src/lib.rs index 7278c37..945c0c0 100644 --- a/syrette/src/lib.rs +++ b/syrette/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(unboxed_closures, fn_traits)] + //! Syrette //! //! Syrette is a collection of utilities useful for performing dependency injection. @@ -115,6 +117,7 @@ //! //! ``` +pub mod castable_factory; pub mod di_container; pub mod errors; pub mod interfaces; diff --git a/syrette/src/libs/intertrait/cast_rc.rs b/syrette/src/libs/intertrait/cast_rc.rs new file mode 100644 index 0000000..58d212a --- /dev/null +++ b/syrette/src/libs/intertrait/cast_rc.rs @@ -0,0 +1,34 @@ +/** + * Originally from Intertrait by CodeChain + * + * https://github.com/CodeChain-io/intertrait + * https://crates.io/crates/intertrait/0.2.2 + * + * Licensed under either of + * + * Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) + + * at your option. +*/ +use std::rc::Rc; + +use crate::libs::intertrait::{caster, CastFrom}; + +pub trait CastRc +{ + /// Casts an `Rc` for this trait into that for type `T`. + fn cast<T: ?Sized + 'static>(self: Rc<Self>) -> Result<Rc<T>, Rc<Self>>; +} + +/// A blanket implementation of `CastRc` for traits extending `CastFrom`. +impl<S: ?Sized + CastFrom> CastRc for S +{ + fn cast<T: ?Sized + 'static>(self: Rc<Self>) -> Result<Rc<T>, Rc<Self>> + { + match caster::<T>((*self).type_id()) { + Some(caster) => Ok((caster.cast_rc)(self.rc_any())), + None => Err(self), + } + } +} diff --git a/syrette/src/libs/intertrait/mod.rs b/syrette/src/libs/intertrait/mod.rs index b07d91e..e7b3bdd 100644 --- a/syrette/src/libs/intertrait/mod.rs +++ b/syrette/src/libs/intertrait/mod.rs @@ -27,6 +27,7 @@ mod hasher; use hasher::BuildFastHasher; pub mod cast_box; +pub mod cast_rc; pub type BoxedCaster = Box<dyn Any + Send + Sync>; diff --git a/syrette/src/provider.rs b/syrette/src/provider.rs index 0d6a1cc..800315f 100644 --- a/syrette/src/provider.rs +++ b/syrette/src/provider.rs @@ -1,17 +1,25 @@ use std::marker::PhantomData; +use std::rc::Rc; -extern crate error_stack; - +use crate::castable_factory::AnyFactory; use crate::errors::injectable::ResolveError; use crate::interfaces::injectable::Injectable; use crate::DIContainer; -pub trait IInjectableTypeProvider +extern crate error_stack; + +pub enum Providable +{ + Injectable(Box<dyn Injectable>), + Factory(Rc<dyn AnyFactory>), +} + +pub trait IProvider { fn provide( &self, di_container: &DIContainer, - ) -> error_stack::Result<Box<dyn Injectable>, ResolveError>; + ) -> error_stack::Result<Providable, ResolveError>; } pub struct InjectableTypeProvider<InjectableType> @@ -33,15 +41,41 @@ where } } -impl<InjectableType> IInjectableTypeProvider for InjectableTypeProvider<InjectableType> +impl<InjectableType> IProvider for InjectableTypeProvider<InjectableType> where InjectableType: Injectable, { fn provide( &self, di_container: &DIContainer, - ) -> error_stack::Result<Box<dyn Injectable>, ResolveError> + ) -> error_stack::Result<Providable, ResolveError> + { + Ok(Providable::Injectable(InjectableType::resolve( + di_container, + )?)) + } +} + +pub struct FactoryProvider +{ + _factory: Rc<dyn AnyFactory>, +} + +impl FactoryProvider +{ + pub fn new(factory: Rc<dyn AnyFactory>) -> Self + { + Self { _factory: factory } + } +} + +impl IProvider for FactoryProvider +{ + fn provide( + &self, + _di_container: &DIContainer, + ) -> error_stack::Result<Providable, ResolveError> { - Ok(InjectableType::resolve(di_container)?) + Ok(Providable::Factory(self._factory.clone())) } } |