diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-06-20 01:54:57 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-06-20 01:54:57 +0800 |
| commit | fcfe66875f46e8652170fd190416d796ae30aabc (patch) | |
| tree | 2af968ac8cabcb06337e36c35133313781cd1071 | |
| parent | 78330940bd0fcab6ffbb8b930a1ae88d313a9437 (diff) | |
Remove all explicit program name modes from macrosremoved-shit
| -rw-r--r-- | CHANGELOG.md | 26 | ||||
| -rw-r--r-- | mingling/src/lib.rs | 16 | ||||
| -rw-r--r-- | mingling_macros/src/chain.rs | 45 | ||||
| -rw-r--r-- | mingling_macros/src/dispatcher.rs | 190 | ||||
| -rw-r--r-- | mingling_macros/src/dispatcher_clap.rs | 121 | ||||
| -rw-r--r-- | mingling_macros/src/group_impl.rs | 48 | ||||
| -rw-r--r-- | mingling_macros/src/groupped.rs | 24 | ||||
| -rw-r--r-- | mingling_macros/src/lib.rs | 124 | ||||
| -rw-r--r-- | mingling_macros/src/pack.rs | 144 | ||||
| -rw-r--r-- | mingling_macros/src/program_setup.rs | 57 | ||||
| -rw-r--r-- | mingling_macros/src/renderer.rs | 14 |
11 files changed, 209 insertions, 600 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index f32bb60..314c56a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -190,6 +190,30 @@ This macro is only available with the `extra_macros` feature. #### **BREAKING CHANGES** (API CHANGES): +--- + +**IMPORTANT**: **Breaking: Remove All Explicit Program Name Modes** + +**All macros no longer accept a custom program path.** The program name is now always `crate::ThisProgram`, determined by `gen_program!()`. + +The following explicit syntaxes are **removed**: + +| Macro | Removed syntax | +|---|---| +| `pack!` | `pack!(MyProgram, Type = Inner)` → only `pack!(Type = Inner)` | +| `group!` | `group!(MyProgram, Type)` → only `group!(Type)` | +| `#[derive(Groupped)]` | `#[group(MyProgram)]` attribute | +| `#[chain]` | `#[chain(MyProgram)]` argument | +| `#[renderer]` | `#[renderer(MyProgram)]` argument | +| `dispatcher!` | `dispatcher!(MyProgram, "cmd", CMD => Entry)` | +| `#[dispatcher_clap]` | `#[dispatcher_clap(MyProgram, "cmd", Disp)]` | +| `#[program_setup]` | `#[program_setup(MyProgram)]` argument | +| `gen_program!` | `gen_program!(MyProgram)` → only `gen_program!()` | + +> **Tradeoff Rationale** — Removing explicit program names is a sacrifice of flexibility in exchange for reduced development and maintenance complexity. In practice, no use case has emerged that genuinely requires a custom program name — all real-world programs can be expressed with the default `ThisProgram`. Keeping the parameter in every macro would add ongoing documentation, testing, and cognitive overhead that is not justified by current needs. + +--- + 1. **\[core\]** Changed the signature of `ProgramSetup::setup` from `fn setup(&mut self, program: &mut Program<C>) -> S` to `fn setup(self, program: &mut Program<C>)`, consuming `self` instead of taking a mutable reference. Correspondingly, `Program::with_setup` now accepts `S` by value (`&mut self, setup: S`) instead of by mutable reference (`&mut self, setup: &mut S`). 2. **\[core\]** Consolidated resource naming for `ExitCode` and `REPL`: @@ -219,7 +243,7 @@ use mingling::{res::ResExitCode, res::ResREPL}; AnyOutput::new(self).route_renderer() } } - + // After (provided by Groupped trait default methods): // just ensure Groupped is implemented — to_chain() and to_render() // are automatically available diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs index a295af9..70b69bc 100644 --- a/mingling/src/lib.rs +++ b/mingling/src/lib.rs @@ -106,11 +106,11 @@ pub mod macros { #[cfg(feature = "extra_macros")] pub use mingling_macros::pack_err; #[cfg(feature = "comp")] - /// Internal macro for '`gen_program`' used to finally generate the completion structure + #[doc(hidden)] pub use mingling_macros::program_comp_gen; - /// Internal macro for '`gen_program`' used to finally generate the fallback + #[doc(hidden)] pub use mingling_macros::program_fallback_gen; - /// Internal macro for '`gen_program`' used to finally generate the program + #[doc(hidden)] pub use mingling_macros::program_final_gen; /// Used to generate program setup #[cfg(feature = "extra_macros")] @@ -119,15 +119,15 @@ pub mod macros { pub use mingling_macros::r_print; /// Used to print content with a newline within a `Renderer` context pub use mingling_macros::r_println; - /// Used to register a chain + #[doc(hidden)] pub use mingling_macros::register_chain; - /// Used to register a dispatcher for `dispatch_tree` feature + #[doc(hidden)] pub use mingling_macros::register_dispatcher; - /// Used to register a help + #[doc(hidden)] pub use mingling_macros::register_help; - /// Used to register a renderer + #[doc(hidden)] pub use mingling_macros::register_renderer; - /// Used to register a type into the context + #[doc(hidden)] pub use mingling_macros::register_type; /// Used to generate a struct implementing the `Renderer` trait via a method pub use mingling_macros::renderer; diff --git a/mingling_macros/src/chain.rs b/mingling_macros/src/chain.rs index b0ea8ae..fb5999a 100644 --- a/mingling_macros/src/chain.rs +++ b/mingling_macros/src/chain.rs @@ -9,20 +9,6 @@ use quote::{ToTokens, quote}; use syn::spanned::Spanned; use syn::{Ident, ItemFn, Pat, ReturnType, Signature, Type, TypePath, parse_macro_input}; -/// Parses the `#[chain(...)]` attribute arguments. -/// -/// Returns: -/// - `program_path`: the token stream representing the program type -/// - `use_crate_prefix`: whether to use the default crate-defined program path -fn parse_chain_attr_args(attr: TokenStream) -> (proc_macro2::TokenStream, bool) { - if attr.is_empty() { - (crate::default_program_path(), true) - } else { - let path: syn::Path = syn::parse(attr).expect("#[chain(..)] argument must be a path"); - (quote! { #path }, false) - } -} - /// Validates that the return type of the function is `Next`. /// Checks whether the return type is `()` (unit). fn is_unit_return_type(sig: &Signature) -> bool { @@ -226,18 +212,10 @@ fn generate_struct_and_impl( struct_name: &Ident, previous_type: &TypePath, previous_type_str: &proc_macro2::TokenStream, - group_name: &proc_macro2::TokenStream, program_type: &proc_macro2::TokenStream, - use_crate_prefix: bool, proc_fn: &proc_macro2::TokenStream, origin_proc_fn: &proc_macro2::TokenStream, ) -> proc_macro2::TokenStream { - let chain_type = if use_crate_prefix { - program_type - } else { - group_name - }; - quote! { #(#fn_attrs)* #[doc(hidden)] @@ -246,7 +224,7 @@ fn generate_struct_and_impl( ::mingling::macros::register_chain!(#previous_type_str, #struct_name); - impl ::mingling::Chain<#chain_type> for #struct_name { + impl ::mingling::Chain<#program_type> for #struct_name { type Previous = #previous_type; #proc_fn @@ -284,8 +262,15 @@ fn reject_mut_in_async(resources: &[ResourceInjection]) -> Result<(), proc_macro } pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream { - // Parse attribute arguments - let (group_name, use_crate_prefix) = parse_chain_attr_args(attr); + // Reject non-empty attribute arguments; #[chain] must be bare + if !attr.is_empty() { + return syn::Error::new( + attr.into_iter().next().unwrap().span().into(), + "#[chain] does not accept arguments", + ) + .to_compile_error() + .into(); + } // Parse the function item let input_fn = parse_macro_input!(item as ItemFn); @@ -340,12 +325,8 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream { ); let struct_name = Ident::new(&internal_name, fn_name.span()); - // Determine the program type for the return type - let program_type = if use_crate_prefix { - crate::default_program_path() - } else { - group_name.clone() - }; + // Always use the default crate-defined program path + let program_type = crate::default_program_path(); // Generate the `proc` function let proc_fn = generate_proc_fn( @@ -386,9 +367,7 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream { &struct_name, &previous_type, &previous_type_str, - &group_name, &program_type, - use_crate_prefix, &proc_fn, &origin_proc_fn, ); diff --git a/mingling_macros/src/dispatcher.rs b/mingling_macros/src/dispatcher.rs index 6bf10f1..51dce95 100644 --- a/mingling_macros/src/dispatcher.rs +++ b/mingling_macros/src/dispatcher.rs @@ -8,14 +8,6 @@ use syn::{Attribute, Ident, LitStr, Result as SynResult, Token}; use crate::COMPILE_TIME_DISPATCHERS; enum DispatcherChainInput { - Explicit { - cmd_attrs: Vec<Attribute>, - entry_attrs: Vec<Attribute>, - group_name: syn::Path, - command_name: syn::LitStr, - command_struct: Ident, - pack: Ident, - }, Default { cmd_attrs: Vec<Attribute>, entry_attrs: Vec<Attribute>, @@ -35,27 +27,7 @@ impl Parse for DispatcherChainInput { // Collect outer attributes for the CMD struct let cmd_attrs = input.call(Attribute::parse_outer)?; - 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![,]>()?; - let command_struct = input.parse()?; - input.parse::<Token![=>]>()?; - let entry_attrs = input.call(Attribute::parse_outer)?; - let pack = input.parse()?; - - Ok(DispatcherChainInput::Explicit { - cmd_attrs, - entry_attrs, - group_name, - command_name, - command_struct, - pack, - }) - } else if input.peek(syn::LitStr) { + if input.peek(syn::LitStr) { // Parse the command name string first let command_name: LitStr = input.parse()?; @@ -97,12 +69,8 @@ impl Parse for DispatcherChainInput { } } -// NOTICE: This implementation contains significant code duplication between the explicit -// and default cases in both `dispatcher_chain` and `dispatcher_render` functions. -// The logic for handling default vs explicit group names and generating the appropriate -// code should be extracted into common helper functions to reduce redundancy. -// Additionally, the token stream generation patterns are nearly identical between -// the two main functions and could benefit from refactoring. +// NOTICE: The token stream generation patterns in `dispatcher_chain` and `dispatcher_render` +// are nearly identical and could benefit from refactoring into common helper functions. #[allow(clippy::too_many_lines)] pub fn dispatcher(input: TokenStream) -> TokenStream { @@ -110,94 +78,36 @@ pub fn dispatcher(input: TokenStream) -> TokenStream { let dispatcher_input = syn::parse_macro_input!(input as DispatcherChainInput); #[cfg(not(feature = "extra_macros"))] - let (command_name, command_struct, pack, cmd_attrs, entry_attrs, _use_default, group_path) = - match dispatcher_input { - DispatcherChainInput::Explicit { - cmd_attrs, - entry_attrs, - group_name, - command_name, - command_struct, - pack, - } => ( - command_name, - command_struct, - pack, - cmd_attrs, - entry_attrs, - false, - quote! { #group_name }, - ), - DispatcherChainInput::Default { - cmd_attrs, - entry_attrs, - command_name, - command_struct, - pack, - } => ( - command_name, - command_struct, - pack, - cmd_attrs, - entry_attrs, - true, - crate::default_program_path(), - ), - }; + let (command_name, command_struct, pack, cmd_attrs, entry_attrs) = match dispatcher_input { + DispatcherChainInput::Default { + cmd_attrs, + entry_attrs, + command_name, + command_struct, + pack, + } => (command_name, command_struct, pack, cmd_attrs, entry_attrs), + }; #[cfg(feature = "extra_macros")] - let (command_name, command_struct, pack, cmd_attrs, entry_attrs, _use_default, group_path) = - match dispatcher_input { - DispatcherChainInput::Explicit { - cmd_attrs, - entry_attrs, - group_name, - command_name, - command_struct, - pack, - } => ( - command_name, - command_struct, - pack, - cmd_attrs, - entry_attrs, - false, - quote! { #group_name }, - ), - DispatcherChainInput::Default { - cmd_attrs, - entry_attrs, - command_name, - command_struct, - pack, - } => ( - command_name, - command_struct, - pack, - cmd_attrs, - entry_attrs, - true, - crate::default_program_path(), - ), - DispatcherChainInput::Auto { - cmd_attrs, - command_name, - } => { - let command_name_str = command_name.value(); - let pascal = dotted_to_pascal_case(&command_name_str); - let command_struct = Ident::new(&format!("CMD{pascal}"), command_name.span()); - let pack = Ident::new(&format!("Entry{pascal}"), command_name.span()); - ( - command_name, - command_struct, - pack, - cmd_attrs, - Vec::new(), - true, - crate::default_program_path(), - ) - } - }; + let (command_name, command_struct, pack, cmd_attrs, entry_attrs) = match dispatcher_input { + DispatcherChainInput::Default { + cmd_attrs, + entry_attrs, + command_name, + command_struct, + pack, + } => (command_name, command_struct, pack, cmd_attrs, entry_attrs), + DispatcherChainInput::Auto { + cmd_attrs, + command_name, + } => { + let command_name_str = command_name.value(); + let pascal = dotted_to_pascal_case(&command_name_str); + let command_struct = Ident::new(&format!("CMD{pascal}"), command_name.span()); + let pack = Ident::new(&format!("Entry{pascal}"), command_name.span()); + (command_name, command_struct, pack, cmd_attrs, Vec::new()) + } + }; let command_name_str = command_name.value(); @@ -205,30 +115,28 @@ pub fn dispatcher(input: TokenStream) -> TokenStream { let dispatch_tree_entry = get_dispatch_tree_entry(&command_name_str, &command_struct, &pack); - let expanded = { - let program_path = group_path; + let program_type = crate::default_program_path(); - quote! { - #(#cmd_attrs)* - #[derive(Debug, Default)] - pub struct #command_struct; + let expanded = quote! { + #(#cmd_attrs)* + #[derive(Debug, Default)] + pub struct #command_struct; - ::mingling::macros::pack!(#(#entry_attrs)* #program_path, #pack = Vec<String>); + ::mingling::macros::pack!(#(#entry_attrs)* #pack = Vec<String>); - #comp_entry - #dispatch_tree_entry + #comp_entry + #dispatch_tree_entry - impl ::mingling::Dispatcher<#program_path> for #command_struct { - fn node(&self) -> ::mingling::Node { - ::mingling::macros::node!(#command_name_str) - } - fn begin(&self, args: Vec<String>) -> ::mingling::ChainProcess<#program_path> { - use ::mingling::Groupped; - #pack::new(args).to_chain() - } - fn clone_dispatcher(&self) -> Box<dyn ::mingling::Dispatcher<#program_path>> { - Box::new(#command_struct) - } + impl ::mingling::Dispatcher<#program_type> for #command_struct { + fn node(&self) -> ::mingling::Node { + ::mingling::macros::node!(#command_name_str) + } + fn begin(&self, args: Vec<String>) -> ::mingling::ChainProcess<#program_type> { + use ::mingling::Groupped; + #pack::new(args).to_chain() + } + fn clone_dispatcher(&self) -> Box<dyn ::mingling::Dispatcher<#program_type>> { + Box::new(#command_struct) } } }; diff --git a/mingling_macros/src/dispatcher_clap.rs b/mingling_macros/src/dispatcher_clap.rs index 34d146c..1c9db4b 100644 --- a/mingling_macros/src/dispatcher_clap.rs +++ b/mingling_macros/src/dispatcher_clap.rs @@ -61,74 +61,34 @@ impl Parse for ClapOptions { } /// Input for the dispatcher_clap attribute -enum DispatcherClapInput { +struct DispatcherClapInput { /// `("cmd", Disp, ...)` - Default { - command_name: LitStr, - dispatcher_struct: Ident, - options: ClapOptions, - }, - /// `(Program, "cmd", Disp, ...)` - Explicit { - group_name: syn::Path, - command_name: LitStr, - dispatcher_struct: Ident, - options: ClapOptions, - }, + command_name: LitStr, + dispatcher_struct: Ident, + options: ClapOptions, } impl Parse for DispatcherClapInput { fn parse(input: ParseStream) -> syn::Result<Self> { - let lookahead = input.lookahead1(); + // Format: "cmd", Disp, ... + let command_name: LitStr = input.parse()?; + input.parse::<Token![,]>()?; + let dispatcher_struct: Ident = input.parse()?; - if (input.peek(Ident) || input.peek(Token![crate])) - && (input.peek2(Token![::]) || input.peek2(Token![,])) - { - // Explicit format: Program, "cmd", Disp, ... - let group_name: syn::Path = input.parse()?; - input.parse::<Token![,]>()?; - let command_name: LitStr = input.parse()?; - input.parse::<Token![,]>()?; - let dispatcher_struct: Ident = input.parse()?; - - let options = if input.is_empty() { - ClapOptions { - error_struct: None, - help_enabled: false, - } - } else { - input.parse::<ClapOptions>()? - }; - - Ok(DispatcherClapInput::Explicit { - group_name, - command_name, - dispatcher_struct, - options, - }) - } else if lookahead.peek(syn::LitStr) { - // Default format: "cmd", Disp, ... - let command_name: LitStr = input.parse()?; - input.parse::<Token![,]>()?; - let dispatcher_struct: Ident = input.parse()?; - - let options = if input.is_empty() { - ClapOptions { - error_struct: None, - help_enabled: false, - } - } else { - input.parse::<ClapOptions>()? - }; - - Ok(DispatcherClapInput::Default { - command_name, - dispatcher_struct, - options, - }) + let options = if input.is_empty() { + ClapOptions { + error_struct: None, + help_enabled: false, + } } else { - Err(lookahead.error()) - } + input.parse::<ClapOptions>()? + }; + + Ok(DispatcherClapInput { + command_name, + dispatcher_struct, + options, + }) } } @@ -138,36 +98,11 @@ pub fn dispatcher_clap_attr(attr: TokenStream, item: TokenStream) -> TokenStream let input_struct = parse_macro_input!(item as ItemStruct); let struct_name = &input_struct.ident; - // Determine the program name and other fields - 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 }, - ), - }; + let program_path = crate::default_program_path(); + + let command_name_str = attr_input.command_name.value(); + let dispatcher_struct = &attr_input.dispatcher_struct; + let options = &attr_input.options; // Generate the `begin` method body let begin_body = if let Some(ref error_struct) = options.error_struct { @@ -196,7 +131,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_path, #error_struct = String); + ::mingling::macros::pack!(#error_struct = String); } }); @@ -233,7 +168,7 @@ pub fn dispatcher_clap_attr(attr: TokenStream, item: TokenStream) -> TokenStream }; let dispatch_tree_entry = - get_dispatch_tree_entry(&command_name_str, &dispatcher_struct, &struct_name); + get_dispatch_tree_entry(&command_name_str, dispatcher_struct, &struct_name); let expanded = quote! { // Keep the original struct definition diff --git a/mingling_macros/src/group_impl.rs b/mingling_macros/src/group_impl.rs index 499fd1c..a7bc84f 100644 --- a/mingling_macros/src/group_impl.rs +++ b/mingling_macros/src/group_impl.rs @@ -1,52 +1,25 @@ use proc_macro::TokenStream; use quote::quote; use syn::parse::{Parse, ParseStream}; -use syn::{Ident, Path, Result as SynResult, Token, TypePath}; +use syn::{Ident, Result as SynResult, TypePath}; /// Input for the `group!` macro /// /// # Syntax /// /// ```rust,ignore -/// // Explicit mode: specify both program path and type path -/// group!(crate::ThisProgram, std::io::Error); -/// -/// // Implicit mode: only type path, uses default `crate::ThisProgram` as program +/// /// Only a type path — uses default `crate::ThisProgram` as program /// group!(std::io::Error); /// group!(ParseIntError); /// ``` -enum GroupInput { - Explicit { - program_path: Path, - type_path: TypePath, - }, - Implicit { - type_path: TypePath, - }, +struct GroupInput { + type_path: TypePath, } impl Parse for GroupInput { fn parse(input: ParseStream) -> SynResult<Self> { - // Parse the first path (could be program path or type path) - let first_path: Path = input.parse()?; - - // If followed by a comma, it's explicit mode: Path, TypePath - if input.peek(Token![,]) { - input.parse::<Token![,]>()?; - let type_path: TypePath = input.parse()?; - Ok(GroupInput::Explicit { - program_path: first_path, - type_path, - }) - } else { - // Otherwise it's implicit mode: just a type path - Ok(GroupInput::Implicit { - type_path: TypePath { - qself: None, - path: first_path, - }, - }) - } + let type_path: TypePath = input.parse()?; + Ok(GroupInput { type_path }) } } @@ -96,14 +69,9 @@ fn gen_type_use(type_path: &TypePath) -> proc_macro2::TokenStream { pub fn group_macro(input: TokenStream) -> TokenStream { let input = syn::parse_macro_input!(input as GroupInput); + let type_path = input.type_path; - let (program_path, type_path) = match input { - GroupInput::Explicit { - program_path, - type_path, - } => (quote! { #program_path }, type_path), - GroupInput::Implicit { type_path } => (crate::default_program_path(), type_path), - }; + let program_path = crate::default_program_path(); // Use the type's simple name as the enum variant identifier let type_name = type_simple_name(&type_path); diff --git a/mingling_macros/src/groupped.rs b/mingling_macros/src/groupped.rs index 03f0b9c..1459522 100644 --- a/mingling_macros/src/groupped.rs +++ b/mingling_macros/src/groupped.rs @@ -1,29 +1,13 @@ use proc_macro::TokenStream; use quote::quote; -use syn::{Attribute, DeriveInput, Ident, parse_macro_input}; - -/// Parses the `#[group(...)]` attribute to extract the group type -fn parse_group_attribute(attrs: &[Attribute]) -> Option<Ident> { - for attr in attrs { - if attr.path().is_ident("group") - && let Ok(meta) = attr.parse_args::<syn::Meta>() - && let syn::Meta::Path(path) = meta - && let Some(segment) = path.segments.last() - { - return Some(segment.ident.clone()); - } - } - None -} +use syn::{DeriveInput, Ident, parse_macro_input}; pub fn derive_groupped(input: TokenStream) -> TokenStream { // Parse the input struct/enum let input = parse_macro_input!(input as DeriveInput); let struct_name = input.ident; - // Parse attributes to find #[group(...)] - let group_ident: proc_macro2::TokenStream = parse_group_attribute(&input.attrs) - .map_or_else(crate::default_program_path, |ident| quote! { #ident }); + let group_ident: proc_macro2::TokenStream = crate::default_program_path(); let any_output_convert_impls = proc_macro2::TokenStream::from(build_any_output_convert_impls(&struct_name, &group_ident)); @@ -50,9 +34,7 @@ pub fn derive_groupped_serialize(input: TokenStream) -> TokenStream { let input_parsed = parse_macro_input!(input as DeriveInput); let struct_name = input_parsed.ident.clone(); - // Parse attributes to find #[group(...)] - let group_ident: proc_macro2::TokenStream = parse_group_attribute(&input_parsed.attrs) - .map_or_else(crate::default_program_path, |ident| quote! { #ident }); + let group_ident: proc_macro2::TokenStream = crate::default_program_path(); let any_output_convert_impls = proc_macro2::TokenStream::from(build_any_output_convert_impls(&struct_name, &group_ident)); diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index bb296ef..e774335 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -135,7 +135,6 @@ //! ``` use proc_macro::TokenStream; -use proc_macro2::Ident; use quote::quote; use std::collections::BTreeSet; use std::sync::Mutex; @@ -169,13 +168,6 @@ mod res_injection; #[cfg(feature = "comp")] mod suggest; -pub(crate) const DEFAULT_PROGRAM_NAME: &str = "ThisProgram"; - -#[allow(dead_code)] -pub(crate) fn default_program_ident() -> Ident { - Ident::new(DEFAULT_PROGRAM_NAME, proc_macro2::Span::call_site()) -} - pub(crate) fn default_program_path() -> proc_macro2::TokenStream { quote::quote! { crate::ThisProgram } } @@ -240,16 +232,13 @@ pub(crate) fn check_single_segment_type( /// /// # Syntax /// -/// Two forms are supported: -/// /// ```rust,ignore -/// // Explicit mode — specify both program path and outside-type: -/// group!(crate::ThisProgram, std::io::Error); -/// -/// // Implicit mode — uses default `crate::ThisProgram` as the program: /// group!(std::io::Error); +/// group!(ParseIntError); /// ``` /// +/// The type is registered under the default program (`crate::ThisProgram`). +/// /// # How it works /// /// The macro generates a module containing: @@ -267,9 +256,6 @@ pub(crate) fn check_single_segment_type( /// /// // Register std::io::Error as a group member /// group!(std::io::Error); -/// -/// // With explicit program path: -/// group!(crate::MyProgram, serde_json::Error); /// ``` /// /// After expansion, the type can be used in chains and renderers like any @@ -722,18 +708,11 @@ pub fn r_println(input: TokenStream) -> TokenStream { /// # Syntax /// /// ```rust,ignore -/// // Default program (ThisProgram): /// #[chain] /// fn my_step(prev: InputType) -> Next { /// // transform `prev`... /// OutputType::new(result) /// } -/// -/// // Explicit program name: -/// #[chain(MyProgram)] -/// fn my_step(prev: InputType) -> Next { -/// // ... -/// } /// ``` /// /// # Resource Injection @@ -891,7 +870,6 @@ pub fn chain(attr: TokenStream, item: TokenStream) -> TokenStream { /// # Syntax /// /// ```rust,ignore -/// // Default program (ThisProgram): /// #[renderer] /// fn render_my_type(prev: MyType) { /// r_println!("Output: {:?}", *prev); @@ -1004,17 +982,10 @@ pub fn completion(attr: TokenStream, item: TokenStream) -> TokenStream { /// # Syntax /// /// ```rust,ignore -/// // Default program (ThisProgram): /// #[program_setup] /// fn setup_my_program(program: &mut Program<ThisProgram>) { /// program.stdout_setting.render_output = false; /// } -/// -/// // Explicit program name: -/// #[program_setup(MyProgram)] -/// fn setup_my_program(program: &mut Program<MyProgram>) { -/// // ... -/// } /// ``` /// /// # Example @@ -1063,11 +1034,6 @@ pub fn program_setup(attr: TokenStream, item: TokenStream) -> TokenStream { /// #[derive(clap::Parser)] /// #[dispatcher_clap("cmd", Disp, error = ParseError, help = true)] /// struct CmdEntry { /* ... */ } -/// -/// // With explicit program name: -/// #[derive(clap::Parser)] -/// #[dispatcher_clap(MyProgram, "cmd", Disp)] -/// struct CmdEntry { /* ... */ } /// ``` /// /// # Example @@ -1257,7 +1223,7 @@ pub fn help(_attr: TokenStream, item: TokenStream) -> TokenStream { /// Derive macro for automatically implementing the `Groupped` trait on a struct. /// /// The `#[derive(Groupped)]` macro: -/// 1. Implements `Groupped<Group>` where the group is specified via `#[group(GroupName)]`. +/// 1. Implements `Groupped<crate::ThisProgram>`. /// 2. Registers the type via `register_type!` so it's included in the program enum. /// 3. Generates `Into<AnyOutput<Group>>` and `Into<ChainProcess<Group>>` conversions. /// 4. Adds `to_chain()` and `to_render()` methods to the struct. @@ -1266,19 +1232,17 @@ pub fn help(_attr: TokenStream, item: TokenStream) -> TokenStream { /// /// ```rust,ignore /// #[derive(Groupped)] -/// #[group(MyProgram)] // optional; defaults to `ThisProgram` /// struct MyStruct { -/// field: String, +/// // ... /// } /// ``` /// /// # Example /// /// ```rust,ignore -/// use mingling::{Groupped, macros::{chain, gen_program, renderer, r_println}}; +/// use mingling::macros::Groupped; /// /// #[derive(Groupped)] -/// #[group(ThisProgram)] /// struct Greeting { /// name: String, /// } @@ -1345,7 +1309,6 @@ pub fn derive_enum_tag(input: TokenStream) -> TokenStream { /// /// ```rust,ignore /// #[derive(GrouppedSerialize)] -/// #[group(MyProgram)] /// struct Info { /// name: String, /// age: i32, @@ -1379,11 +1342,7 @@ pub fn derive_groupped_serialize(input: TokenStream) -> TokenStream { /// # Syntax /// /// ```rust,ignore -/// // Default program name (uses `ThisProgram`): /// gen_program!(); -/// -/// // Explicit program name: -/// gen_program!(MyProgram); /// ``` /// /// # What it generates @@ -1420,26 +1379,21 @@ pub fn derive_groupped_serialize(input: TokenStream) -> TokenStream { /// gen_program!(); /// ``` #[proc_macro] -pub fn gen_program(input: TokenStream) -> TokenStream { - let name = read_name(&input); - +pub fn gen_program(_input: TokenStream) -> TokenStream { #[cfg(feature = "comp")] let comp_gen = quote! { - ::mingling::macros::program_comp_gen!(#name); + ::mingling::macros::program_comp_gen!(); }; #[cfg(not(feature = "comp"))] let comp_gen = quote! {}; TokenStream::from(quote! { - // Shit, this feature is unstable - // TODO :: This logic will be implemented when Rust's Impl In Type Alias feature becomes stable - // pub type Next = impl Into<::mingling::ChainProcess<#name>>; - pub type Next = ::mingling::ChainProcess<#name>; + pub type Next = ::mingling::ChainProcess<crate::ThisProgram>; #comp_gen - ::mingling::macros::program_fallback_gen!(#name); - ::mingling::macros::program_final_gen!(#name); + ::mingling::macros::program_fallback_gen!(); + ::mingling::macros::program_final_gen!(); }) } @@ -1457,20 +1411,18 @@ pub fn gen_program(input: TokenStream) -> TokenStream { /// - A `__render_completion` renderer that outputs completion suggestions #[proc_macro] #[cfg(feature = "comp")] -pub fn program_comp_gen(input: TokenStream) -> TokenStream { - let name = read_name(&input); - +pub fn program_comp_gen(_input: TokenStream) -> TokenStream { #[cfg(feature = "async")] let fn_exec_comp = quote! { #[doc(hidden)] - #[::mingling::macros::chain(#name)] + #[::mingling::macros::chain] pub async fn __exec_completion(prev: CompletionContext) -> Next { use ::mingling::Groupped; let read_ctx = ::mingling::ShellContext::try_from(prev.inner); match read_ctx { Ok(ctx) => { - let suggest = ::mingling::CompletionHelper::exec_completion::<#name>(&ctx); + let suggest = ::mingling::CompletionHelper::exec_completion::<crate::ThisProgram>(&ctx); crate::CompletionSuggest::new((ctx, suggest)).to_render() } Err(_) => std::process::exit(1), @@ -1481,14 +1433,14 @@ pub fn program_comp_gen(input: TokenStream) -> TokenStream { #[cfg(not(feature = "async"))] let fn_exec_comp = quote! { #[doc(hidden)] - #[::mingling::macros::chain(#name)] + #[::mingling::macros::chain] pub fn __exec_completion(prev: CompletionContext) -> Next { use ::mingling::Groupped; let read_ctx = ::mingling::ShellContext::try_from(prev.inner); match read_ctx { Ok(ctx) => { - let suggest = ::mingling::CompletionHelper::exec_completion::<#name>(&ctx); + let suggest = ::mingling::CompletionHelper::exec_completion::<crate::ThisProgram>(&ctx); crate::CompletionSuggest::new((ctx, suggest)).to_render() } Err(_) => std::process::exit(1), @@ -1507,11 +1459,9 @@ pub fn program_comp_gen(input: TokenStream) -> TokenStream { let comp_dispatcher = quote! { #[doc(hidden)] mod __internal_completion_mod { - use super::#name; use ::mingling::Groupped; - ::mingling::macros::dispatcher!(#name, "__comp", CMDCompletion => CompletionContext); + ::mingling::macros::dispatcher!("__comp", CMDCompletion => CompletionContext); ::mingling::macros::pack!( - #name, CompletionSuggest = (::mingling::ShellContext, ::mingling::Suggest) ); } @@ -1526,10 +1476,10 @@ pub fn program_comp_gen(input: TokenStream) -> TokenStream { #[allow(unused)] #[doc(hidden)] - #[::mingling::macros::renderer(#name)] + #[::mingling::macros::renderer] pub fn __render_completion(prev: CompletionSuggest) { let (ctx, suggest) = prev.inner; - ::mingling::CompletionHelper::render_suggest::<#name>(ctx, suggest); + ::mingling::CompletionHelper::render_suggest::<crate::ThisProgram>(ctx, suggest); } }; @@ -1630,25 +1580,22 @@ pub fn register_renderer(input: TokenStream) -> TokenStream { /// /// ```rust,ignore /// // Called internally by gen_program!: -/// program_fallback_gen!(ThisProgram); -/// program_fallback_gen!(MyProgram); +/// program_fallback_gen!(); /// ``` /// /// # Generated code equivalent /// /// ```rust,ignore -/// pack!(ProgramName, ErrorRendererNotFound = String); -/// pack!(ProgramName, ErrorDispatcherNotFound = Vec<String>); -/// pack!(ProgramName, ResultEmpty = ()); +/// pack!(ErrorRendererNotFound = String); +/// pack!(ErrorDispatcherNotFound = Vec<String>); +/// pack!(ResultEmpty = ()); /// ``` #[proc_macro] -pub fn program_fallback_gen(input: TokenStream) -> TokenStream { - let name = read_name(&input); - +pub fn program_fallback_gen(_input: TokenStream) -> TokenStream { let expanded = quote! { - ::mingling::macros::pack!(#name, ErrorRendererNotFound = String); - ::mingling::macros::pack!(#name, ErrorDispatcherNotFound = Vec<String>); - ::mingling::macros::pack!(#name, ResultEmpty = ()); + ::mingling::macros::pack!(ErrorRendererNotFound = String); + ::mingling::macros::pack!(ErrorDispatcherNotFound = Vec<String>); + ::mingling::macros::pack!(ResultEmpty = ()); }; TokenStream::from(expanded) } @@ -1673,15 +1620,14 @@ pub fn program_fallback_gen(input: TokenStream) -> TokenStream { /// # Syntax /// /// ```rust,ignore -/// program_final_gen!(ThisProgram); -/// program_final_gen!(MyProgram); +/// program_final_gen!(); /// ``` /// /// # Generated code structure /// /// ```rust,ignore /// #[repr(u8)] -/// pub enum MyProgram { +/// pub enum ThisProgram { /// TypeA, /// TypeB, /// // ... @@ -1710,8 +1656,8 @@ pub fn program_fallback_gen(input: TokenStream) -> TokenStream { /// are poisoned. #[proc_macro] #[allow(clippy::too_many_lines)] -pub fn program_final_gen(input: TokenStream) -> TokenStream { - let name = read_name(&input); +pub fn program_final_gen(_input: TokenStream) -> TokenStream { + let name = syn::Ident::new("ThisProgram", proc_macro2::Span::call_site()); let packed_types = get_global_set(&PACKED_TYPES).lock().unwrap().clone(); @@ -2045,11 +1991,3 @@ pub fn suggest(input: TokenStream) -> TokenStream { pub fn suggest_enum(input: TokenStream) -> TokenStream { suggest::suggest_enum(input) } - -fn read_name(input: &TokenStream) -> Ident { - if input.is_empty() { - Ident::new(DEFAULT_PROGRAM_NAME, proc_macro2::Span::call_site()) - } else { - syn::parse(input.clone()).unwrap() - } -} diff --git a/mingling_macros/src/pack.rs b/mingling_macros/src/pack.rs index bf2536d..ffb07f2 100644 --- a/mingling_macros/src/pack.rs +++ b/mingling_macros/src/pack.rs @@ -3,86 +3,35 @@ use quote::quote; use syn::parse::{Parse, ParseStream}; use syn::{Attribute, Ident, Result as SynResult, Token, Type}; -enum PackInput { - Explicit { - attrs: Vec<Attribute>, - group_name: syn::Path, - type_name: Ident, - inner_type: Type, - }, - Default { - attrs: Vec<Attribute>, - type_name: Ident, - inner_type: Type, - }, +struct PackInput { + attrs: Vec<Attribute>, + type_name: Ident, + inner_type: Type, } impl Parse for PackInput { fn parse(input: ParseStream) -> SynResult<Self> { - // First, collect any outer attributes (`#[...]` and `///...`) before the main syntax. let attrs = input.call(Attribute::parse_outer)?; + let type_name: Ident = input.parse()?; + input.parse::<Token![=]>()?; + let inner_type: Type = input.parse()?; - // Now determine the format: - // - `Path, TypeName = InnerType` → Explicit - // - `TypeName = InnerType` → 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![=]>()?; - let inner_type = input.parse()?; - - Ok(PackInput::Explicit { - attrs, - group_name, - type_name, - inner_type, - }) - } else if input.peek(Ident) && input.peek2(Token![=]) { - // Default format: TypeName = InnerType - let type_name = input.parse()?; - input.parse::<Token![=]>()?; - let inner_type = input.parse()?; - - Ok(PackInput::Default { - attrs, - type_name, - inner_type, - }) - } else { - Err(input.lookahead1().error()) - } + Ok(PackInput { + attrs, + type_name, + inner_type, + }) } } #[allow(clippy::too_many_lines)] pub fn pack(input: TokenStream) -> TokenStream { - // Parse the input let pack_input = syn::parse_macro_input!(input as PackInput); - let (group_name, type_name, inner_type, attrs, use_default) = match pack_input { - PackInput::Explicit { - attrs, - group_name, - type_name, - inner_type, - } => (quote! { #group_name }, type_name, inner_type, attrs, false), - PackInput::Default { - attrs, - type_name, - inner_type, - } => ( - crate::default_program_path(), - type_name, - inner_type, - attrs, - true, - ), - }; + let group_name = crate::default_program_path(); + let type_name = pack_input.type_name; + let inner_type = pack_input.inner_type; + let attrs = pack_input.attrs; // Generate the struct definition #[cfg(not(feature = "general_renderer"))] @@ -175,7 +124,16 @@ pub fn pack(input: TokenStream) -> TokenStream { ::mingling::macros::register_type!(#type_name); }; - let any_out_impl = quote! { + let expanded = quote! { + #struct_def + + #new_impl + #from_into_impl + #as_ref_impl + #deref_impl + #default_impl + #register_impl + impl Into<mingling::AnyOutput<#group_name>> for #type_name { fn into(self) -> mingling::AnyOutput<#group_name> { mingling::AnyOutput::new(self) @@ -187,9 +145,7 @@ pub fn pack(input: TokenStream) -> TokenStream { mingling::AnyOutput::new(self).route_chain() } } - }; - let group_impl = quote! { impl ::mingling::Groupped<#group_name> for #type_name { fn member_id() -> #group_name { #group_name::#type_name @@ -197,53 +153,5 @@ pub fn pack(input: TokenStream) -> TokenStream { } }; - // Combine all implementations - let expanded = if use_default { - // For default case, use ThisProgram - quote! { - #struct_def - - #new_impl - #from_into_impl - #as_ref_impl - #deref_impl - #default_impl - #register_impl - - 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<#group_name> { - fn from(value: #type_name) -> Self { - mingling::AnyOutput::new(value).route_chain() - } - } - - impl ::mingling::Groupped<#group_name> for #type_name { - fn member_id() -> #group_name { - #group_name::#type_name - } - } - } - } else { - // For explicit case, use the provided group_name - quote! { - #struct_def - - #new_impl - #from_into_impl - #as_ref_impl - #deref_impl - #default_impl - #register_impl - - #any_out_impl - #group_impl - } - }; - expanded.into() } diff --git a/mingling_macros/src/program_setup.rs b/mingling_macros/src/program_setup.rs index d8f9507..7fd9d16 100644 --- a/mingling_macros/src/program_setup.rs +++ b/mingling_macros/src/program_setup.rs @@ -48,14 +48,9 @@ fn extract_return_type(sig: &Signature) -> syn::Result<()> { } pub fn setup_attr(attr: TokenStream, item: TokenStream) -> TokenStream { - // Parse the attribute arguments (e.g., MyProgram from #[program_setup(MyProgram)]) - // If no argument is provided, use ThisProgram - let (program_name, use_crate_prefix) = if attr.is_empty() { - (crate::default_program_path(), true) - } else { - let path: syn::Path = parse_macro_input!(attr as syn::Path); - (quote! { #path }, false) - }; + // #[program_setup] takes no arguments; always use the default program path + let _ = attr; + let program_path = crate::default_program_path(); // Parse the function item let input_fn = parse_macro_input!(item as ItemFn); @@ -98,42 +93,22 @@ pub fn setup_attr(attr: TokenStream, item: TokenStream) -> TokenStream { let struct_name = Ident::new(&pascal_case_name, fn_name.span()); // Generate the struct and implementation - let expanded = if use_crate_prefix { - quote! { - #(#fn_attrs)* - #[doc(hidden)] - #vis struct #struct_name; - - impl ::mingling::setup::ProgramSetup<#program_name> for #struct_name { - fn setup(self, program: &mut ::mingling::Program<#program_name>) { - // Call the original function with the actual Program type - #fn_name(program); - } - } - - // Keep the original function for internal use - #(#fn_attrs)* - #vis fn #fn_name(#program_param: #program_type) { - #fn_body + let expanded = quote! { + #(#fn_attrs)* + #[doc(hidden)] + #vis struct #struct_name; + + impl ::mingling::setup::ProgramSetup<#program_path> for #struct_name { + fn setup(self, program: &mut ::mingling::Program<#program_path>) { + // Call the original function with the actual Program type + #fn_name(program); } } - } else { - quote! { - #(#fn_attrs)* - #vis struct #struct_name; - - impl ::mingling::setup::ProgramSetup<#program_name> for #struct_name { - fn setup(&mut self, program: &mut ::mingling::Program<#program_name>) { - // Call the original function with the actual Program type - #fn_name(program); - } - } - // Keep the original function for internal use - #(#fn_attrs)* - #vis fn #fn_name(#program_param: #program_type) { - #fn_body - } + // Keep the original function for internal use + #(#fn_attrs)* + #vis fn #fn_name(#program_param: #program_type) { + #fn_body } }; diff --git a/mingling_macros/src/renderer.rs b/mingling_macros/src/renderer.rs index 4cf9fc1..ac82799 100644 --- a/mingling_macros/src/renderer.rs +++ b/mingling_macros/src/renderer.rs @@ -23,8 +23,9 @@ fn extract_return_type(sig: &Signature) -> Option<syn::Type> { #[allow(clippy::too_many_lines)] pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream { - // Parse attribute arguments for program path (e.g. #[renderer(my_crate::Program)]) - let (program_path, _use_crate_prefix) = parse_renderer_attr_args(attr); + // #[renderer] takes no arguments; always use the default program path + let _ = attr; + let program_path = crate::default_program_path(); let program_type = &program_path; // Parse the function item @@ -167,15 +168,6 @@ pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream { expanded.into() } -fn parse_renderer_attr_args(attr: TokenStream) -> (proc_macro2::TokenStream, bool) { - if attr.is_empty() { - (crate::default_program_path(), true) - } else { - let path: syn::Path = - syn::parse(attr).expect("Expected a path argument for #[renderer(path)]"); - (quote! { #path }, false) - } -} /// Builds the renderer entry for the global renderers list pub fn build_renderer_entry( |
