aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src/attr/chain.rs
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_macros/src/attr/chain.rs')
-rw-r--r--mingling_macros/src/attr/chain.rs95
1 files changed, 61 insertions, 34 deletions
diff --git a/mingling_macros/src/attr/chain.rs b/mingling_macros/src/attr/chain.rs
index 120e65d..dc28a39 100644
--- a/mingling_macros/src/attr/chain.rs
+++ b/mingling_macros/src/attr/chain.rs
@@ -34,27 +34,72 @@ fn validate_return_type(sig: &Signature) -> Result<(), proc_macro2::TokenStream>
/// Builds the `proc` function implementation inside the generated `Chain` impl.
///
-/// The user's function body is inlined directly, and its result is converted
-/// via `.into()` to `ChainProcess<ProgramType>`.
-#[allow(unused_variables)]
+/// Instead of inlining the user's body, the trait method calls the original
+/// function by name, with resources injected from the application context.
fn generate_proc_fn(
+ fn_name: &Ident,
has_resources: bool,
resources: &[ResourceInjection],
program_type: &proc_macro2::TokenStream,
previous_type: &TypePath,
- prev_param: &Pat,
- fn_body_stmts: &[syn::Stmt],
is_async_fn: bool,
is_unit_return: bool,
- origin_return_type: &proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
let immut_resource_stmts = generate_immut_resource_bindings(resources.iter(), program_type);
let mut_resources: Vec<_> = resources.iter().filter(|r| r.is_mut).collect();
+ // Use a fixed parameter name `prev` for the trait method, regardless of
+ // the user's original parameter name (which may be `_` and cannot be
+ // referenced in expression position).
+ let fixed_prev: Pat = syn::parse_quote!(prev);
+
+ // Build the call to the original function with resource arguments injected.
+ // The variable names come from the resource injection bindings
+ // (immut bindings are `let #name = …`, mut closures receive `|#name: &mut T|`),
+ // which match the original function's parameter names.
+ let resource_args: Vec<_> = resources
+ .iter()
+ .map(|res| {
+ let var_name = &res.var_name;
+ quote! { #var_name }
+ })
+ .collect();
+
+ let fn_call = if has_resources {
+ let call = quote! { #fn_name(#fixed_prev, #(#resource_args),*) };
+ if is_async_fn {
+ quote! { #call.await }
+ } else {
+ call
+ }
+ } else {
+ let call = quote! { #fn_name(#fixed_prev) };
+ if is_async_fn {
+ quote! { #call.await }
+ } else {
+ call
+ }
+ };
+
+ // Convert the function call to a syn::Stmt so existing wrapping functions can use it.
+ // For non-unit returns, wrap in `to_chain()` so mutable-resource closures
+ // return `ChainProcess<C>` (as required by `__modify_res_and_return_route`).
+ let fn_call_expr: syn::Expr = if is_unit_return {
+ syn::parse_quote! { #fn_call }
+ } else {
+ syn::parse_quote! { ::mingling::Routable::<#program_type>::to_chain(#fn_call) }
+ };
+ let fn_call_stmt = syn::Stmt::Expr(fn_call_expr, None);
+
let wrapped_body = if is_async_fn && !mut_resources.is_empty() {
- wrap_body_with_mut_resources_async(fn_body_stmts, &mut_resources, program_type)
+ wrap_body_with_mut_resources_async(&[fn_call_stmt], &mut_resources, program_type)
} else {
- wrap_body_with_mut_resources(fn_body_stmts, &mut_resources, program_type, is_unit_return)
+ wrap_body_with_mut_resources(
+ &[fn_call_stmt],
+ &mut_resources,
+ program_type,
+ is_unit_return,
+ )
};
let proc_body = if is_unit_return {
@@ -62,13 +107,13 @@ fn generate_proc_fn(
quote! {
#(#immut_resource_stmts)*
#wrapped_body;
- <crate::ResultEmpty as ::mingling::Grouped::<crate::ThisProgram>>
+ <crate::ResultEmpty as ::mingling::Routable::<crate::ThisProgram>>
::to_chain(crate::ResultEmpty)
}
} else {
quote! {
#wrapped_body;
- <crate::ResultEmpty as ::mingling::Grouped::<crate::ThisProgram>>
+ <crate::ResultEmpty as ::mingling::Routable::<crate::ThisProgram>>
::to_chain(crate::ResultEmpty)
}
};
@@ -82,23 +127,16 @@ fn generate_proc_fn(
} else {
quote! { #wrapped_body }
};
- // Convert the body result to `ChainProcess` using the user-declared
- // return type as the source type for a fully-qualified `Into` call.
- // This works for both:
- // - `-> Next` / `-> ChainProcess`: identity `From<T> for T`
- // - `-> PackType`: `Into<ChainProcess>` from pack!/derive
quote! {
let __chain_result = { #body };
- <#origin_return_type as ::std::convert::Into<
- ::mingling::ChainProcess<#program_type>
- >>::into(__chain_result)
+ ::mingling::Routable::<#program_type>::to_chain(__chain_result)
}
};
#[cfg(feature = "async")]
{
quote! {
- async fn proc(#prev_param: #previous_type) -> ::mingling::ChainProcess<#program_type> {
+ async fn proc(#fixed_prev: #previous_type) -> ::mingling::ChainProcess<#program_type> {
#proc_body
}
}
@@ -107,7 +145,7 @@ fn generate_proc_fn(
#[cfg(not(feature = "async"))]
{
quote! {
- fn proc(#prev_param: #previous_type) -> ::mingling::ChainProcess<#program_type> {
+ fn proc(#fixed_prev: #previous_type) -> ::mingling::ChainProcess<#program_type> {
#proc_body
}
}
@@ -192,13 +230,12 @@ pub(crate) fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
}
// Extract the previous type, parameter name, and resource injection params
- let (prev_param, previous_type, resources) = match extract_args_info(&input_fn.sig) {
+ let (_, previous_type, resources) = match extract_args_info(&input_fn.sig) {
Ok(info) => info,
Err(e) => return e.to_compile_error().into(),
};
// Prepare building blocks
- let fn_body = &input_fn.block;
let mut fn_attrs = input_fn.attrs.clone();
fn_attrs.retain(|attr| !attr.path().is_ident("chain"));
let vis = &input_fn.vis;
@@ -215,33 +252,23 @@ pub(crate) fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
// Always use the default crate-defined program path
let program_type = crate::default_program_path();
- // Extract the user's return type for the explicit Into turbofish
- let origin_return_type = match &input_fn.sig.output {
- ReturnType::Type(_, ty) => quote! { #ty },
- ReturnType::Default => quote! { () },
- };
-
// Generate the `proc` function for the Chain impl
let proc_fn = generate_proc_fn(
+ fn_name,
has_resources,
&resources,
&program_type,
&previous_type,
- &prev_param,
- &fn_body.stmts,
#[cfg(feature = "async")]
is_async_fn,
#[cfg(not(feature = "async"))]
false,
is_unit_return,
- &origin_return_type,
);
- // Preserve the original function untouched, with dead_code allowed
- // since it may only be called through the Chain trait dispatch.
+ // Preserve the original function untouched
// Note: do NOT add `#vis` here — `input_fn` (ItemFn) already contains its own visibility.
let original_fn = quote! {
- #[allow(dead_code)]
#(#fn_attrs)*
#input_fn
};