aboutsummaryrefslogtreecommitdiff
path: root/syrette/src
diff options
context:
space:
mode:
Diffstat (limited to 'syrette/src')
-rw-r--r--syrette/src/castable_factory.rs72
-rw-r--r--syrette/src/di_container.rs132
-rw-r--r--syrette/src/interfaces/factory.rs7
-rw-r--r--syrette/src/interfaces/mod.rs1
-rw-r--r--syrette/src/lib.rs3
-rw-r--r--syrette/src/libs/intertrait/cast_rc.rs34
-rw-r--r--syrette/src/libs/intertrait/mod.rs1
-rw-r--r--syrette/src/provider.rs48
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()))
}
}