diff options
Diffstat (limited to 'mingling_macros/src/help.rs')
| -rw-r--r-- | mingling_macros/src/help.rs | 101 |
1 files changed, 50 insertions, 51 deletions
diff --git a/mingling_macros/src/help.rs b/mingling_macros/src/help.rs index e9e91cf..e904b16 100644 --- a/mingling_macros/src/help.rs +++ b/mingling_macros/src/help.rs @@ -1,44 +1,10 @@ use proc_macro::TokenStream; use quote::{ToTokens, quote}; use syn::spanned::Spanned; -use syn::{ - FnArg, Ident, ItemFn, Pat, PatType, ReturnType, Signature, Type, TypePath, parse_macro_input, -}; +use syn::{Ident, ItemFn, ReturnType, Signature, Type, TypePath, parse_macro_input}; use crate::get_global_set; - -/// Extracts the previous type and parameter name from function arguments -fn extract_previous_info(sig: &Signature) -> syn::Result<(Pat, TypePath)> { - // The function should have exactly one parameter - if sig.inputs.len() != 1 { - return Err(syn::Error::new( - sig.inputs.span(), - "Help function must have exactly one parameter (the entry type)", - )); - } - - // First and only parameter is the entry type - let arg = &sig.inputs[0]; - match arg { - FnArg::Typed(PatType { pat, ty, .. }) => { - // Extract the pattern (parameter name) - let param_pat = (**pat).clone(); - - // Extract the type - match &**ty { - Type::Path(type_path) => Ok((param_pat, type_path.clone())), - _ => Err(syn::Error::new( - ty.span(), - "Parameter type must be a type path", - )), - } - } - FnArg::Receiver(_) => Err(syn::Error::new( - arg.span(), - "Help function cannot have self parameter", - )), - } -} +use crate::res_injection::{extract_args_info, generate_immut_resource_bindings}; /// Validates the return type is () or empty fn validate_return_type(sig: &Signature) -> syn::Result<()> { @@ -65,8 +31,8 @@ pub fn help_attr(item: TokenStream) -> TokenStream { .into(); } - // Extract the entry type and parameter name from function arguments - let (prev_param, entry_type) = match extract_previous_info(&input_fn.sig) { + // Extract the entry type, parameter name, and resource injection params + let (prev_param, entry_type, resources) = match extract_args_info(&input_fn.sig) { Ok(info) => info, Err(e) => return e.to_compile_error().into(), }; @@ -78,8 +44,9 @@ pub fn help_attr(item: TokenStream) -> TokenStream { // Get the function body let fn_body = &input_fn.block; + let fn_body_stmts = &fn_body.stmts; - // Get function attributes (excluding the help attribute) + // Get function attributes excluding the help attribute let mut fn_attrs = input_fn.attrs.clone(); fn_attrs.retain(|attr| !attr.path().is_ident("help")); @@ -89,13 +56,52 @@ pub fn help_attr(item: TokenStream) -> TokenStream { // Get function name let fn_name = &input_fn.sig.ident; - // Generate internal name using snake_case for the chain macro + // Get original inputs to keep the original function + + let original_inputs = input_fn.sig.inputs.clone(); + + // Generate internal name using snake_case let internal_name = format!( "__internal_help_{}", just_fmt::snake_case!(fn_name.to_string()) ); let struct_name = Ident::new(&internal_name, fn_name.span()); + let program_type = crate::default_program_path(); + let has_resources = !resources.is_empty(); + let mut_resources: Vec<_> = resources.iter().filter(|r| r.is_mut).collect(); + + // 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 + + let wrapped_body = if mut_resources.is_empty() { + quote! { #(#fn_body_stmts)* } + } else { + let mut wrapped = quote! { #(#fn_body_stmts)* }; + 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| { + #wrapped + }) + }; + } + wrapped + }; + + let help_render_body = if has_resources { + quote! { + #(#immut_resource_stmts)* + #wrapped_body + } + } else { + quote! { #(#fn_body_stmts)* } + }; + // Register the help request mapping let help_entry = build_help_entry(&struct_name, &entry_type); let entry_str = help_entry.to_string(); @@ -115,23 +121,16 @@ pub fn help_attr(item: TokenStream) -> TokenStream { type Entry = #entry_type; fn render_help(#prev_param: Self::Entry, __renderer_inner_result: &mut ::mingling::RenderResult) { - // Create a local wrapper function that includes `__renderer_inner_result` parameter - // This allows r_println! to access `__renderer_inner_result` - #[allow(non_snake_case)] - fn help_wrapper(#prev_param: #entry_type, __renderer_inner_result: &mut ::mingling::RenderResult) { - #fn_body - } - - // Call the wrapper function - help_wrapper(#prev_param, __renderer_inner_result); + #help_render_body } } ::mingling::macros::register_help!(#entry_type, #struct_name); - // Keep the original function for internal use (without `__renderer_inner_result` parameter) + // Keep the original function for internal use with original params without __renderer_inner_result + #(#fn_attrs)* - #vis fn #fn_name(#prev_param: #entry_type) { + #vis fn #fn_name(#original_inputs) { let mut dummy_r = ::mingling::RenderResult::default(); let __renderer_inner_result = &mut dummy_r; #fn_body |
