diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-05-02 01:02:23 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-05-02 01:25:06 +0800 |
| commit | 11a3258834587a267f63588366ecc00d6b82156c (patch) | |
| tree | 9a7d2db5daa52a5f4de2a9d946c8f15438ab89d3 /mingling_macros | |
| parent | f31347533c2b13f58eeae29ffc3910ca5e2f04d5 (diff) | |
Add architecture overview and doc comments to macro crate
Diffstat (limited to 'mingling_macros')
| -rw-r--r-- | mingling_macros/src/chain.rs | 8 | ||||
| -rw-r--r-- | mingling_macros/src/completion.rs | 5 | ||||
| -rw-r--r-- | mingling_macros/src/dispatcher_clap.rs | 39 | ||||
| -rw-r--r-- | mingling_macros/src/enum_tag.rs | 8 | ||||
| -rw-r--r-- | mingling_macros/src/groupped.rs | 5 | ||||
| -rw-r--r-- | mingling_macros/src/help.rs | 19 | ||||
| -rw-r--r-- | mingling_macros/src/lib.rs | 920 | ||||
| -rw-r--r-- | mingling_macros/src/node.rs | 5 | ||||
| -rw-r--r-- | mingling_macros/src/pack.rs | 5 | ||||
| -rw-r--r-- | mingling_macros/src/program_setup.rs | 5 | ||||
| -rw-r--r-- | mingling_macros/src/render.rs | 5 | ||||
| -rw-r--r-- | mingling_macros/src/renderer.rs | 1 |
12 files changed, 921 insertions, 104 deletions
diff --git a/mingling_macros/src/chain.rs b/mingling_macros/src/chain.rs index 6cc249f..a91949d 100644 --- a/mingling_macros/src/chain.rs +++ b/mingling_macros/src/chain.rs @@ -1,11 +1,3 @@ -//! Chain Attribute Macro Implementation -//! -//! This module provides the `#[chain(Group)]` attribute macro for automatically -//! generating structs that implement the `Chain` trait from functions. -//! -//! When the `async` feature is enabled, chain functions must be async functions. -//! When the `async` feature is disabled, chain functions can be regular functions. - use proc_macro::TokenStream; use quote::{ToTokens, quote}; use syn::spanned::Spanned; diff --git a/mingling_macros/src/completion.rs b/mingling_macros/src/completion.rs index fcb9784..ea57f06 100644 --- a/mingling_macros/src/completion.rs +++ b/mingling_macros/src/completion.rs @@ -1,8 +1,3 @@ -//! Completion Attribute Macro Implementation -//! -//! This module provides the `#[completion]` attribute macro for automatically -//! generating structs that implement the `Completion` trait from functions. - use proc_macro::TokenStream; use quote::quote; use syn::{Ident, ItemFn, parse_macro_input}; diff --git a/mingling_macros/src/dispatcher_clap.rs b/mingling_macros/src/dispatcher_clap.rs index 06f2c5a..37c662f 100644 --- a/mingling_macros/src/dispatcher_clap.rs +++ b/mingling_macros/src/dispatcher_clap.rs @@ -1,42 +1,3 @@ -//! Dispatcher Clap Attribute Macro -//! -//! This module provides the `#[dispatcher_clap(...)]` attribute macro for -//! automatically generating a `Dispatcher` implementation that uses `clap::Parser` -//! to parse command arguments into the annotated struct. -//! -//! This macro is only available when the `clap` feature is enabled. -//! -//! # Syntax -//! -//! ```rust,ignore -//! #[derive(Groupped, clap::Parser)] -//! #[dispatcher_clap("command_name", DispatcherName)] -//! struct MyEntry { -//! #[arg(long, short)] -//! name: String, -//! } -//! ``` -//! -//! Or with explicit program name: -//! -//! ```rust,ignore -//! #[dispatcher_clap(MyProgram, "ok", CommandOk, error = CommandParseError)] -//! struct OkEntry { -//! #[arg(long, short)] -//! str: String, -//! } -//! ``` -//! -//! Or with help: -//! -//! ```rust,ignore -//! #[dispatcher_clap("ok", CommandOk, error = CommandParseError, help = true)] -//! struct OkEntry { -//! #[arg(long, short)] -//! str: String, -//! } -//! ``` - use proc_macro::TokenStream; use quote::quote; use syn::{ diff --git a/mingling_macros/src/enum_tag.rs b/mingling_macros/src/enum_tag.rs index a53e1aa..8f0576a 100644 --- a/mingling_macros/src/enum_tag.rs +++ b/mingling_macros/src/enum_tag.rs @@ -1,11 +1,3 @@ -//! EnumTag derive macro implementation -//! -//! This module provides the `#[derive(EnumTag)]` procedural macro for enums. -//! The macro generates implementations of the `EnumTag` trait for enums with -//! unit variants only (no fields). Variants can have an optional `#[enum_desc]` -//! attribute to provide descriptions, and an optional `#[enum_rename]` attribute -//! to rename the variant for building and listing purposes. - use proc_macro::TokenStream; use quote::quote; use syn::{ diff --git a/mingling_macros/src/groupped.rs b/mingling_macros/src/groupped.rs index 508092c..3c30827 100644 --- a/mingling_macros/src/groupped.rs +++ b/mingling_macros/src/groupped.rs @@ -1,8 +1,3 @@ -//! Groupped Derive Macro Implementation -//! -//! This module provides the `#[derive(Groupped)]` macro for automatically -//! implementing the `Groupped` trait on structs and enums. - use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; diff --git a/mingling_macros/src/help.rs b/mingling_macros/src/help.rs index 7943270..315483d 100644 --- a/mingling_macros/src/help.rs +++ b/mingling_macros/src/help.rs @@ -1,22 +1,3 @@ -//! Help Attribute Macro -//! -//! This module provides the `#[help]` attribute macro for automatically -//! generating structs that implement the `HelpRequest` trait from functions. -//! -//! # Syntax -//! -//! ```rust,ignore -//! #[help] -//! fn help_my_entry(prev: MyEntry) { -//! // use r_println! here -//! r_println!("Help: ..."); -//! } -//! ``` -//! -//! This expands to: -//! - A struct `HelpMyEntry` implementing `HelpRequest` with `Entry = MyEntry` -//! - The original function with injected `RenderResult` dummy context - use proc_macro::TokenStream; use quote::{ToTokens, quote}; use syn::spanned::Spanned; diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index a8b3b15..70a5c3e 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -2,6 +2,20 @@ //! //! This crate provides procedural macros for the Mingling framework. //! Macros are implemented in separate modules and re-exported here. +//! +//! # Architecture Overview +//! +//! The Mingling macros crate provides the following categories of macros: +//! +//! - **Command definition**: `dispatcher!`, `dispatcher_clap!`, `node!`, `pack!` +//! - **Chain processing**: `#[chain]`, `gen_program!`, `route!` +//! - **Rendering**: `#[renderer]`, `r_print!`, `r_println!` +//! - **Help system**: `#[help]`, `register_help!` +//! - **Derive macros**: `#[derive(Groupped)]`, `#[derive(EnumTag)]`, `#[derive(GrouppedSerialize)]` +//! - **Program setup**: `#[program_setup]` +//! - **Completion (comp feature)**: `#[completion]`, `suggest!`, `suggest_enum!` +//! - **Internal registration**: `register_type!`, `register_chain!`, `register_renderer!`, +//! `program_fallback_gen!`, `program_final_gen!`, `program_comp_gen!` use once_cell::sync::Lazy; use proc_macro::TokenStream; @@ -48,16 +62,123 @@ pub(crate) static RENDERERS_EXIST: Lazy<Mutex<BTreeSet<String>>> = pub(crate) static HELP_REQUESTS: Lazy<Mutex<BTreeSet<String>>> = Lazy::new(|| Mutex::new(BTreeSet::new())); +/// Creates a [`Node`] from a dot-separated path string. +/// +/// Each segment is converted to kebab-case (unless it starts with `_`). +/// Segments are joined via `.join()` calls, building a path hierarchy for +/// command matching. +/// +/// # Syntax +/// +/// ```rust,ignore +/// node!("subcommand") +/// node!("sub.subsub") +/// node!("") // empty → Node::default() +/// ``` +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::macros::node; +/// +/// // Creates a single-level node for "hello" +/// let n = node!("hello"); +/// +/// // Creates a two-level node for "remote control" +/// let n = node!("remote.control"); +/// ``` +/// +/// # Internals +/// +/// The generated code is equivalent to: +/// ```rust,ignore +/// Node::default().join("hello") +/// Node::default().join("remote").join("control") +/// ``` +/// +/// This macro is typically used internally by [`dispatcher!`] and should rarely +/// need to be called directly. #[proc_macro] pub fn node(input: TokenStream) -> TokenStream { node::node(input) } +/// Creates a type-safe wrapper struct around an inner type, with automatic +/// trait implementations for use in the Mingling chain/render pipeline. +/// +/// The generated struct implements: `From`/`Into`, `AsRef`/`AsMut`, `Deref`/`DerefMut`, +/// `Default` (conditional on inner type), and conversion into [`AnyOutput`] / +/// [`ChainProcess`] for routing. +/// +/// # Syntax +/// +/// ```rust,ignore +/// // Default program name (uses `ThisProgram`): +/// pack!(TypeName = InnerType); +/// +/// // Explicit program name: +/// pack!(MyProgram, TypeName = InnerType); +/// ``` +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::macros::pack; +/// +/// // Creates `Hello` wrapping `String`, registered under `ThisProgram`: +/// pack!(Hello = String); +/// +/// // Creates `Greeting` wrapping `String`, registered under `MyApp`: +/// pack!(MyApp, Greeting = String); +/// ``` +/// +/// After expansion, `Hello` has: +/// - `Hello::new(String)` — constructor +/// - `Hello::to_chain()` — routes to the next chain processor +/// - `Hello::to_render()` — routes to a renderer +/// - `From<String> for Hello`, `From<Hello> for String` +/// - `Deref<Target = String>`, `DerefMut` +/// - `AsRef<String>`, `AsMut<String>` +/// - `Default` if `String: Default` +/// - `Into<AnyOutput<ThisProgram>>`, `Into<ChainProcess<ThisProgram>>` +/// - Implements `Groupped<ThisProgram>` with `member_id()` returning the enum variant +/// +/// The struct is also registered via `register_type!` so that `gen_program!` +/// can include it in the program enum. +/// +/// When the `general_renderer` feature is enabled, the struct also gets +/// `#[derive(serde::Serialize)]`. #[proc_macro] pub fn pack(input: TokenStream) -> TokenStream { pack::pack(input) } +/// Early-returns an error from a `Result`, converting the `Ok` branch to a +/// [`ChainProcess`]. +/// +/// This macro is equivalent to: +/// ```rust,ignore +/// match expr { +/// Ok(r) => r, +/// Err(e) => return e, +/// } +/// ``` +/// +/// It is useful inside chain functions where you have a `Result<ChainProcess<G>, ChainProcess<G>>` +/// and want to propagate the error case as an early return. +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::macros::{chain, route}; +/// +/// #[chain] +/// fn process(prev: SomeEntry) -> ChainProcess<ThisProgram> { +/// let value = route!(try_something().ok_or(ErrorEntry::new("failed".into()).to_render())); +/// // value is the Ok(ChainProcess) from try_something() +/// value +/// } +/// ``` #[proc_macro] pub fn route(input: TokenStream) -> TokenStream { let expr = parse_macro_input!(input as syn::Expr); @@ -70,74 +191,628 @@ pub fn route(input: TokenStream) -> TokenStream { TokenStream::from(expanded) } +/// Creates a [`Dispatcher`] implementation for a subcommand. +/// +/// This is the primary way to define command-line subcommands in Mingling. +/// It generates a dispatcher struct that, when matched against user input, +/// converts the arguments into a [`ChainProcess`] via the specified entry type. +/// +/// # Syntax +/// +/// ```rust,ignore +/// // Default program name (uses `ThisProgram`): +/// dispatcher!("command.path", CommandStruct => EntryStruct); +/// +/// // Explicit program name: +/// dispatcher!(MyProgram, "command.path", CommandStruct => EntryStruct); +/// ``` +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::macros::dispatcher; +/// +/// // "hello" subcommand → HelloCommand → HelloEntry +/// dispatcher!("hello", HelloCommand => HelloEntry); +/// +/// // Nested: "remote control" → RemoteControlCommand → RemoteControlEntry +/// dispatcher!("remote.control", RemoteControlCommand => RemoteControlEntry); +/// +/// // With explicit program: +/// dispatcher!(MyApp, "status", StatusCommand => StatusEntry); +/// ``` +/// +/// The generated `HelloCommand` implements `Dispatcher<ThisProgram>`: +/// - `node()` returns the [`Node`] hierarchy for "hello" +/// - `begin(args)` wraps `args` into `HelloEntry` and routes to chain +/// - `clone_dispatcher()` returns a boxed clone +/// +/// The `HelloEntry` struct is a wrapper around `Vec<String>` created via +/// an implicit `pack!` call with the program name. +/// +/// When the `comp` feature is enabled, the entry type also implements +/// `CompletionEntry` for providing shell completion suggestions. #[proc_macro] pub fn dispatcher(input: TokenStream) -> TokenStream { dispatcher::dispatcher(input) } +/// Prints formatted text to the current [`RenderResult`] buffer within a +/// [`#[renderer]`](macro.renderer.html) function. +/// +/// This macro requires a mutable reference to a [`RenderResult`] named `r` +/// to be in scope, which is automatically provided inside `#[renderer]` +/// functions. +/// +/// # Syntax +/// +/// Same as `format!` / `print!`: +/// +/// ```rust,ignore +/// r_print!("Hello, {}!", name); +/// ``` +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::macros::{renderer, r_print}; +/// +/// #[renderer] +/// fn show_greeting(prev: Greeting) { +/// r_print!("Hello, {}!", *prev); +/// } +/// ``` +/// +/// # Difference from `r_println!` +/// +/// `r_print!` does **not** append a newline. Use [`r_println!`] for newline-terminated output. #[proc_macro] pub fn r_print(input: TokenStream) -> TokenStream { render::r_print(input) } +/// Prints formatted text followed by a newline to the current [`RenderResult`] +/// buffer within a [`#[renderer]`](macro.renderer.html) function. +/// +/// This macro requires a mutable reference to a [`RenderResult`] named `r` +/// to be in scope, which is automatically provided inside `#[renderer]` +/// functions. +/// +/// # Syntax +/// +/// Same as `println!`: +/// +/// ```rust,ignore +/// r_println!("Hello, {}!", name); +/// r_println!(); // just a newline +/// ``` +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::macros::{renderer, r_println}; +/// +/// #[renderer] +/// fn show_greeting(prev: Greeting) { +/// r_println!("Hello, {}!", *prev); +/// } +/// ``` #[proc_macro] pub fn r_println(input: TokenStream) -> TokenStream { render::r_println(input) } +/// Declares a chain processing step that transforms one type into another +/// within a Mingling pipeline. +/// +/// The `#[chain]` attribute converts an ordinary function (or async function +/// with the `async` feature) into a chain step by: +/// 1. Generating a hidden struct implementing the [`Chain`] trait. +/// 2. Registering the chain mapping in the global chain registry. +/// 3. Keeping the original function for direct calls. +/// +/// # Syntax +/// +/// ```rust,ignore +/// // Default program (ThisProgram): +/// #[chain] +/// fn my_step(prev: InputType) -> ChainProcess<ThisProgram> { +/// // transform `prev`... +/// OutputType::new(result).to_render() +/// } +/// +/// // Explicit program name: +/// #[chain(MyProgram)] +/// fn my_step(prev: InputType) -> ChainProcess<MyProgram> { +/// // ... +/// } +/// ``` +/// +/// # Sync Example +/// +/// ```rust,ignore +/// use mingling::macros::{chain, pack, gen_program}; +/// +/// pack!(MyOutput = String); +/// +/// #[chain] +/// fn greet(prev: HelloEntry) -> ChainProcess<ThisProgram> { +/// let name = prev.first().cloned().unwrap_or_else(|| "World".to_string()); +/// MyOutput::new(name).to_render() +/// } +/// ``` +/// +/// # Async Example (with `async` feature) +/// +/// ```rust,ignore +/// use mingling::macros::{chain, pack, gen_program}; +/// +/// pack!(MyOutput = String); +/// +/// #[chain] +/// async fn greet(prev: HelloEntry) -> ChainProcess<ThisProgram> { +/// let name = prev.first().cloned().unwrap_or_else(|| "World".to_string()); +/// some_async_fn(&name).await; +/// MyOutput::new(name).to_render() +/// } +/// ``` +/// +/// # Requirements +/// +/// - The function must have exactly **one** parameter (the previous type in the chain). +/// - The function must return `ChainProcess<ProgramName>` (or `impl Into<ChainProcess<ProgramName>>`). +/// - With the `async` feature, async functions are supported; without it, async functions are rejected. #[proc_macro_attribute] pub fn chain(attr: TokenStream, item: TokenStream) -> TokenStream { chain::chain_attr(attr, item) } +/// Declares a renderer step that renders the output of a chain to the terminal. +/// +/// The `#[renderer]` attribute converts a function into a renderer by: +/// 1. Generating a hidden struct implementing the [`Renderer`] trait. +/// 2. Registering the renderer mapping in the global renderer registry. +/// 3. Keeping the original function for direct calls. When called directly, +/// a new `RenderResult` is created and the renderer function writes its +/// output directly to the current terminal output buffer. +/// +/// Inside a `#[renderer]` function, you can use [`r_print!`] and [`r_println!`] +/// to write output to the [`RenderResult`] buffer. +/// +/// # Syntax +/// +/// ```rust,ignore +/// // Default program (ThisProgram): +/// #[renderer] +/// fn render_my_type(prev: MyType) { +/// r_println!("Output: {:?}", *prev); +/// } +/// ``` +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::macros::{renderer, r_println, pack, gen_program}; +/// +/// pack!(Greeting = String); +/// +/// #[renderer] +/// fn render_greeting(prev: Greeting) { +/// r_println!("Hello, {}!", *prev); +/// } +/// ``` +/// +/// # Requirements +/// +/// - The function must have exactly **one** parameter (the type to render). +/// - The function must return `()` (unit). +/// - The function **cannot** be async. +/// +/// # Fallback Renderers +/// +/// The macros `gen_program!` automatically generates two fallback types that +/// you can provide renderers for: +/// - `RendererNotFound` — triggered when no matching renderer is found +/// - `DispatcherNotFound` — triggered when no matching dispatcher is found +/// +/// ```rust,ignore +/// #[renderer] +/// fn fallback_dispatcher_not_found(prev: DispatcherNotFound) { +/// r_println!("Unknown command: {}", prev.join(", ")); +/// } +/// +/// #[renderer] +/// fn fallback_renderer_not_found(prev: RendererNotFound) { +/// r_println!("No renderer for `{}`", *prev); +/// } +/// ``` #[proc_macro_attribute] pub fn renderer(_attr: TokenStream, item: TokenStream) -> TokenStream { renderer::renderer_attr(item) } +/// Declares a completion suggestion provider for a command entry type. +/// +/// **This macro is only available with the `comp` feature.** +/// +/// The `#[completion]` attribute converts a function into a completion provider by: +/// 1. Generating a hidden struct implementing the [`Completion`] trait. +/// 2. Registering the completion mapping for the specified entry type. +/// 3. Keeping the original function for direct calls. +/// +/// # Syntax +/// +/// ```rust,ignore +/// #[completion(EntryType)] +/// fn complete_my_entry(ctx: &ShellContext) -> Suggest { +/// // Return suggestions based on current input state... +/// } +/// ``` +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::macros::{completion, suggest, suggest_enum}; +/// use mingling::{ShellContext, Suggest}; +/// +/// #[completion(MyEntry)] +/// fn complete_my_command(ctx: &ShellContext) -> Suggest { +/// if ctx.filling_argument_first("--name") { +/// return suggest!(); +/// } +/// if ctx.filling_argument_first("--type") { +/// return suggest_enum!(MyEnum); +/// } +/// if ctx.typing_argument() { +/// return suggest! { +/// "--name": "Provide a name", +/// "--type": "Select a type" +/// }.strip_typed_argument(ctx); +/// } +/// suggest!() +/// } +/// ``` +/// +/// # Requirements +/// +/// - The `comp` feature must be enabled. +/// - The function must have exactly one parameter of type `&ShellContext`. +/// - The function must return `Suggest`. +/// - The function cannot be async. #[cfg(feature = "comp")] #[proc_macro_attribute] pub fn completion(attr: TokenStream, item: TokenStream) -> TokenStream { completion::completion_attr(attr, item) } +/// Declares a program setup function that initializes the program instance +/// before execution. +/// +/// The `#[program_setup]` attribute converts a function into a setup step by: +/// 1. Generating a struct implementing the [`ProgramSetup`] trait. +/// 2. The setup function receives a mutable reference to `&mut Program<G>`. +/// +/// # 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 +/// +/// ```rust,ignore +/// use mingling::macros::program_setup; +/// use mingling::Program; +/// +/// #[program_setup] +/// fn configure(program: &mut Program<ThisProgram>) { +/// program.with_setup(GeneralRendererSetup); +/// program.user_context.some_flag = true; +/// } +/// ``` +/// +/// # Requirements +/// +/// - The function must have exactly one parameter of type `&mut Program<G>`. +/// - The function must return `()`. +/// - The function cannot be async. #[proc_macro_attribute] pub fn program_setup(attr: TokenStream, item: TokenStream) -> TokenStream { program_setup::setup_attr(attr, item) } +/// Declares a [`Dispatcher`] that uses [`clap::Parser`] for argument parsing. +/// +/// **This macro is only available with the `clap` feature.** +/// +/// The `#[dispatcher_clap]` attribute: +/// 1. Keeps the original struct definition (typically with `#[derive(clap::Parser)]`). +/// 2. Generates a dispatcher struct that parses arguments using clap and routes +/// to the chain pipeline. +/// 3. Optionally generates a `#[help]` block for displaying clap-generated help. +/// +/// # Syntax +/// +/// ```rust,ignore +/// // Default program (ThisProgram): +/// #[derive(clap::Parser)] +/// #[dispatcher_clap("command.name", DispatcherStruct)] +/// struct MyEntry { /* ... */ } +/// +/// // With explicit error type and help: +/// #[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 +/// +/// ```rust,ignore +/// use clap::Parser; +/// use mingling::macros::dispatcher_clap; +/// +/// #[derive(Parser)] +/// #[dispatcher_clap("greet", GreetDispatcher, error = GreetParseError, help = true)] +/// struct GreetArgs { +/// #[arg(short, long)] +/// name: String, +/// } +/// ``` +/// +/// # Options +/// +/// - `error = ErrorType` — Specifies an error wrapper type for clap parse failures. +/// The error message is captured and routed to the renderer. +/// - `help = true` — Generates a `#[help]` block that displays clap's help output +/// when `--help` is passed. #[cfg(feature = "clap")] #[proc_macro_attribute] pub fn dispatcher_clap(attr: TokenStream, item: TokenStream) -> TokenStream { dispatcher_clap::dispatcher_clap_attr(attr, item) } +/// Registers a help request mapping between an entry type and a help struct. +/// +/// This macro is used internally by the [`#[help]`](macro.help.html) attribute +/// and is also available for manual registration if needed. +/// +/// # Syntax +/// +/// ```rust,ignore +/// register_help!(EntryType, HelpStruct); +/// ``` +/// +/// This adds an entry to the global `HELP_REQUESTS` registry, mapping the +/// enum variant for `EntryType` to the help rendering logic in `HelpStruct`. #[proc_macro] pub fn register_help(input: TokenStream) -> TokenStream { help::register_help(input) } +/// Declares a help rendering function for an entry type. +/// +/// The `#[help]` attribute converts a function into a help provider by: +/// 1. Generating a hidden struct implementing the [`HelpRequest`] trait. +/// 2. Registering the help mapping in the global `HELP_REQUESTS` registry. +/// 3. Keeping the original function for direct calls (with a dummy `RenderResult`). +/// +/// Inside a `#[help]` function, you can use [`r_print!`] and [`r_println!`] +/// to write help text to the [`RenderResult`] buffer. +/// +/// # Syntax +/// +/// ```rust,ignore +/// #[help] +/// fn help_my_entry(prev: MyEntry) { +/// r_println!("Usage: myapp myentry [options]"); +/// r_println!(" Does something useful."); +/// } +/// ``` +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::macros::{help, r_println, pack, gen_program}; +/// +/// pack!(MyEntry = Vec<String>); +/// +/// #[help] +/// fn help_my_entry(_prev: MyEntry) { +/// r_println!("Usage: myapp greet [name]"); +/// r_println!("Greets the user."); +/// } +/// ``` +/// +/// # Requirements +/// +/// - The function must have exactly one parameter (the entry type to provide help for). +/// - The function must return `()`. +/// - The function cannot be async. #[proc_macro_attribute] pub fn help(_attr: TokenStream, item: TokenStream) -> TokenStream { help::help_attr(item) } +/// 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)]`. +/// 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. +/// +/// # Syntax +/// +/// ```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}}; +/// +/// #[derive(Groupped)] +/// #[group(ThisProgram)] +/// struct Greeting { +/// name: String, +/// } +/// ``` +/// +/// This is equivalent to using `pack!` but works with custom structs that +/// have named fields. For simple wrappers, prefer `pack!`. #[proc_macro_derive(Groupped, attributes(group))] pub fn derive_groupped(input: TokenStream) -> TokenStream { groupped::derive_groupped(input) } +/// Derive macro for automatically implementing the [`EnumTag`] trait on an enum +/// with unit-only variants. +/// +/// The `#[derive(EnumTag)]` macro generates: +/// - `enum_info(&self) -> (&'static str, &'static str)` — returns (name, description) +/// for the current variant. +/// - `build_enum(name: String) -> Option<Self>` — constructs a variant from its +/// display name (or `#[enum_rename]` value). +/// - `enums() -> &'static [(&'static str, &'static str)]` — returns all (name, description) +/// pairs. +/// +/// # Attributes +/// +/// - `#[enum_desc("description text")]` — Provides a description for the variant. +/// - `#[enum_rename("display name")]` — Changes the display/build name of the variant. +/// +/// # Syntax +/// +/// ```rust,ignore +/// #[derive(EnumTag)] +/// enum Fruit { +/// #[enum_desc("A sweet red fruit")] +/// #[enum_rename("apple")] +/// Apple, +/// +/// #[enum_desc("A yellow tropical fruit")] +/// #[enum_rename("banana")] +/// Banana, +/// } +/// ``` +/// +/// # Requirements +/// +/// - Can only be derived for **enums** (not structs or unions). +/// - All variants must be **unit variants** (no fields). +/// - Each variant is optional; variants without attributes get their Rust name as display name +/// and an empty description. #[proc_macro_derive(EnumTag, attributes(enum_desc, enum_rename))] pub fn derive_enum_tag(input: TokenStream) -> TokenStream { enum_tag::derive_enum_tag(input) } +/// Derive macro for implementing both [`Groupped`] and `serde::Serialize` on a struct. +/// +/// **This macro is only available with the `general_renderer` feature.** +/// +/// This is identical to `#[derive(Groupped)]` but also adds `#[derive(serde::Serialize)]` +/// to the struct, which is required for the general renderer to serialize output +/// to formats like JSON, YAML, TOML, or RON. +/// +/// # Syntax +/// +/// ```rust,ignore +/// #[derive(GrouppedSerialize)] +/// #[group(MyProgram)] +/// struct Info { +/// name: String, +/// age: i32, +/// } +/// ``` +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::GrouppedSerialize; +/// use serde::Serialize; +/// +/// #[derive(GrouppedSerialize)] +/// struct Info { +/// name: String, +/// age: i32, +/// } +/// ``` #[cfg(feature = "general_renderer")] #[proc_macro_derive(GrouppedSerialize, attributes(group))] pub fn derive_groupped_serialize(input: TokenStream) -> TokenStream { groupped::derive_groupped_serialize(input) } +/// Generates the program enum and all collected types, chains, and renderers. +/// +/// This macro **must** be called at the end of your program module to collect +/// all registered types, chains, renderers, and help requests into a single +/// program enum that implements [`ProgramCollect`]. +/// +/// # Syntax +/// +/// ```rust,ignore +/// // Default program name (uses `ThisProgram`): +/// gen_program!(); +/// +/// // Explicit program name: +/// gen_program!(MyProgram); +/// ``` +/// +/// # What it generates +/// +/// The macro expands to: +/// 1. **`pub type NextProcess = ChainProcess<ProgramName>`** — A convenience type alias +/// for use in chain function return types. +/// 2. **`program_comp_gen!(...)`** (with `comp` feature) — Generates completion infrastructure. +/// 3. **`program_fallback_gen!(...)`** — Generates `RendererNotFound` and `DispatcherNotFound` types. +/// 4. **`program_final_gen!(...)`** — Generates the program enum with: +/// - An enum with all packed types as variants +/// - `Display` implementation for the enum +/// - `ProgramCollect` implementation dispatching to all registered renderers and chains +/// - A `new()` constructor returning `Program<ProgramName>` +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::macros::{dispatcher, chain, renderer, gen_program}; +/// +/// dispatcher!("hello", HelloCommand => HelloEntry); +/// +/// #[chain] +/// fn process(prev: HelloEntry) -> NextProcess { +/// // ... +/// } +/// +/// #[renderer] +/// fn render(prev: /* ... */) { +/// r_println!("Done!"); +/// } +/// +/// // Collect everything: +/// gen_program!(); +/// ``` #[proc_macro] pub fn gen_program(input: TokenStream) -> TokenStream { let name = read_name(&input); @@ -162,6 +837,18 @@ pub fn gen_program(input: TokenStream) -> TokenStream { }) } +/// Internal macro used by `gen_program!` to generate completion infrastructure. +/// +/// **This macro is only available with the `comp` feature.** +/// +/// This is an internal macro and should not be called directly by user code. +/// It generates a completion dispatcher, the `CompletionContext` type, and +/// the execution/render logic for shell completion. +/// +/// The generated module `__completion_gen` contains: +/// - A `__comp` dispatcher that routes completion requests +/// - A `__exec_completion` chain that processes `CompletionContext` into `CompletionSuggest` +/// - A `__render_completion` renderer that outputs completion suggestions #[proc_macro] #[cfg(feature = "comp")] pub fn program_comp_gen(input: TokenStream) -> TokenStream { @@ -224,6 +911,22 @@ pub fn program_comp_gen(input: TokenStream) -> TokenStream { TokenStream::from(comp_dispatcher) } +/// Registers a type into the global packed types registry for inclusion in +/// the program enum generated by `gen_program!`. +/// +/// This macro is called internally by [`pack!`] and [`#[derive(Groupped)]`](macro.derive_groupped.html) +/// and is generally not needed in user code. However, it can be used for manual +/// registration if you are implementing custom type registration outside of +/// the standard macros. +/// +/// # Syntax +/// +/// ```rust,ignore +/// register_type!(MyType); +/// ``` +/// +/// Each call inserts the type's name into the `PACKED_TYPES` global set, which +/// is later consumed by `program_final_gen!` to generate enum variants. #[proc_macro] pub fn register_type(input: TokenStream) -> TokenStream { let type_ident = parse_macro_input!(input as syn::Ident); @@ -234,16 +937,75 @@ pub fn register_type(input: TokenStream) -> TokenStream { TokenStream::new() } +/// Registers a chain mapping from a previous type to a chain struct. +/// +/// This macro is called internally by [`#[chain]`](macro.chain.html) and is +/// generally not needed in user code. It inserts entries into the global +/// `CHAINS` and `CHAINS_EXIST` registries. +/// +/// # Syntax +/// +/// ```rust,ignore +/// register_chain!(PreviousType, ChainStruct); +/// ``` +/// +/// The `PreviousType` is the input type of the chain step, and `ChainStruct` +/// is the generated struct that implements the [`Chain`] trait. #[proc_macro] pub fn register_chain(input: TokenStream) -> TokenStream { chain::register_chain(input) } +/// Registers a renderer mapping from a type to a renderer struct. +/// +/// This macro is called internally by [`#[renderer]`](macro.renderer.html) and is +/// generally not needed in user code. It inserts entries into the global +/// `RENDERERS`, `RENDERERS_EXIST` and (with `general_renderer` feature) +/// `GENERAL_RENDERERS` registries. +/// +/// # Syntax +/// +/// ```rust,ignore +/// register_renderer!(PreviousType, RendererStruct); +/// ``` +/// +/// The `PreviousType` is the input type of the renderer, and `RendererStruct` +/// is the generated struct that implements the [`Renderer`] trait. #[proc_macro] pub fn register_renderer(input: TokenStream) -> TokenStream { renderer::register_renderer(input) } +/// Internal macro used by [`gen_program!`] to generate fallback types. +/// +/// This macro generates two fallback wrapper types that are essential +/// for error handling in the Mingling pipeline: +/// +/// - **`RendererNotFound`** — Wraps a `String` (the name of the missing renderer). +/// Used when no matching renderer is found for a given output type. +/// - **`DispatcherNotFound`** — Wraps `Vec<String>` (the unrecognized command args). +/// Used when no matching dispatcher is found for user input. +/// +/// Users can (and should) write `#[renderer]` functions for these types +/// to provide meaningful error messages. +/// +/// This macro is called automatically by `gen_program!` and should not +/// be called directly by user code. +/// +/// # Syntax +/// +/// ```rust,ignore +/// // Called internally by gen_program!: +/// program_fallback_gen!(ThisProgram); +/// program_fallback_gen!(MyProgram); +/// ``` +/// +/// # Generated code equivalent +/// +/// ```rust,ignore +/// pack!(ProgramName, RendererNotFound = String); +/// pack!(ProgramName, DispatcherNotFound = Vec<String>); +/// ``` #[proc_macro] pub fn program_fallback_gen(input: TokenStream) -> TokenStream { let name = read_name(&input); @@ -255,6 +1017,55 @@ pub fn program_fallback_gen(input: TokenStream) -> TokenStream { TokenStream::from(expanded) } +/// Internal macro used by [`gen_program!`] to generate the final program enum +/// and its [`ProgramCollect`] implementation. +/// +/// This is the core code generation macro that: +/// 1. Collects all registered types (from `pack!`, `#[derive(Groupped)]`, etc.) and +/// creates an enum with each type as a variant. +/// 2. Generates the `Display` implementation for the enum. +/// 3. Generates the `ProgramCollect` implementation that dispatches to all +/// registered renderers, chains, help handlers, completions, and general renderers. +/// 4. Adds a `new()` constructor on the enum returning `Program<EnumName>`. +/// +/// The generated enum's representation type (`#[repr(u8)]`, `#[repr(u16)]`, etc.) +/// is automatically chosen based on the number of variants. +/// +/// This macro is called automatically by `gen_program!` and should not +/// be called directly by user code. +/// +/// # Syntax +/// +/// ```rust,ignore +/// program_final_gen!(ThisProgram); +/// program_final_gen!(MyProgram); +/// ``` +/// +/// # Generated code structure +/// +/// ```rust,ignore +/// #[repr(u8)] +/// pub enum MyProgram { +/// TypeA, +/// TypeB, +/// // ... +/// } +/// +/// impl ProgramCollect for MyProgram { +/// type Enum = MyProgram; +/// fn render(any, r) { /* dispatches to all registered renderers */ } +/// fn do_chain(any) -> ChainProcess { /* dispatches to all registered chain steps */ } +/// fn render_help(any, r) { /* dispatches to all registered help handlers */ } +/// fn has_renderer(any) -> bool { /* checks renderer registry */ } +/// fn has_chain(any) -> bool { /* checks chain registry */ } +/// // (with comp feature) fn do_comp(...) +/// // (with general_renderer feature) fn general_render(...) +/// } +/// +/// impl MyProgram { +/// pub fn new() -> Program<MyProgram> { Program::new() } +/// } +/// ``` #[proc_macro] pub fn program_final_gen(input: TokenStream) -> TokenStream { let name = read_name(&input); @@ -418,12 +1229,121 @@ pub fn program_final_gen(input: TokenStream) -> TokenStream { TokenStream::from(expanded) } +/// Builds a [`Suggest`] instance with inline suggestion items. +/// +/// **This macro is only available with the `comp` feature.** +/// +/// The `suggest!` macro provides a concise syntax for creating shell completion +/// suggestions. Each item can be either a simple flag or a flag with a description. +/// +/// # Syntax +/// +/// ```rust,ignore +/// // Empty suggestions: +/// suggest!() +/// +/// // Simple flags (no description): +/// suggest! { "--flag1", "--flag2" } +/// +/// // Flags with descriptions: +/// suggest! { +/// "--name": "User's name", +/// "--age": "User's age" +/// } +/// +/// // Mixed: +/// suggest! { +/// "--name": "User's name", +/// "--verbose" +/// } +/// ``` +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::macros::{completion, suggest}; +/// use mingling::{ShellContext, Suggest}; +/// +/// #[completion(MyEntry)] +/// fn complete(ctx: &ShellContext) -> Suggest { +/// if ctx.typing_argument() { +/// return suggest! { +/// "--name": "Provide a name", +/// "--type": "Select a type" +/// }.strip_typed_argument(ctx); +/// } +/// suggest!() +/// } +/// ``` +/// +/// # Related +/// +/// - [`suggest_enum!`](macro.suggest_enum.html) — Build suggestions from an [`EnumTag`] enum. #[cfg(feature = "comp")] #[proc_macro] pub fn suggest(input: TokenStream) -> TokenStream { suggest::suggest(input) } +/// Builds a [`Suggest`] instance from an [`EnumTag`] enum's variants. +/// +/// **This macro is only available with the `comp` feature.** +/// +/// The `suggest_enum!` macro iterates over all variants of an [`EnumTag`]-derived +/// enum and creates suggestion items using each variant's display name +/// (from `#[enum_rename]`) and description (from `#[enum_desc]`). +/// +/// # Syntax +/// +/// ```rust,ignore +/// suggest_enum!(MyEnumType); +/// ``` +/// +/// # Example +/// +/// ```rust,ignore +/// use mingling::macros::{completion, suggest_enum}; +/// use mingling::{ShellContext, Suggest, EnumTag}; +/// +/// #[derive(EnumTag)] +/// enum Fruit { +/// #[enum_desc("A sweet red fruit")] +/// #[enum_rename("apple")] +/// Apple, +/// #[enum_desc("A yellow tropical fruit")] +/// #[enum_rename("banana")] +/// Banana, +/// } +/// +/// #[completion(MyEntry)] +/// fn complete(ctx: &ShellContext) -> Suggest { +/// if ctx.filling_argument_first("--fruit") { +/// return suggest_enum!(Fruit); +/// } +/// suggest!() +/// } +/// ``` +/// +/// # Generated code equivalent +/// +/// ```rust,ignore +/// { +/// let mut enum_suggest = Suggest::new(); +/// for (name, desc) in <Fruit>::enums() { +/// if desc.is_empty() { +/// enum_suggest.insert(SuggestItem::new(name.to_string())); +/// } else { +/// enum_suggest.insert(SuggestItem::new_with_desc(name.to_string(), desc.to_string())); +/// } +/// } +/// enum_suggest +/// } +/// ``` +/// +/// # Related +/// +/// - [`suggest!`](macro.suggest.html) — Build suggestions with inline syntax. +/// - [`EnumTag`](derive.EnumTag.html) — The derive macro required for the enum type. #[cfg(feature = "comp")] #[proc_macro] pub fn suggest_enum(input: TokenStream) -> TokenStream { diff --git a/mingling_macros/src/node.rs b/mingling_macros/src/node.rs index be47374..b3a61c6 100644 --- a/mingling_macros/src/node.rs +++ b/mingling_macros/src/node.rs @@ -1,8 +1,3 @@ -//! Command Node Macro Implementation -//! -//! This module provides the `node` procedural macro for creating -//! command nodes from dot-separated path strings. - use just_fmt::kebab_case; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; diff --git a/mingling_macros/src/pack.rs b/mingling_macros/src/pack.rs index f7bdfe3..10a34e4 100644 --- a/mingling_macros/src/pack.rs +++ b/mingling_macros/src/pack.rs @@ -1,8 +1,3 @@ -//! Chain Struct Macro Implementation -//! -//! This module provides the `pack!` macro for creating wrapper types -//! with automatic implementations of common traits. - use proc_macro::TokenStream; use quote::quote; use syn::parse::{Parse, ParseStream}; diff --git a/mingling_macros/src/program_setup.rs b/mingling_macros/src/program_setup.rs index ee6f054..60a11a9 100644 --- a/mingling_macros/src/program_setup.rs +++ b/mingling_macros/src/program_setup.rs @@ -1,8 +1,3 @@ -//! Setup Attribute Macro Implementation -//! -//! This module provides the `#[program_setup]` attribute macro for automatically -//! generating structs that implement the `ProgramSetup` trait from functions. - use proc_macro::TokenStream; use quote::quote; use syn::spanned::Spanned; diff --git a/mingling_macros/src/render.rs b/mingling_macros/src/render.rs index 3f1bbe8..63ec49c 100644 --- a/mingling_macros/src/render.rs +++ b/mingling_macros/src/render.rs @@ -1,8 +1,3 @@ -//! Render Macros Module -//! -//! This module provides procedural macros for rendering operations. -//! These macros expect a mutable reference to a `RenderResult` named `r` to be in scope. - use proc_macro::TokenStream; use quote::quote; use syn::parse::Parser; diff --git a/mingling_macros/src/renderer.rs b/mingling_macros/src/renderer.rs index 7ab1ac1..89b2188 100644 --- a/mingling_macros/src/renderer.rs +++ b/mingling_macros/src/renderer.rs @@ -135,6 +135,7 @@ pub fn renderer_attr(item: TokenStream) -> TokenStream { let mut dummy_r = ::mingling::RenderResult::default(); let r = &mut dummy_r; #fn_body + println!("{}", r.trim()); } }; |
