From 4f7bd4f6fa5d27cfe703c54aa029a321f40d19fb Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Sat, 27 Jun 2026 17:46:29 +0800 Subject: feat(macros): add async mutable resource injection for `#[chain]` Support `&mut T` resource parameters in async chain functions by using an extract-store pattern that avoids holding mutable borrows across await points. Remove the previous compile-time rejection of this combination. --- mingling_macros/src/chain.rs | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) (limited to 'mingling_macros/src/chain.rs') diff --git a/mingling_macros/src/chain.rs b/mingling_macros/src/chain.rs index 6191d39..5f72422 100644 --- a/mingling_macros/src/chain.rs +++ b/mingling_macros/src/chain.rs @@ -2,7 +2,7 @@ use crate::res_injection::{ ResourceInjection, extract_args_info, generate_immut_resource_bindings, - wrap_body_with_mut_resources, + wrap_body_with_mut_resources, wrap_body_with_mut_resources_async, }; use proc_macro::TokenStream; use quote::{ToTokens, quote}; @@ -93,7 +93,11 @@ fn generate_proc_fn( fn_body_stmts }; - let wrapped_body = wrap_body_with_mut_resources(body_stmts, &mut_resources, program_type); + let wrapped_body = if is_async_fn && !mut_resources.is_empty() { + wrap_body_with_mut_resources_async(body_stmts, &mut_resources, program_type) + } else { + wrap_body_with_mut_resources(body_stmts, &mut_resources, program_type) + }; // When the function returns `()`, wrap the result with ResultEmpty let call_or_wrapped = if is_unit_return { @@ -252,19 +256,6 @@ fn reject_async(sig: &Signature) -> Result<(), proc_macro2::TokenStream> { Ok(()) } -/// Ensures no `&mut` resource injection is used in async functions. -#[cfg(feature = "async")] -fn reject_mut_in_async(resources: &[ResourceInjection]) -> Result<(), proc_macro2::TokenStream> { - if let Some(mut_res) = resources.iter().find(|r| r.is_mut) { - return Err(syn::Error::new( - mut_res.var_name.span(), - "Cannot use `&mut` resource injection in async chain function.", - ) - .to_compile_error()); - } - Ok(()) -} - pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream { // Reject non-empty attribute arguments; #[chain] must be bare if !attr.is_empty() { @@ -304,14 +295,6 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream { Err(e) => return e.to_compile_error().into(), }; - // Reject `&mut` in async chains - #[cfg(feature = "async")] - if is_async_fn { - if let Err(err) = reject_mut_in_async(&resources) { - return err.into(); - } - } - // Prepare building blocks let sig = &input_fn.sig; let inputs = &sig.inputs; -- cgit