aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_macros/src')
-rw-r--r--mingling_macros/src/attr/chain.rs73
-rw-r--r--mingling_macros/src/attr/completion.rs39
-rw-r--r--mingling_macros/src/attr/help.rs41
-rw-r--r--mingling_macros/src/attr/renderer.rs47
4 files changed, 140 insertions, 60 deletions
diff --git a/mingling_macros/src/attr/chain.rs b/mingling_macros/src/attr/chain.rs
index 26bb137..dbd87dd 100644
--- a/mingling_macros/src/attr/chain.rs
+++ b/mingling_macros/src/attr/chain.rs
@@ -34,16 +34,14 @@ 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,
@@ -51,10 +49,52 @@ fn generate_proc_fn(
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
+ let fn_call_expr: syn::Expr = syn::parse_quote! { #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 {
@@ -82,11 +122,6 @@ 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<
@@ -98,7 +133,7 @@ fn generate_proc_fn(
#[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 +142,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 +227,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;
@@ -223,12 +257,11 @@ pub(crate) fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
// 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"))]
@@ -237,11 +270,9 @@ pub(crate) fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
&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
};
diff --git a/mingling_macros/src/attr/completion.rs b/mingling_macros/src/attr/completion.rs
index e917d7d..3ced091 100644
--- a/mingling_macros/src/attr/completion.rs
+++ b/mingling_macros/src/attr/completion.rs
@@ -47,11 +47,8 @@ pub(crate) fn completion_attr(attr: TokenStream, item: TokenStream) -> TokenStre
// Extract the first param pattern and type for the ctx parameter
let first_arg = &inputs[0];
- let (ctx_pat, _ctx_type) = match first_arg {
- FnArg::Typed(PatType { pat, ty, .. }) => {
- let param_pat = (**pat).clone();
- (param_pat, (**ty).clone())
- }
+ let _ctx_type = match first_arg {
+ FnArg::Typed(PatType { ty, .. }) => (**ty).clone(),
FnArg::Receiver(_) => {
return syn::Error::new(
first_arg.span(),
@@ -61,6 +58,7 @@ pub(crate) fn completion_attr(attr: TokenStream, item: TokenStream) -> TokenStre
.into();
}
};
+ let fixed_ctx: Pat = syn::parse_quote!(ctx);
// Extract resources from params 2 through N, skipping ctx
let resources = match extract_resources_from_args(sig, 1) {
@@ -70,7 +68,6 @@ pub(crate) fn completion_attr(attr: TokenStream, item: TokenStream) -> TokenStre
// Get the function body
let fn_body = &input_fn.block;
- let fn_body_stmts = &fn_body.stmts;
// Get function attributes excluding the completion attribute
let mut fn_attrs = input_fn.attrs.clone();
@@ -96,12 +93,26 @@ pub(crate) fn completion_attr(attr: TokenStream, item: TokenStream) -> TokenStre
// Generate immutable resource bindings
let immut_resource_stmts = generate_immut_resource_bindings(resources.iter(), &program_type);
- // Build the comp method body with resource injection
- // Use modify_res for mutable resources same pattern as renderer.rs
- let wrapped_body = if mut_resources.is_empty() {
- quote! { #(#fn_body_stmts)* }
+ // Build the call to the original function with resource arguments injected
+ let resource_args: Vec<_> = resources
+ .iter()
+ .map(|res| {
+ let var_name = &res.var_name;
+ quote! { #var_name }
+ })
+ .collect();
+
+ let fn_call = if has_resources {
+ quote! { #fn_name(#fixed_ctx, #(#resource_args),*) }
+ } else {
+ quote! { #fn_name(#fixed_ctx) }
+ };
+
+ // Wrap the function call with modify_res for mutable resources
+ let inner_call = if mut_resources.is_empty() {
+ fn_call
} else {
- let mut wrapped = quote! { #(#fn_body_stmts)* };
+ let mut wrapped = fn_call;
for res in mut_resources.iter().rev() {
let var_name = &res.var_name;
let inner_type = &res.inner_type;
@@ -117,10 +128,10 @@ pub(crate) fn completion_attr(attr: TokenStream, item: TokenStream) -> TokenStre
let comp_body = if has_resources {
quote! {
#(#immut_resource_stmts)*
- #wrapped_body
+ #inner_call
}
} else {
- quote! { #(#fn_body_stmts)* }
+ quote! { #inner_call }
};
// Generate the struct and implementation
@@ -135,7 +146,7 @@ pub(crate) fn completion_attr(attr: TokenStream, item: TokenStream) -> TokenStre
impl ::mingling::Completion for #struct_name {
type Previous = #previous_type_path;
- fn comp(#ctx_pat: &::mingling::ShellContext) #output {
+ fn comp(#fixed_ctx: &::mingling::ShellContext) #output {
#comp_body
}
}
diff --git a/mingling_macros/src/attr/help.rs b/mingling_macros/src/attr/help.rs
index 6defae4..aa7bc88 100644
--- a/mingling_macros/src/attr/help.rs
+++ b/mingling_macros/src/attr/help.rs
@@ -1,7 +1,7 @@
use proc_macro::TokenStream;
use quote::{ToTokens, quote};
use syn::spanned::Spanned;
-use syn::{Ident, ItemFn, ReturnType, Signature, TypePath, parse_macro_input};
+use syn::{Ident, ItemFn, Pat, ReturnType, Signature, TypePath, parse_macro_input};
use crate::get_global_set;
use crate::res_injection::{extract_args_info, generate_immut_resource_bindings};
@@ -25,8 +25,8 @@ pub(crate) fn help_attr(item: TokenStream) -> TokenStream {
.into();
}
- // Extract the entry type, parameter name, and resource injection params
- let (prev_param, entry_type, resources) = match extract_args_info(&input_fn.sig) {
+ // Extract the entry type and resource injection params
+ let (_, entry_type, resources) = match extract_args_info(&input_fn.sig) {
Ok(info) => info,
Err(e) => return e.to_compile_error().into(),
};
@@ -66,13 +66,31 @@ pub(crate) fn help_attr(item: TokenStream) -> TokenStream {
// Generate immutable resource bindings
let immut_resource_stmts = generate_immut_resource_bindings(resources.iter(), &program_type);
- // Build the render_help body with resource injection
- // Use modify_res for mutable resources same pattern as renderer.rs
+ // Build the call to the original function with resource arguments injected
+ let resource_args: Vec<_> = resources
+ .iter()
+ .map(|res| {
+ let var_name = &res.var_name;
+ quote! { #var_name }
+ })
+ .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);
+
+ let fn_call = if has_resources {
+ quote! { #fn_name(#fixed_prev, #(#resource_args),*) }
+ } else {
+ quote! { #fn_name(#fixed_prev) }
+ };
- let wrapped_body = if mut_resources.is_empty() {
- quote! { #(#fn_body_stmts)* }
+ // Wrap the function call with modify_res for mutable resources
+ let inner_call = if mut_resources.is_empty() {
+ fn_call
} else {
- let mut wrapped = quote! { #(#fn_body_stmts)* };
+ let mut wrapped = fn_call;
for res in mut_resources.iter().rev() {
let var_name = &res.var_name;
let inner_type = &res.inner_type;
@@ -88,10 +106,10 @@ pub(crate) fn help_attr(item: TokenStream) -> TokenStream {
let help_render_body = if has_resources {
quote! {
#(#immut_resource_stmts)*
- #wrapped_body
+ #inner_call
}
} else {
- quote! { #(#fn_body_stmts)* }
+ quote! { #inner_call }
};
// Register the help request mapping
@@ -128,7 +146,7 @@ pub(crate) fn help_attr(item: TokenStream) -> TokenStream {
impl ::mingling::HelpRequest for #struct_name {
type Entry = #entry_type;
- fn render_help(#prev_param: Self::Entry) -> ::mingling::RenderResult {
+ fn render_help(#fixed_prev: Self::Entry) -> ::mingling::RenderResult {
let __help_result = { #help_render_body };
::std::convert::Into::into(__help_result)
}
@@ -137,7 +155,6 @@ pub(crate) fn help_attr(item: TokenStream) -> TokenStream {
::mingling::macros::register_help!(#entry_type, #struct_name);
// Keep the original function unchanged
- #[allow(dead_code)]
#(#fn_attrs)*
#vis fn #fn_name(#original_inputs) -> #original_return_type {
#(#fn_body_stmts)*
diff --git a/mingling_macros/src/attr/renderer.rs b/mingling_macros/src/attr/renderer.rs
index c7cbb0b..828dc00 100644
--- a/mingling_macros/src/attr/renderer.rs
+++ b/mingling_macros/src/attr/renderer.rs
@@ -1,7 +1,7 @@
use proc_macro::TokenStream;
use quote::{ToTokens, quote};
use syn::spanned::Spanned;
-use syn::{ItemFn, ReturnType, Signature, TypePath, parse_macro_input};
+use syn::{ItemFn, Pat, ReturnType, Signature, TypePath, parse_macro_input};
use crate::get_global_set;
use crate::res_injection::{extract_args_info, generate_immut_resource_bindings};
@@ -31,8 +31,8 @@ pub(crate) fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream
.into();
}
- // Extract the previous type, parameter name, and resource injection params
- let (prev_param, previous_type, resources) = match extract_args_info(&input_fn.sig) {
+ // Extract the previous type and resource injection params
+ let (_, previous_type, resources) = match extract_args_info(&input_fn.sig) {
Ok(info) => info,
Err(e) => return e.to_compile_error().into(),
};
@@ -69,31 +69,53 @@ pub(crate) fn renderer_attr(attr: TokenStream, item: TokenStream) -> 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();
- let inner_body_with_resources = if has_mut_resources {
- let mut wrapped = quote! { #(#fn_body_stmts)* };
+ // Build the call to the original function with resource arguments injected
+ let resource_args: Vec<_> = resources
+ .iter()
+ .map(|res| {
+ let var_name = &res.var_name;
+ quote! { #var_name }
+ })
+ .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);
+
+ let fn_call = if has_resources {
+ quote! { #fn_name(#fixed_prev, #(#resource_args),*) }
+ } else {
+ quote! { #fn_name(#fixed_prev) }
+ };
+
+ // Wrap the function call with modify_res for mutable resources
+ let inner_call = if has_mut_resources {
+ let mut wrapped = fn_call;
for res in mut_resources.iter().rev() {
let var_name = &res.var_name;
let inner_type = &res.inner_type;
wrapped = quote! {
- ::mingling::this::<#program_type>().modify_res(|#var_name: &mut #inner_type| {
+ ::mingling::this::<#program_type>()
+ .modify_res(|#var_name: &mut #inner_type| {
#wrapped
})
};
}
wrapped
} else {
- quote! { #(#fn_body_stmts)* }
+ fn_call
};
- // Build the Renderer::render body with resource injection
- // The user's body now directly creates and returns a RenderResult.
+ // Build the Renderer::render body with resource injection.
+ // The trait method injects resources and calls the original function.
let render_fn_body = if has_resources {
quote! {
#(#immut_resource_stmts)*
- #inner_body_with_resources
+ #inner_call
}
} else {
- quote! { #inner_body_with_resources }
+ quote! { #inner_call }
};
// The original function preserves the user's exact signature and body.
@@ -112,14 +134,13 @@ pub(crate) fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream
impl ::mingling::Renderer for #struct_name {
type Previous = #previous_type;
- fn render(#prev_param: Self::Previous) -> ::mingling::RenderResult {
+ fn render(#fixed_prev: Self::Previous) -> ::mingling::RenderResult {
let __renderer_result = { #render_fn_body };
::std::convert::Into::into(__renderer_result)
}
}
// Keep the original function unchanged
- #[allow(dead_code)]
#(#fn_attrs)*
#vis fn #fn_name(#original_inputs) -> #original_return_type {
#(#fn_body_stmts)*