aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md1
-rw-r--r--examples/async-factory/main.rs65
-rw-r--r--macros/src/lib.rs4
-rw-r--r--src/async_di_container.rs48
-rw-r--r--src/future.rs10
-rw-r--r--src/lib.rs51
6 files changed, 176 insertions, 3 deletions
diff --git a/README.md b/README.md
index 55ff28e..b71d6e3 100644
--- a/README.md
+++ b/README.md
@@ -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>>;
diff --git a/src/lib.rs b/src/lib.rs
index 2155350..247f907 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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)* })
+ })
+ };
+}