diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-07-20 00:59:17 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-07-20 01:01:27 +0800 |
| commit | 1f583f625a7d541dc7d47a8136b31d29a5ed5441 (patch) | |
| tree | 795006c8c2a9f10c866951de5aba318a64007219 /mingling_macros/src/help.rs | |
| parent | e8ae63988d562969df47ae6d92129974e7fb6980 (diff) | |
chore(macros): reorganize module hierarchy in mingling_macros
Restructure flat module layout into logical directories (attr/, derive/,
func/, systems/, extensions/, utils.rs). Tighten internal function
visibility to pub(crate). All public API signatures remain unchanged.
Diffstat (limited to 'mingling_macros/src/help.rs')
| -rw-r--r-- | mingling_macros/src/help.rs | 214 |
1 files changed, 0 insertions, 214 deletions
diff --git a/mingling_macros/src/help.rs b/mingling_macros/src/help.rs deleted file mode 100644 index 6e0d12e..0000000 --- a/mingling_macros/src/help.rs +++ /dev/null @@ -1,214 +0,0 @@ -use proc_macro::TokenStream; -use quote::{ToTokens, quote}; -use syn::spanned::Spanned; -use syn::{Ident, ItemFn, ReturnType, Signature, TypePath, parse_macro_input}; - -use crate::get_global_set; -use crate::res_injection::{extract_args_info, generate_immut_resource_bindings}; - -/// Extracts the user's return type, returning `None` for no return type. -fn extract_user_return_type(sig: &Signature) -> Option<proc_macro2::TokenStream> { - match &sig.output { - ReturnType::Type(_, ty) => Some(quote! { #ty }), - ReturnType::Default => None, - } -} - -pub fn help_attr(item: TokenStream) -> TokenStream { - // Parse the function item - let input_fn = parse_macro_input!(item as ItemFn); - - // Validate the function is not async - if input_fn.sig.asyncness.is_some() { - return syn::Error::new(input_fn.sig.span(), "Help function cannot be async") - .to_compile_error() - .into(); - } - - // 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(), - }; - - // Determine the user's return type for preserving the original function - let user_return_type = extract_user_return_type(&input_fn.sig); - - // Get the function body - let fn_body = &input_fn.block; - let fn_body_stmts = &fn_body.stmts; - - // Get function attributes excluding the help attribute - let mut fn_attrs = input_fn.attrs.clone(); - fn_attrs.retain(|attr| !attr.path().is_ident("help")); - - // Get function visibility - let vis = &input_fn.vis; - - // Get function name - let fn_name = &input_fn.sig.ident; - - // Get original inputs to keep the original function - let original_inputs = input_fn.sig.inputs.clone(); - let original_return_type = user_return_type.clone().unwrap_or(quote! { () }); - - // 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(); - - // Check for duplicate variant before inserting - let variant_name = entry_type.path.segments.last().unwrap().ident.to_string(); - { - let helps = get_global_set(&crate::HELP_REQUESTS).lock().unwrap(); - if let Err(err) = crate::check_duplicate_variant( - &helps, - &entry_str, - &variant_name, - "help", - entry_type.span(), - ) { - return err.into(); - } - } - - get_global_set(&crate::HELP_REQUESTS) - .lock() - .unwrap() - .insert(entry_str); - - // Generate the struct and HelpRequest implementation - let expanded = quote! { - #(#fn_attrs)* - #[doc(hidden)] - #[allow(non_camel_case_types)] - #vis struct #struct_name; - - impl ::mingling::HelpRequest for #struct_name { - type Entry = #entry_type; - - fn render_help(#prev_param: Self::Entry) -> ::mingling::RenderResult { - let __help_result = { #help_render_body }; - ::std::convert::Into::into(__help_result) - } - } - - ::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)* - } - }; - - expanded.into() -} - -/// Builds a help request entry for the global help requests list -fn build_help_entry(struct_name: &Ident, entry_type: &TypePath) -> proc_macro2::TokenStream { - let enum_variant = &entry_type.path.segments.last().unwrap().ident; - quote! { - Self::#enum_variant => { - // SAFETY: The member_id check ensures that `any` contains a value of type `#entry_type`, - // so downcasting to `#entry_type` is safe. - let value = unsafe { any.downcast::<#entry_type>().unwrap_unchecked() }; - <#struct_name as ::mingling::HelpRequest>::render_help(value) - } - } -} - -pub fn register_help(input: TokenStream) -> TokenStream { - // Parse the input as a comma-separated list of arguments - let input_parsed = syn::parse_macro_input!(input with syn::punctuated::Punctuated<syn::Expr, syn::Token![,]>::parse_terminated); - - // Check if there are exactly two elements - if input_parsed.len() != 2 { - return syn::Error::new( - input_parsed.span(), - "Expected exactly two comma-separated arguments: `EntryType, StructName`", - ) - .to_compile_error() - .into(); - } - - // Extract the two elements - let entry_type_expr = &input_parsed[0]; - let struct_name_expr = &input_parsed[1]; - - // Convert expressions to TypePath and Ident - let entry_type = match syn::parse2::<TypePath>(entry_type_expr.to_token_stream()) { - Ok(ty) => ty, - Err(e) => return e.to_compile_error().into(), - }; - - let struct_name = match syn::parse2::<syn::Ident>(struct_name_expr.to_token_stream()) { - Ok(ident) => ident, - Err(e) => return e.to_compile_error().into(), - }; - - // Register the help request mapping - let help_entry = build_help_entry(&struct_name, &entry_type); - let entry_str = help_entry.to_string(); - - // Check if entry was already pre-inserted by `#[help]` attribute - let mut helps = get_global_set(&crate::HELP_REQUESTS).lock().unwrap(); - if helps.contains(&entry_str) { - // Already registered by `#[help]`, no duplicate check needed - return quote! {}.into(); - } - - // Check for duplicate variant (different struct, same type) - let variant_name = entry_type.path.segments.last().unwrap().ident.to_string(); - if let Err(err) = - crate::check_duplicate_variant(&helps, &entry_str, &variant_name, "help", entry_type.span()) - { - return err.into(); - } - - helps.insert(entry_str); - - quote! {}.into() -} |
