diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-05-16 22:31:52 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-05-16 22:31:52 +0800 |
| commit | 60b91db3df168532f9143f0cafb7b5166e1dc78b (patch) | |
| tree | ddb10f909a46033af16d725ab0dae8385227b985 /mingling_macros | |
| parent | 05d07bbc627b59fd07a8764a972b4aac63a46f53 (diff) | |
Accept paths for program name parameters in macros
All proc macros (`pack!`, `dispatcher!`, `#[chain]`, `#[program_setup]`,
`#[dispatcher_clap]`, `#[derive(Groupped)]`) now parse program names as
`syn::Path` instead of bare `Ident`, allowing use of paths like
`crate::MyProgram` or `my_crate::MyProgram`.
The default program name `ThisProgram` is no longer re-exported or
required
as an import — generated code references `crate::ThisProgram` directly.
Diffstat (limited to 'mingling_macros')
| -rw-r--r-- | mingling_macros/src/chain.rs | 12 | ||||
| -rw-r--r-- | mingling_macros/src/dispatcher.rs | 40 | ||||
| -rw-r--r-- | mingling_macros/src/dispatcher_clap.rs | 77 | ||||
| -rw-r--r-- | mingling_macros/src/help.rs | 2 | ||||
| -rw-r--r-- | mingling_macros/src/pack.rs | 53 | ||||
| -rw-r--r-- | mingling_macros/src/program_setup.rs | 4 |
6 files changed, 85 insertions, 103 deletions
diff --git a/mingling_macros/src/chain.rs b/mingling_macros/src/chain.rs index 3c0cca5..fd1db65 100644 --- a/mingling_macros/src/chain.rs +++ b/mingling_macros/src/chain.rs @@ -5,8 +5,6 @@ use syn::{ FnArg, Ident, ItemFn, Pat, PatType, ReturnType, Signature, Type, TypePath, parse_macro_input, }; -use crate::DEFAULT_PROGRAM_NAME; - /// Extracted information about a resource injection parameter struct ResourceInjection { var_name: Ident, @@ -137,12 +135,10 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream { // Parse the attribute arguments (e.g., MyProgram from #[chain(MyProgram)]) // If no argument is provided, use ThisProgram let (group_name, use_crate_prefix) = if attr.is_empty() { - ( - Ident::new(DEFAULT_PROGRAM_NAME, proc_macro2::Span::call_site()), - true, - ) + (crate::default_program_path(), true) } else { - (parse_macro_input!(attr as Ident), false) + let path: syn::Path = parse_macro_input!(attr as syn::Path); + (quote! { #path }, false) }; // Parse the function item @@ -235,7 +231,7 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream { let program_type = if use_crate_prefix { crate::default_program_path() } else { - quote! { #group_name } + group_name.clone() }; // Check for async fn + &mut combination, which is not supported diff --git a/mingling_macros/src/dispatcher.rs b/mingling_macros/src/dispatcher.rs index a74db6d..ba36545 100644 --- a/mingling_macros/src/dispatcher.rs +++ b/mingling_macros/src/dispatcher.rs @@ -11,11 +11,10 @@ use syn::{Ident, Result as SynResult, Token}; #[cfg(feature = "dispatch_tree")] use crate::COMPILE_TIME_DISPATCHERS; -use crate::DEFAULT_PROGRAM_NAME; enum DispatcherChainInput { Explicit { - group_name: Ident, + group_name: syn::Path, command_name: syn::LitStr, command_struct: Ident, pack: Ident, @@ -29,10 +28,10 @@ enum DispatcherChainInput { impl Parse for DispatcherChainInput { fn parse(input: ParseStream) -> SynResult<Self> { - let lookahead = input.lookahead1(); - - if lookahead.peek(Ident) && input.peek2(Token![,]) && input.peek3(syn::LitStr) { - let group_name = input.parse()?; + if (input.peek(Ident) || input.peek(Token![crate])) + && (input.peek2(Token![::]) || input.peek2(Token![,])) + { + let group_name = input.parse::<syn::Path>()?; input.parse::<Token![,]>()?; let command_name = input.parse()?; input.parse::<Token![,]>()?; @@ -46,7 +45,7 @@ impl Parse for DispatcherChainInput { command_struct, pack, }) - } else if lookahead.peek(syn::LitStr) { + } else if input.peek(syn::LitStr) { // Default format: "command_name", CommandStruct => ChainStruct let command_name = input.parse()?; input.parse::<Token![,]>()?; @@ -60,7 +59,7 @@ impl Parse for DispatcherChainInput { pack, }) } else { - Err(lookahead.error()) + Err(input.lookahead1().error()) } } } @@ -77,23 +76,29 @@ pub fn dispatcher(input: TokenStream) -> TokenStream { let dispatcher_input = syn::parse_macro_input!(input as DispatcherChainInput); // Determine if we're using default or explicit group - let (group_name, command_name, command_struct, pack, use_default) = match dispatcher_input { + let (command_name, command_struct, pack, _use_default, group_path) = match dispatcher_input { DispatcherChainInput::Explicit { group_name, command_name, command_struct, pack, - } => (group_name, command_name, command_struct, pack, false), + } => ( + command_name, + command_struct, + pack, + false, + quote! { #group_name }, + ), DispatcherChainInput::Default { command_name, command_struct, pack, } => ( - Ident::new(DEFAULT_PROGRAM_NAME, proc_macro2::Span::call_site()), command_name, command_struct, pack, true, + crate::default_program_path(), ), }; @@ -104,22 +109,13 @@ pub fn dispatcher(input: TokenStream) -> TokenStream { let dispatch_tree_entry = get_dispatch_tree_entry(&command_name_str, &command_struct, &pack); let expanded = { - let program_ident = if use_default { - Ident::new(DEFAULT_PROGRAM_NAME, proc_macro2::Span::call_site()) - } else { - group_name.clone() - }; - let program_path = if use_default { - crate::default_program_path() - } else { - quote! { #group_name } - }; + let program_path = group_path; quote! { #[derive(Debug, Default)] pub struct #command_struct; - ::mingling::macros::pack!(#program_ident, #pack = Vec<String>); + ::mingling::macros::pack!(#program_path, #pack = Vec<String>); #comp_entry #dispatch_tree_entry diff --git a/mingling_macros/src/dispatcher_clap.rs b/mingling_macros/src/dispatcher_clap.rs index b32883e..bb40404 100644 --- a/mingling_macros/src/dispatcher_clap.rs +++ b/mingling_macros/src/dispatcher_clap.rs @@ -65,7 +65,7 @@ enum DispatcherClapInput { }, /// `(Program, "cmd", Disp, ...)` Explicit { - group_name: Ident, + group_name: syn::Path, command_name: LitStr, dispatcher_struct: Ident, options: ClapOptions, @@ -76,9 +76,11 @@ impl Parse for DispatcherClapInput { fn parse(input: ParseStream) -> syn::Result<Self> { let lookahead = input.lookahead1(); - if lookahead.peek(Ident) && input.peek2(Token![,]) && input.peek3(syn::LitStr) { + if (input.peek(Ident) || input.peek(Token![crate])) + && (input.peek2(Token![::]) || input.peek2(Token![,])) + { // Explicit format: Program, "cmd", Disp, ... - let group_name: Ident = input.parse()?; + let group_name: syn::Path = input.parse()?; input.parse::<Token![,]>()?; let command_name: LitStr = input.parse()?; input.parse::<Token![,]>()?; @@ -132,44 +134,35 @@ pub fn dispatcher_clap_attr(attr: TokenStream, item: TokenStream) -> TokenStream let struct_name = &input_struct.ident; // Determine the program name and other fields - let (command_name_str, dispatcher_struct, options, program_ident, program_path) = - match &attr_input { - DispatcherClapInput::Default { - command_name, - dispatcher_struct, - options, - } => { - let path = crate::default_program_path(); - ( - command_name.value(), - dispatcher_struct.clone(), - ClapOptions { - error_struct: options.error_struct.clone(), - help_enabled: options.help_enabled, - }, - Ident::new(DEFAULT_PROGRAM_NAME, proc_macro2::Span::call_site()), - path, - ) - } - DispatcherClapInput::Explicit { - group_name, - command_name, - dispatcher_struct, - options, - } => { - let path = quote! { #group_name }; - ( - command_name.value(), - dispatcher_struct.clone(), - ClapOptions { - error_struct: options.error_struct.clone(), - help_enabled: options.help_enabled, - }, - group_name.clone(), - path, - ) - } - }; + let (command_name_str, dispatcher_struct, options, program_path) = match &attr_input { + DispatcherClapInput::Default { + command_name, + dispatcher_struct, + options, + } => ( + command_name.value(), + dispatcher_struct.clone(), + ClapOptions { + error_struct: options.error_struct.clone(), + help_enabled: options.help_enabled, + }, + crate::default_program_path(), + ), + DispatcherClapInput::Explicit { + group_name, + command_name, + dispatcher_struct, + options, + } => ( + command_name.value(), + dispatcher_struct.clone(), + ClapOptions { + error_struct: options.error_struct.clone(), + help_enabled: options.help_enabled, + }, + quote! { #group_name }, + ), + }; // Generate the `begin` method body let begin_body = if let Some(ref error_struct) = options.error_struct { @@ -198,7 +191,7 @@ pub fn dispatcher_clap_attr(attr: TokenStream, item: TokenStream) -> TokenStream // Generate the error pack type let error_pack = options.error_struct.as_ref().map(|error_struct| { quote! { - ::mingling::macros::pack!(#program_ident, #error_struct = String); + ::mingling::macros::pack!(#program_path, #error_struct = String); } }); diff --git a/mingling_macros/src/help.rs b/mingling_macros/src/help.rs index c89aad2..6667ae0 100644 --- a/mingling_macros/src/help.rs +++ b/mingling_macros/src/help.rs @@ -158,7 +158,7 @@ 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 that we have exactly two elements + // Check if there are exactly two elements if input_parsed.len() != 2 { return syn::Error::new( input_parsed.span(), diff --git a/mingling_macros/src/pack.rs b/mingling_macros/src/pack.rs index 1c0bb7b..657f1bb 100644 --- a/mingling_macros/src/pack.rs +++ b/mingling_macros/src/pack.rs @@ -3,11 +3,9 @@ use quote::quote; use syn::parse::{Parse, ParseStream}; use syn::{Ident, Result as SynResult, Token, Type}; -use crate::DEFAULT_PROGRAM_NAME; - enum PackInput { Explicit { - group_name: Ident, + group_name: syn::Path, type_name: Ident, inner_type: Type, }, @@ -19,12 +17,18 @@ enum PackInput { impl Parse for PackInput { fn parse(input: ParseStream) -> SynResult<Self> { - // Try to parse as explicit format first: GroupName, TypeName = InnerType - let lookahead = input.lookahead1(); - - if lookahead.peek(Ident) && input.peek2(Token![,]) { - // Explicit format: GroupName, TypeName = InnerType - let group_name = input.parse()?; + // Look ahead to determine format: + // - `Path, TypeName = InnerType` → Explicit + // - `TypeName = InnerType` → Default + // + // Both start with an ident. We peek at the second token: + // if it's a `,` or `::`, it's explicit; if it's `=`, it's default. + + if (input.peek(Ident) || input.peek(Token![crate])) + && (input.peek2(Token![,]) || input.peek2(Token![::])) + { + // Explicit format: Path, TypeName = InnerType + let group_name = input.parse::<syn::Path>()?; input.parse::<Token![,]>()?; let type_name = input.parse()?; input.parse::<Token![=]>()?; @@ -35,7 +39,7 @@ impl Parse for PackInput { type_name, inner_type, }) - } else if lookahead.peek(Ident) && input.peek2(Token![=]) { + } else if input.peek(Ident) && input.peek2(Token![=]) { // Default format: TypeName = InnerType let type_name = input.parse()?; input.parse::<Token![=]>()?; @@ -46,14 +50,12 @@ impl Parse for PackInput { inner_type, }) } else { - Err(lookahead.error()) + Err(input.lookahead1().error()) } } } pub fn pack(input: TokenStream) -> TokenStream { - let default_program_path = crate::default_program_path(); - // Parse the input let pack_input = syn::parse_macro_input!(input as PackInput); @@ -63,16 +65,11 @@ pub fn pack(input: TokenStream) -> TokenStream { group_name, type_name, inner_type, - } => (group_name, type_name, inner_type, false), + } => (quote! { #group_name }, type_name, inner_type, false), PackInput::Default { type_name, inner_type, - } => ( - Ident::new(DEFAULT_PROGRAM_NAME, proc_macro2::Span::call_site()), - type_name, - inner_type, - true, - ), + } => (crate::default_program_path(), type_name, inner_type, true), }; // Generate the struct definition @@ -211,13 +208,13 @@ pub fn pack(input: TokenStream) -> TokenStream { #default_impl #register_impl - impl Into<mingling::AnyOutput<#default_program_path>> for #type_name { - fn into(self) -> mingling::AnyOutput<#default_program_path> { + impl Into<mingling::AnyOutput<#group_name>> for #type_name { + fn into(self) -> mingling::AnyOutput<#group_name> { mingling::AnyOutput::new(self) } } - impl From<#type_name> for mingling::ChainProcess<#default_program_path> { + impl From<#type_name> for mingling::ChainProcess<#group_name> { fn from(value: #type_name) -> Self { mingling::AnyOutput::new(value).route_chain() } @@ -225,19 +222,19 @@ pub fn pack(input: TokenStream) -> TokenStream { impl #type_name { /// Converts the wrapper type into a `ChainProcess` for chaining operations. - pub fn to_chain(self) -> mingling::ChainProcess<#default_program_path> { + pub fn to_chain(self) -> mingling::ChainProcess<#group_name> { mingling::AnyOutput::new(self).route_chain() } /// Converts the wrapper type into a `ChainProcess` for rendering operations. - pub fn to_render(self) -> mingling::ChainProcess<#default_program_path> { + pub fn to_render(self) -> mingling::ChainProcess<#group_name> { mingling::AnyOutput::new(self).route_renderer() } } - impl ::mingling::Groupped<#default_program_path> for #type_name { - fn member_id() -> #default_program_path { - #default_program_path::#type_name + impl ::mingling::Groupped<#group_name> for #type_name { + fn member_id() -> #group_name { + #group_name::#type_name } } } diff --git a/mingling_macros/src/program_setup.rs b/mingling_macros/src/program_setup.rs index dd32581..383d632 100644 --- a/mingling_macros/src/program_setup.rs +++ b/mingling_macros/src/program_setup.rs @@ -53,8 +53,8 @@ pub fn setup_attr(attr: TokenStream, item: TokenStream) -> TokenStream { let (program_name, use_crate_prefix) = if attr.is_empty() { (crate::default_program_path(), true) } else { - let ident: Ident = parse_macro_input!(attr as Ident); - (quote! { #ident }, false) + let path: syn::Path = parse_macro_input!(attr as syn::Path); + (quote! { #path }, false) }; // Parse the function item |
