aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2023-12-22 12:33:35 +0100
committerHampusM <hampus@hampusmat.com>2023-12-22 12:50:58 +0100
commit60c6ce824c1b19adc86d893053010e1de52c3265 (patch)
treeeaa40fbe578a58ff8f40b4a993335d39254aa5c0
parent0f2756536e8fc311119da2af5b4dcc33f41bec6e (diff)
feat: add support for async constructors
-rw-r--r--examples/async/animals/human.rs12
-rw-r--r--macros/Cargo.toml2
-rw-r--r--macros/src/injectable/implementation.rs34
-rw-r--r--macros/src/lib.rs27
4 files changed, 62 insertions, 13 deletions
diff --git a/examples/async/animals/human.rs b/examples/async/animals/human.rs
index fc98ed8..5bc4903 100644
--- a/examples/async/animals/human.rs
+++ b/examples/async/animals/human.rs
@@ -1,5 +1,8 @@
+use std::time::Duration;
+
use syrette::injectable;
use syrette::ptr::{ThreadsafeSingletonPtr, TransientPtr};
+use tokio::time::sleep;
use crate::interfaces::cat::ICat;
use crate::interfaces::dog::IDog;
@@ -14,9 +17,14 @@ pub struct Human
#[injectable(IHuman, async = true)]
impl Human
{
- pub fn new(dog: ThreadsafeSingletonPtr<dyn IDog>, cat: TransientPtr<dyn ICat>)
- -> Self
+ pub async fn new(
+ dog: ThreadsafeSingletonPtr<dyn IDog>,
+ cat: TransientPtr<dyn ICat>,
+ ) -> Self
{
+ // The human needs some rest first
+ sleep(Duration::from_secs(1)).await;
+
Self { dog, cat }
}
}
diff --git a/macros/Cargo.toml b/macros/Cargo.toml
index f35729a..e883316 100644
--- a/macros/Cargo.toml
+++ b/macros/Cargo.toml
@@ -18,7 +18,7 @@ rustdoc-args = ["--cfg", "doc_cfg", "--html-in-header", "docs-style.html"]
[features]
factory = ["syrette/factory"]
prevent-circular = []
-async = []
+async = ["syrette/async"]
[dependencies]
syn = { version = "1.0.96", features = ["full"] }
diff --git a/macros/src/injectable/implementation.rs b/macros/src/injectable/implementation.rs
index e2bcd3e..a98d1ce 100644
--- a/macros/src/injectable/implementation.rs
+++ b/macros/src/injectable/implementation.rs
@@ -83,7 +83,7 @@ impl<Dep: IDependency> InjectableImpl<Dep>
})
}
- pub fn validate(&self) -> Result<(), InjectableImplError>
+ pub fn validate(&self, is_async: bool) -> Result<(), InjectableImplError>
{
if matches!(self.constructor_method.sig.output, ReturnType::Default) {
return Err(InjectableImplError::InvalidConstructorMethodReturnType {
@@ -127,10 +127,14 @@ impl<Dep: IDependency> InjectableImpl<Dep>
});
}
- if let Some(asyncness) = self.constructor_method.sig.asyncness {
- return Err(InjectableImplError::ConstructorMethodAsync {
- asyncness_span: asyncness.span,
- });
+ if !is_async {
+ if let Some(asyncness) = self.constructor_method.sig.asyncness {
+ return Err(
+ InjectableImplError::ConstructorMethodAsyncWithMissingAsyncAttr {
+ asyncness_span: asyncness.span,
+ },
+ );
+ }
}
if !self.constructor_method.sig.generics.params.is_empty() {
@@ -235,6 +239,12 @@ impl<Dep: IDependency> InjectableImpl<Dep>
.map(|index| format_ident!("dependency_{index}"))
.collect::<Vec<_>>();
+ let maybe_await_constructor = if self.constructor_method.sig.asyncness.is_some() {
+ quote! { .await }
+ } else {
+ quote! {}
+ };
+
quote! {
#maybe_doc_hidden
impl #generics syrette::interfaces::async_injectable::AsyncInjectable<
@@ -273,7 +283,7 @@ impl<Dep: IDependency> InjectableImpl<Dep>
Ok(syrette::ptr::TransientPtr::new(Self::#constructor(
#(#dependency_idents),*
- )))
+ )#maybe_await_constructor))
})
}
}
@@ -522,9 +532,15 @@ pub enum InjectableImplError
unsafety_span: Span
},
- #[error("Constructor method is not allowed to be async"), span = asyncness_span]
- #[note("Required by the 'injectable' attribute macro")]
- ConstructorMethodAsync {
+ #[
+ error(concat!(
+ "Constructor method is not allowed to be async when the 'async' flag of the ",
+ "'injectable' macro is not set to true"
+ )),
+ span = asyncness_span
+ ]
+ #[help("Enable the 'async' flag here")]
+ ConstructorMethodAsyncWithMissingAsyncAttr {
asyncness_span: Span
},
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index fa84e0e..e55e23f 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -100,6 +100,30 @@ const PACKAGE_VERSION: &str = env!("CARGO_PKG_VERSION");
/// }
/// }
/// ```
+/// <br>
+///
+/// When the `async` crate feature is enabled, the constructor can be [`async`].
+/// ```
+/// # #[cfg(feature = "async")]
+/// # mod example {
+/// # use syrette::injectable;
+/// #
+/// # struct PasswordManager { value: u32}
+/// #
+/// # async fn some_async_computation() -> u32 { 1 }
+/// #
+/// #[injectable(async = true)]
+/// impl PasswordManager
+/// {
+/// pub async fn new() -> Self
+/// {
+/// let value = some_async_computation().await;
+///
+/// Self { value }
+/// }
+/// }
+/// # }
+/// ```
///
/// # Attributes
/// Attributes specific to impls with this attribute macro.
@@ -149,6 +173,7 @@ const PACKAGE_VERSION: &str = env!("CARGO_PKG_VERSION");
/// [`Injectable`]: ../syrette/interfaces/injectable/trait.Injectable.html
/// [`AsyncInjectable`]: ../syrette/interfaces/async_injectable/trait.AsyncInjectable.html
/// [`di_container_bind`]: ../syrette/macro.di_container_bind.html
+/// [`async`]: https://doc.rust-lang.org/std/keyword.async.html
#[cfg(not(tarpaulin_include))]
#[proc_macro_error]
#[proc_macro_attribute]
@@ -233,7 +258,7 @@ pub fn injectable(args_stream: TokenStream, input_stream: TokenStream) -> TokenS
let injectable_impl =
InjectableImpl::<Dependency>::new(item_impl, &constructor).unwrap_or_abort();
- injectable_impl.validate().unwrap_or_abort();
+ injectable_impl.validate(is_async).unwrap_or_abort();
let expanded_injectable_impl = injectable_impl.expand(no_doc_hidden, is_async);