diff options
author | HampusM <hampus@hampusmat.com> | 2022-09-18 20:20:53 +0200 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2022-09-18 20:20:53 +0200 |
commit | 6813690e893bc461bd2be60509850a5a80454c6a (patch) | |
tree | f84e5321a0f608beda6b3abbf82de5c9496efc3e | |
parent | 0b914f415cb04c45d8655cae3828af264887d203 (diff) |
feat: add binding async factories to async DI container
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | examples/async-factory/main.rs | 65 | ||||
-rw-r--r-- | macros/src/lib.rs | 4 | ||||
-rw-r--r-- | src/async_di_container.rs | 48 | ||||
-rw-r--r-- | src/future.rs | 10 | ||||
-rw-r--r-- | src/lib.rs | 51 |
6 files changed, 176 insertions, 3 deletions
@@ -22,6 +22,7 @@ From the [syrette Wikipedia article](https://en.wikipedia.org/wiki/Syrette). - Binding singletons - Injection of third-party structs & traits - Named bindings +- Async factories ## Optional features - `factory`. Binding factories (Rust nightly required) diff --git a/examples/async-factory/main.rs b/examples/async-factory/main.rs new file mode 100644 index 0000000..74e12c7 --- /dev/null +++ b/examples/async-factory/main.rs @@ -0,0 +1,65 @@ +#![deny(clippy::all)] +#![deny(clippy::pedantic)] +#![allow(clippy::module_name_repetitions)] + +use anyhow::Result; +use syrette::{async_closure, factory, AsyncDIContainer}; + +trait IFoo +{ + fn bar(&self); +} + +#[factory(async = true)] +type IFooFactory = dyn Fn(i32) -> dyn IFoo; + +struct Foo +{ + cnt: i32, +} + +impl Foo +{ + fn new(cnt: i32) -> Self + { + Self { cnt } + } +} + +impl IFoo for Foo +{ + fn bar(&self) + { + for _ in 1..self.cnt { + println!("Foobar"); + } + } +} + +#[tokio::main] +async fn main() -> Result<()> +{ + let mut di_container = AsyncDIContainer::new(); + + di_container + .bind::<IFooFactory>() + .to_async_factory(&|_| { + async_closure!(|cnt| { + let foo = Box::new(Foo::new(cnt)); + + foo as Box<dyn IFoo> + }) + }) + .await?; + + let foo_factory = di_container + .get::<IFooFactory>() + .await? + .threadsafe_factory()?; + + let foo = foo_factory(4).await; + + foo.bar(); + + Ok(()) +} diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 2715f3d..2cd57f0 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -225,9 +225,7 @@ pub fn factory(args_stream: TokenStream, type_alias_stream: TokenStream) -> Toke factory_interface.output = parse( if is_async { quote! { - std::pin::Pin<Box< - dyn std::future::Future<Output = syrette::ptr::TransientPtr<#output>> - >> + syrette::future::BoxFuture<'static, syrette::ptr::TransientPtr<#output>> } } else { quote! { diff --git a/src/async_di_container.rs b/src/async_di_container.rs index 7dcca57..1ead116 100644 --- a/src/async_di_container.rs +++ b/src/async_di_container.rs @@ -294,6 +294,54 @@ where Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) } + /// Creates a binding of factory type `Interface` to a async factory inside of the + /// associated [`AsyncDIContainer`]. + /// + /// *This function is only available if Syrette is built with the "factory" and + /// "async" features.* + /// + /// # Errors + /// Will return Err if the associated [`AsyncDIContainer`] already have a binding for + /// the interface. + #[cfg(all(feature = "factory", feature = "async"))] + pub async fn to_async_factory<Args, Return, FactoryFunc>( + &self, + factory_func: &'static FactoryFunc, + ) -> Result<AsyncBindingWhenConfigurator<Interface>, AsyncBindingBuilderError> + where + Args: 'static, + Return: 'static + ?Sized, + Interface: Fn<Args, Output = crate::future::BoxFuture<'static, Return>>, + FactoryFunc: Fn< + (Arc<AsyncDIContainer>,), + Output = Box< + (dyn Fn<Args, Output = crate::future::BoxFuture<'static, Return>>), + >, + > + Send + + Sync, + { + let mut bindings_lock = self.di_container.bindings.lock().await; + + if bindings_lock.has::<Interface>(None) { + return Err(AsyncBindingBuilderError::BindingAlreadyExists(type_name::< + Interface, + >( + ))); + } + + let factory_impl = ThreadsafeCastableFactory::new(factory_func); + + bindings_lock.set::<Interface>( + None, + Box::new(crate::provider::r#async::AsyncFactoryProvider::new( + crate::ptr::ThreadsafeFactoryPtr::new(factory_impl), + false, + )), + ); + + Ok(AsyncBindingWhenConfigurator::new(self.di_container.clone())) + } + /// Creates a binding of type `Interface` to a factory that takes no arguments /// inside of the associated [`AsyncDIContainer`]. /// diff --git a/src/future.rs b/src/future.rs new file mode 100644 index 0000000..ae4b3a2 --- /dev/null +++ b/src/future.rs @@ -0,0 +1,10 @@ +//! Future related utilities. +//! +//! *This module is only available if Syrette is built with the "async" feature.* + +#![allow(clippy::module_name_repetitions)] +use std::future::Future; +use std::pin::Pin; + +/// A boxed future. +pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>; @@ -16,6 +16,9 @@ pub mod ptr; pub mod async_di_container; #[cfg(feature = "async")] +pub mod future; + +#[cfg(feature = "async")] pub use async_di_container::AsyncDIContainer; pub use di_container::DIContainer; pub use syrette_macros::*; @@ -75,3 +78,51 @@ macro_rules! di_container_bind { syrette::declare_interface!($implementation -> $interface); }; } + +/// Creates a async closure. +/// +/// *This macro is only available if Syrette is built with the "async" feature.* +/// +/// # Examples +/// ``` +/// # use syrette::async_closure; +/// # +/// # async fn do_heavy_operation(timeout: u32, size: u32) -> String { String::new() } +/// # +/// # async fn do_other_heavy_operation(input: String) -> String { String::new() } +/// # +/// async_closure!(|timeout, size| { +/// let value = do_heavy_operation(timeout, size).await; +/// +/// let final_value = do_other_heavy_operation(value).await; +/// +/// final_value +/// }); +/// ``` +/// +/// expands to the following +/// +/// ```rust +/// # async fn do_heavy_operation(timeout: u32, size: u32) -> String { String::new() } +/// # +/// # async fn do_other_heavy_operation(input: String) -> String { String::new() } +/// # +/// Box::new(|timeout, size| { +/// Box::pin(async move { +/// let value = do_heavy_operation(timeout, size).await; +/// +/// let final_value = do_other_heavy_operation(value).await; +/// +/// final_value +/// }) +/// }); +/// ``` +#[cfg(feature = "async")] +#[macro_export] +macro_rules! async_closure { + (|$($args: ident),*| { $($inner: stmt);* }) => { + Box::new(|$($args),*| { + Box::pin(async move { $($inner)* }) + }) + }; +} |