From 2dff21cc78fd2d6a4fa4f227f6ad94e3f7baa943 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Tue, 28 Jul 2026 19:11:14 +0800 Subject: feat(mingling_macros, mingling_pathf): add `#[command]` attribute macro Add a new `#[command]` attribute macro (feature-gated behind `extra_macros`) that converts a plain function with a `Vec` parameter into a fully wired Mingling command. The macro generates a `dispatcher!` call, a `#[chain]` wrapper, and a hidden module re-exporting all generated types. Also add `CommandPattern` to the pathf pattern analyzer to recognize `#[command]`-annotated functions and track their generated hidden modules. --- mingling_macros/src/lib.rs | 87 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'mingling_macros/src/lib.rs') diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index 27563a4..ae874af 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -1100,6 +1100,93 @@ pub fn program_setup(attr: TokenStream, item: TokenStream) -> TokenStream { program_setup::setup_attr(attr, item) } +/// Declares a command from a plain function. +/// +/// **This macro is only available with the `extra_macros` feature.** +/// +/// The `#[command]` attribute converts a function taking `Vec` into a +/// Mingling command by: +/// 1. Calling `dispatcher!("command_name")` to register the dispatcher entry. +/// 2. Generating a `#[chain]` wrapper that bridges the entry type (`Entry{Pascal}`) +/// to the original function. +/// 3. Preserving the original function unchanged (including attributes, extensions, +/// visibility, and asyncness). +/// +/// # Syntax +/// +/// ## Simple form (auto-derives names from function name) +/// +/// ```rust,ignore +/// #[command] +/// fn greet(args: Vec) -> Next { +/// // ... +/// } +/// ``` +/// +/// This deduces: +/// - Command path: `"greet"` (via `dot_case` of function name) +/// - Dispatcher struct: `CMDGreet` +/// - Entry struct: `EntryGreet` +/// - Dispatches via `dispatcher!("greet")` +/// +/// ## Explicit attributes +/// +/// ```rust,ignore +/// #[command(node = "hello.world")] +/// fn greet(args: Vec) -> Next { +/// // ... +/// } +/// // → dispatcher!("hello.world", CMDGreet => EntryGreet) +/// ``` +/// +/// ```rust,ignore +/// #[command(name = MyDispatcher, entry = MyEntry)] +/// fn greet(args: Vec) -> Next { +/// // ... +/// } +/// // → dispatcher!("greet", MyDispatcher => MyEntry) +/// ``` +/// +/// ## Extension attributes +/// +/// Extra bare paths (e.g. `buffer`, `routeify`, `::mingling::macros::routeify`) +/// are emitted as `#[ext]` attributes **on the original function**, not on the +/// chain wrapper. The chain wrapper always uses bare `#[::mingling::macros::chain]`. +/// +/// ```rust,ignore +/// #[command(buffer)] +/// fn greet(args: Vec) { +/// r_println!("Hello!"); +/// } +/// ``` +/// +/// # Resource injection +/// +/// Parameters after the first are treated as resource injections and passed +/// through to the generated `#[chain]` wrapper unchanged (as reference params): +/// +/// ```rust,ignore +/// #[command] +/// fn greet(args: Vec, ec: &mut ResExitCode) -> Next { +/// ec.exit_code = 0; +/// // ... +/// } +/// ``` +/// +/// The generated chain wrapper calls the original function with `entry.into()` +/// for the first argument and passes all subsequent arguments directly. +/// +/// # Requirements +/// +/// - The function must have at least one parameter (the `Vec` entry argument). +/// - The function must not have a `self` parameter. +/// - Visibility (`pub` etc.) and `async` are preserved on the original function. +#[cfg(feature = "extra_macros")] +#[proc_macro_attribute] +pub fn command(attr: TokenStream, item: TokenStream) -> TokenStream { + attr::command::command_attr(attr, item) +} + /// Declares a `Dispatcher` that uses `clap::Parser` for argument parsing. /// /// **This macro is only available with the `clap` feature.** -- cgit