{"id":51330696,"url":"https://github.com/mingling-rs/mingling","last_synced_at":"2026-07-01T23:00:39.559Z","repository":{"id":347709351,"uuid":"1195067508","full_name":"mingling-rs/mingling","owner":"mingling-rs","description":"Macro magician in your CLI. ","archived":false,"fork":false,"pushed_at":"2026-07-01T21:06:59.000Z","size":4897,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-07-01T23:00:16.021Z","etag":null,"topics":["bash","cli","command-line","command-line-interface","completion","crate","fish","framework","library","macros","powershell","proc-macro","proc-macro-attribute","proc-macro-derive","repl","rust","shell","zsh"],"latest_commit_sha":null,"homepage":"https://mingling-rs.github.io/mingling/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mingling-rs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-29T07:05:01.000Z","updated_at":"2026-07-01T21:02:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mingling-rs/mingling","commit_stats":null,"previous_names":["catilgrass/mingling","mingling-rs/mingling"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/mingling-rs/mingling","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mingling-rs%2Fmingling","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mingling-rs%2Fmingling/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mingling-rs%2Fmingling/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mingling-rs%2Fmingling/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mingling-rs","download_url":"https://codeload.github.com/mingling-rs/mingling/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mingling-rs%2Fmingling/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35025983,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","cli","command-line","command-line-interface","completion","crate","fish","framework","library","macros","powershell","proc-macro","proc-macro-attribute","proc-macro-derive","repl","rust","shell","zsh"],"created_at":"2026-07-01T23:00:17.860Z","updated_at":"2026-07-01T23:00:39.546Z","avatar_url":"https://github.com/mingling-rs.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/mingling-rs/mingling\"\u003e\n        \u003cimg alt=\"Mingling\" src=\"https://github.com/mingling-rs/mingling/raw/main/docs/res/icon2.png\" width=\"50%\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\u003ch1 align=\"center\"\u003eMìng Lìng - 命令\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cb\u003e/mɪŋ lɪŋ/\u003c/b\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    Macro magician in your CLI.\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/github/license/mingling-rs/mingling\"\u003e\n\t\u003cimg src=\"https://img.shields.io/github/stars/mingling-rs/mingling?style=flat\"\u003e \n\t\u003cimg src=\"https://img.shields.io/crates/size/mingling\"\u003e\n\t\u003cimg src=\"https://img.shields.io/crates/v/mingling?style=flat\"\u003e\n\t\u003cimg src=\"https://img.shields.io/docsrs/mingling?style=flat\"\u003e\n\u003c/p\u003e\n\n\u003e [!WARNING]\n\u003e\n\u003e **Note**: Mingling is still under active development, and its API may change. Feel free to try it out and give us feedback!\n\u003e **Hint**: This note will be removed in version `0.5.0`\n\n\u003ch1 align=\"center\"\u003e\n  What is Mingling?\n\u003c/h1\u003e\n\n[`Mingling`](https://github.com/mingling-rs/mingling) is a **proc-macro and type-system based** Rust CLI framework, suitable for developing complex command-line programs with numerous subcommands.\n\n\u003e Its name comes from the Chinese Pinyin **\"Mìng Lìng\"**, meaning **\"Command\"**.\n\n### Mingling's Core Capabilities\n\n1. **Separation of Concerns, Clear Logic**: Mingling decouples logic by responsibility, helping you organize your CLI program more clearly.\n   See example: [Example](https://github.com/mingling-rs/mingling/blob/main/examples/example-basic/src/main.rs)\n2. **\"All Logic is Functions\"**: Execution logic, rendering logic, completion logic, help logic — everything is a function. Just attach the corresponding attribute macro to bind them to your program.\n3. **Fully Dynamic Completion System**: With the `comp` feature, you can flexibly implement dynamic completion logic for any subcommand.\n   See examples: [Example](https://github.com/mingling-rs/mingling/blob/main/examples/example-completion/src/main.rs)\n4. **Lightning-Fast Subcommand Dispatch**: With the `dispatch_tree` feature, Mingling hardens the subcommand structure into a prefix tree at **compile time**, enabling blazing-fast subcommand lookup.\n   See examples: [Example](https://github.com/mingling-rs/mingling/blob/main/examples/example-dispatch-tree/src/main.rs)\n5. **Lightweight Dependencies, On-Demand Importing**: Minimal core dependencies keep builds fast; enhanced features are imported on demand through fine-grained feature flags.\n6. **Structured Output**: Enabling the `structural_renderer` feature adds support for flags like `--json` and `--yaml`, providing structured output capabilities.\n   See examples: [Example](https://github.com/mingling-rs/mingling/blob/main/examples/example-structural-renderer/src/main.rs)\n\n---\n\n**💡 To learn more, check out the following links:**\n\n- 📖 [Mainpage](https://mingling-rs.github.io/mingling/)\n- 📖 [Examples](https://mingling-rs.github.io/mingling/docs/examples.html)\n- 📖 [docs.rs](https://docs.rs/mingling/latest/mingling/)\n- 📖 Helpdoc [EN](https://mingling-rs.github.io/mingling/docs/doc.html#/) [中文](https://mingling-rs.github.io/mingling/docs/_zh_CN/index.html#/)\n\n\u003ch1 align=\"center\"\u003e\n    Getting Started\n\u003c/h1\u003e\n\nAdd Mingling to your `Cargo.toml`:\n\n```toml\n[dependencies.mingling]\nversion = \"0.2.1\"\nfeatures = []\n```\n\nOr use the github version\n\n```toml\n[dependencies.mingling]\ngit = \"https://github.com/mingling-rs/mingling.git\"\ntag = \"unreleased\"\nfeatures = []\n```\n\nOr use the [template project](https://github.com/mingling-rs/mingling-template):\n\n```bash\ncargo generate --git mingling-rs/mingling-template\n```\n\n\u003ch1 align=\"center\"\u003e\n    Writing with Mingling\n\u003c/h1\u003e\n\n### The Big Picture\n\nMingling organizes your CLI program into three distinct phases:\n\n```\nUser Input → [Dispatcher] → Entry → [Chain(s)] → Result → [Renderer] → Output\n```\n\n**Step1: Input** — The user's raw arguments flow in.\n**Step2: Dispatch** — A **Dispatcher** picks them up and wraps them into an **Entry** type.\n**Step3: Chain** — The entry is handed off to a **Chain** function, which processes it.\n**Step4: Render** — A **Renderer** takes that result and writes it to the terminal.\n\n\u003e [!NOTE]\n\u003e A Chain can produce a **State** type to be passed to the next Chain for further processing,\n\u003e\n\u003e or it can produce a **Result** type to be handed off to a Renderer.\n\nEverything in this pipeline is a **plain Rust function** with an attribute macro on top.\n\nYou never need to manually implement traits or construct boilerplate.\n\n---\n\n### 1. Defining Commands — `dispatcher!`\n\nThe entry point for every subcommand is the `dispatcher!` macro. It generates two structs for you: a **Dispatcher** (used to register the command with the program) and an **Entry** (a wrapper around `Vec\u003cString\u003e` that holds the raw arguments).\n\n```rust\nuse mingling::prelude::*;\n\n//           \"command.name\" Dispatcher  EntryType\n//           │              │           │\ndispatcher!(\"greet\",        CMDGreet =\u003e EntryGreet);\n\n// Nested subcommand: `remote add`\ndispatcher!(\"remote.add\",   CMDRemoteAdd =\u003e EntryRemoteAdd);\n```\n\nThen in `main()`, register the dispatcher with the program:\n\n```rust\ndispatcher!(\"greet\", CMDGreet =\u003e EntryGreet);\n\nfn main() {\n    let mut program = ThisProgram::new();\n    program.with_dispatcher(CMDGreet);\n    program.exec_and_exit();\n}\n```\n\nMingling also supports an abbreviated form (with the `extra_macros` feature):\n\n```rust\n// Features: [\"extra_macros\"]\n\n// Auto-generates CMDGreet / EntryGreet from \"greet\"\ndispatcher!(\"greet\");\n```\n\n---\n\n### 2. The Chain — \"#[chain]\" — Where Logic Lives\n\nThe `#[chain]` attribute turns a plain function into an execution step. Think of it as \"the logic that transforms one typed value into another.\"\n\n```rust\ndispatcher!(\"greet\", CMDGreet =\u003e EntryGreet);\n\npack!(ResultGreeting = String);\n\n#[chain]\nfn handle_greet(args: EntryGreet) -\u003e Next {\n    let greeting = args\n        .inner\n        .first()\n        .cloned()\n        .unwrap_or_else(|| \"World\".to_string());\n    ResultGreeting::new(greeting)\n}\n```\n\nKey points:\n\n- The return type is `Next` — a type alias for `ChainProcess\u003cThisProgram\u003e`.\n- You chain results by calling `.to_chain()` on any `pack!`-ed type.\n- You can have **multiple chain functions** for the same command, each transforming the data further.\n- With the `async` feature, chain functions can be `async fn`.\n\n---\n\n### 3. The Renderer — \"#[renderer]\" — How Output Works\n\nThe `#[renderer]` attribute turns a function into an output handler. It receives the final result of a chain and writes it to the terminal.\n\n```rust\npack!(ResultGreeting = String);\n\n#[renderer]\nfn render_greeting(greeting: ResultGreeting) {\n    r_println!(\"Hello, {}!\", *greeting);\n}\n```\n\nInside a renderer, use `r_print!` / `r_println!` to write to the output buffer. This is not `println!` — it writes into Mingling's internal `RenderResult` buffer, which is flushed at the end of the pipeline.\n\nYou can write renderers for **any type** in your program, including error types:\n\n```rust\nuse mingling::prelude::*;\n\n#[renderer]\nfn render_dispatcher_not_found(err: ErrorDispatcherNotFound) {\n    r_println!(\"Command not found: [{}]\", err.join(\" \"));\n}\n```\n\n---\n\n### 4. Parsing Arguments — The Picker\n\nMingling provides a **Picker** for zero-cost argument extraction. You use `pick()` or `pick_or()` on an entry to extract typed values, then `unpack()` to get the final tuple.\n\n```rust\n// Features: [\"parser\"]\n\nuse mingling::parser::Picker;\n\ndispatcher!(\"greet\", CMDGreet =\u003e EntryGreet);\npack!(ResultGreeting = String);\n\n#[chain]\nfn handle_greet(args: EntryGreet) -\u003e Next {\n    let (name, count) = Picker::new(args.inner)\n        .pick::\u003cString\u003e(())                   // positional: first string\n        .pick_or::\u003cu8\u003e([\"-r\", \"--repeat\"], 1) // optional flag with default\n        .unpack();\n    ResultGreeting::new(format!(\"{} x{}\", name, count))\n}\n```\n\nWith the `parser` feature, the `AsPicker` trait provides a shorthand directly on entries:\n\n```rust\n// Features: [\"parser\"]\n\ndispatcher!(\"greet\", CMDGreet =\u003e EntryGreet);\npack!(ResultGreeting = String);\n\n#[chain]\nfn handle(args: EntryGreet) -\u003e Next {\n    let (name, count) = args\n        .pick::\u003cOption\u003cString\u003e\u003e(())\n        .pick_or::\u003cu8\u003e([\"-r\", \"--repeat\"], 1)\n        .unpack();\n    ResultGreeting::new(format!(\"{} x{}\", name.unwrap_or_default(), count))\n}\n```\n\nFor enums, derive `EnumTag` and implement `PickableEnum` to parse enum variants from strings:\n\n```rust\n// Features: [\"parser\", \"extra_macros\"]\n\nuse mingling::{EnumTag, Groupped};\nuse mingling::parser::PickableEnum;\n\ndispatcher!(\"lang.select\", CMDLang =\u003e EntryLang);\n\n#[derive(Debug, Default, EnumTag, Groupped)]\npub enum Language {\n    #[default]\n    Rust,\n    #[enum_rename(\"C++\")]\n    CPlusPlus,\n}\n\nimpl PickableEnum for Language {}\n\n#[chain]\nfn handle(args: EntryLang) -\u003e Next {\n    let lang: Language = args.pick(()).unpack();\n    lang\n}\n```\n\n---\n\n### 5. The Help System — \"#[help]\"\n\nHelp is just another attribute macro. When the user passes `--help` or `-h`, the program skips the normal chain/render pipeline and routes directly to your `#[help]` function.\n\nEnable it by adding `BasicProgramSetup`:\n\n```rust\nuse mingling::{macros::help, setup::BasicProgramSetup};\n\ndispatcher!(\"greet\", CMDGreet =\u003e EntryGreet);\n\n#[help]\nfn help_greet(_prev: EntryGreet) {\n    r_println!(\"Usage: greet \u003cNAME\u003e\");\n    r_println!(\"Greets the user with the given name.\");\n}\n\nfn main() {\n    let mut program = ThisProgram::new();\n    program.with_setup(BasicProgramSetup);  // enables --help / -h\n    program.with_dispatcher(CMDGreet);\n    program.exec_and_exit();\n}\n\ngen_program!();\n```\n\nThe flow is:\n\n- User types `greet --help`\n- `BasicProgramSetup` sets `program.user_context.help = true`\n- The dispatcher sees this flag and routes to the `#[help]` function instead of the `#[chain]`\n\n---\n\n### 6. Completion — \"#[completion]\" — Dynamic Shell Completions\n\nWith the `comp` feature, Mingling provides a fully dynamic completion system. You write a function that returns `Suggest` based on the current shell context, and Mingling generates the completion scripts for bash, zsh, fish, and pwsh.\n\n```rust\n// Features: [\"comp\", \"extra_macros\"]\n\nuse mingling::{macros::suggest, ShellContext, Suggest};\n\ndispatcher!(\"greet\", CMDGreet =\u003e EntryGreet);\npack!(ResultName = (u8, String));\n\n#[completion(EntryGreet)]\nfn complete_greet(ctx: \u0026ShellContext) -\u003e Suggest {\n    // Suggest positional arguments\n    if ctx.previous_word == \"greet\" {\n        return suggest! {\n            \"Alice\": \"Likes to receive messages\",\n            \"Bob\":   \"Likes to pass messages\",\n            \"World\"\n        };\n    }\n\n    // Suggest flag arguments\n    if ctx.typing_argument() {\n        return suggest! {\n            \"-r\":        \"Number of repetitions\",\n            \"--repeat\":  \"Number of repetitions\",\n        }\n        .strip_typed_argument(ctx);\n    }\n\n    suggest!()  // no suggestions\n}\n```\n\nYou also need to register the built-in completion dispatcher:\n\n```rust\n// Features: [\"comp\"]\n\nfn main() {\n    let mut program = ThisProgram::new();\n    program.with_dispatcher(crate::CMDCompletion);\n    program.exec_and_exit();\n}\n```\n\nIn your `build.rs`, generate the shell scripts:\n\n```rust\n// BUILD TIME\n// Features: [\"comp\", \"builds\"]\nmingling::build::build_comp_scripts(env!(\"CARGO_PKG_NAME\")).unwrap();\n```\n\nFor enum-based completions, use `suggest_enum!`:\n\n```rust\n// Features: [\"comp\", \"extra_macros\"]\n\nuse mingling::{ShellContext, Suggest};\nuse mingling::macros::suggest_enum;\nuse mingling::EnumTag;\n\ndispatcher!(\"lang.select\", CMDLang =\u003e EntryLang);\n\n#[derive(EnumTag)]\npub enum ProgrammingLanguages {\n    Rust,\n    Python,\n    JavaScript,\n}\n\n#[completion(EntryLang)]\nfn complete_lang(_: \u0026ShellContext) -\u003e Suggest {\n    suggest_enum!(ProgrammingLanguages)\n}\n```\n\n---\n\n### 7. Error Handling\n\nMingling doesn't use `?` operator propagation. Instead, errors are just **alternative results** that flow through the same chain/render pipeline. Create error types with `pack!` and route to them with `.to_render()`:\n\n```rust\ndispatcher!(\"hello\", CMDHello =\u003e EntryHello);\npack!(ResultName = String);\npack!(ErrorNoNameProvided = ());\npack!(ErrorNameTooLong = u16);\n\n#[chain]\nfn handle(args: EntryHello) -\u003e Next {\n    let Some(name) = args.inner.first().cloned() else {\n        return ErrorNoNameProvided::default().to_render();  // ← early return to error renderer\n    };\n\n    if name.len() \u003e 10 {\n        return ErrorNameTooLong::new(name.len() as u16).to_render();\n    }\n\n    ResultName::new(name).to_render()  // ← success path\n}\n\n#[renderer]\nfn render_no_name(_: ErrorNoNameProvided) {\n    r_println!(\"No name provided\");\n}\n\n#[renderer]\nfn render_too_long(len: ErrorNameTooLong) {\n    r_println!(\"Name too long: {} \u003e 10\", *len);\n}\n```\n\nTwo built-in fallback types are always available:\n\n- `ErrorDispatcherNotFound` — rendered when no dispatcher matches the input\n- `ErrorRendererNotFound` — rendered when no renderer is found for a result type\n\n---\n\n### 8. Resource Injection\n\nChain and renderer functions can accept **additional parameters** for the program's global state. Resources are singleton values registered with `program.with_resource(...)`.\n\n```rust\n// Features: [\"parser\", \"extra_macros\"]\n\nuse std::path::PathBuf;\n\ndispatcher!(\"current\", CMDCurrent =\u003e EntryCurrent);\ndispatcher!(\"cd\", CMDCd =\u003e EntryCd);\n\n#[derive(Default, Clone)]\nstruct ResCurrentDir {\n    current_dir: PathBuf,\n}\n\nfn main() {\n    let mut program = ThisProgram::new();\n    program.with_resource(ResCurrentDir {\n        current_dir: std::env::current_dir().unwrap(),\n    });\n    program.with_dispatcher(CMDCurrent);\n    program.with_dispatcher(CMDCd);\n    program.exec_and_exit();\n}\n\n// Read-only access (shared reference):\n#[chain]\nfn show_current(_prev: EntryCurrent, current_dir: \u0026ResCurrentDir) -\u003e Next {\n    println!(\"Current: {}\", current_dir.current_dir.display());\n    empty_result!()\n}\n\n// Mutable access:\n#[chain]\nfn change_dir(prev: EntryCd, current_dir: \u0026mut ResCurrentDir) -\u003e Next {\n    let path: String = prev.pick(()).unpack();\n    current_dir.current_dir = current_dir.current_dir.join(path);\n    empty_result!()\n}\n```\n\nResources can also be injected into `#[renderer]`:\n\n```rust\n\ndispatcher!(\"current\", CMDCurrent =\u003e EntryCurrent);\n\n#[derive(Default, Clone)]\nstruct ResCurrentDir {\n    current_dir: std::path::PathBuf,\n}\n\n#[renderer]\nfn render_current(_: EntryCurrent, current_dir: \u0026ResCurrentDir) {\n    r_println!(\"Current directory: {}\", current_dir.current_dir.display());\n}\n```\n\n---\n\n### 9. Dispatch Tree — Compile-Time Command Trie\n\nAs your program grows to dozens or hundreds of subcommands, linear dispatcher lookup becomes slow. Enable the `dispatch_tree` feature to convert the command structure into a **prefix tree (Trie)** at compile time.\n\n```rust\n// Features: [\"dispatch_tree\"]\n\ndispatcher!(\"cmd1\",              CMD1 =\u003e Entry1);\ndispatcher!(\"cmd2.sub1\",         CMD2Sub1 =\u003e Entry2Sub1);\ndispatcher!(\"cmd2.sub2\",         CMD2Sub2 =\u003e Entry2Sub2);\ndispatcher!(\"cmd3.sub1.leaf1\",   CMD3Sub1Leaf1 =\u003e Entry3Sub1Leaf1);\ndispatcher!(\"cmd3.sub1.leaf2\",   CMD3Sub1Leaf2 =\u003e Entry3Sub1Leaf2);\n// ... dozens more\n\nfn main() {\n    let program = ThisProgram::new();\n    // No more with_dispatcher calls — it's all compile-time!\n    program.exec_and_exit();\n}\n```\n\nWith `dispatch_tree` enabled:\n\n- Dispatchers are auto-collected at compile time\n- `Program` no longer stores a dispatcher list\n- `program.with_dispatcher(...)` is not compiled\n- Lookup is **O(n)** where _n_ is input length, not number of commands\n\n---\n\n### 10. Clap Binding — Using Clap's Parser\n\nIf you prefer clap's powerful argument parsing, use `#[dispatcher_clap]`. It generates a dispatcher from a `clap::Parser` struct.\n\n```rust\n// Features: [\"clap\"]\n// Dependencies:\n// clap = \"4\"\n\nuse mingling::macros::dispatcher_clap;\nuse mingling::Groupped;\n\n#[derive(Default, clap::Parser, Groupped)]\n#[dispatcher_clap(\n    \"greet\", CMDGreet,\n    help = true,              // auto-generate #[help] from clap\n    error = ErrorGreetParsed, // capture parse errors as a renderable type\n)]\npub struct EntryGreet {\n    #[clap(default_value = \"World\")]\n    name: String,\n\n    #[arg(short, long, default_value_t = 1)]\n    repeat: i32,\n}\n\n#[renderer]\nfn render_greet(greet: EntryGreet) {\n    r_print!(\"Hello, \");\n    for _ in 0..greet.repeat { r_print!(\"{}\", greet.name); }\n    r_println!(\"!\");\n}\n\n#[renderer]\nfn render_parse_error(err: ErrorGreetParsed) {\n    r_println!(\"{}\", *err);\n}\n```\n\nYou can control how clap help is displayed:\n\n```rust\n// Features: [\"clap\"]\n\ndispatcher!(\"greet\", CMDGreet =\u003e EntryGreet);\n\nfn main() {\n    let mut program = ThisProgram::new();\n    program.with_dispatcher(CMDGreet);\n    program.stdout_setting.clap_help_print_behaviour =\n        mingling::ClapHelpPrintBehaviour::WriteToRenderResult;\n    // or: PrintDirectly — writes clap help straight to stdout\n    program.exec_and_exit();\n}\n```\n\n---\n\n### 11. REPL Mode\n\nWith the `repl` feature, turn your CLI into an interactive shell with one method call:\n\n```rust\n// Features: [\"repl\"]\n\nfn main() {\n    ThisProgram::new().exec_repl();\n}\n```\n\nMingling provides built-in REPL setups:\n\n```rust\n// Features: [\"repl\", \"extra_macros\"]\n\nuse mingling::{\n    res::ResREPL,\n    setup::{BasicREPLReadlineSetup, BasicREPLOutputSetup, BasicREPLPromptSetup},\n};\n\ndispatcher!(\"cd\", CMDCd =\u003e EntryCd);\ndispatcher!(\"exit\", CMDExit =\u003e EntryExit);\n\nfn main() {\n    let mut program = ThisProgram::new();\n\n    program.with_dispatcher(CMDCd);\n    program.with_dispatcher(CMDExit);\n\n    // Enable line reading from stdin\n    program.with_setup(BasicREPLReadlineSetup);\n\n    // Enable output flushing after each render\n    program.with_setup(BasicREPLOutputSetup);\n\n    // Custom prompt\n    program.with_setup(BasicREPLPromptSetup::func(|| \"\u003e \".to_string()));\n\n    program.exec_repl();  // ← interactive loop\n}\n\n// Exit the REPL via the ResREPL resource:\n#[chain]\nfn handle_exit(_prev: EntryExit, repl: \u0026mut ResREPL) {\n    repl.exit = true;\n}\n```\n\n---\n\n### 12. Hooks — Observing the Pipeline\n\nMingling provides a `ProgramHook` system for observing every stage of the execution pipeline. Useful for debugging, logging, or telemetry.\n\n```rust\nuse mingling::{\n    hook::{ProgramControlUnit, ProgramHook},\n};\n\ndispatcher!(\"greet\", CMDGreet =\u003e EntryGreet);\n\nfn main() {\n    let mut program = ThisProgram::new();\n\n    program.with_hook(\n        ProgramHook::\u003cThisProgram\u003e::empty()\n            .on_begin::\u003c_, ()\u003e(|_| println!(\"[DEBUG] Program is begin\"))\n            .on_pre_dispatch(|info| println!(\"[DEBUG] Pre dispatch: {}\", info.arguments.join(\" \")))\n            .on_post_dispatch(|info| println!(\"[DEBUG] Post dispatch: {}\", info.entry))\n            .on_pre_chain(|info| {\n                println!(\"[DEBUG] Pre chain: {}\", info.input);\n            })\n            .on_post_chain(|info| println!(\"[DEBUG] Post chain: {}\", info.output.member_id))\n            .on_finish(|_| {\n                println!(\"[DEBUG] Loop end\");\n                ProgramControlUnit::OverrideExitCode(0) // Override exit code\n            })\n            .on_pre_render(|info| println!(\"[DEBUG] Pre render: {}\", info.input))\n            .on_post_render(|_| println!(\"[DEBUG] Post render\")),\n    );\n\n    program.with_dispatcher(CMDGreet);\n    program.exec_and_exit();\n}\n```\n\n---\n\n### 13. Structural Renderer — Structured Output (JSON/YAML)\n\nWith the `structural_renderer` feature, users can add `--json` or `--yaml` flags to get structured output instead of human-readable text.\n\n```rust\n// Features: [\"structural_renderer\", \"parser\"]\n// Dependencies:\n// serde = \"1\"\n\nuse mingling::{prelude::*, setup::StructuralRendererSetup};\nuse mingling::Groupped;\nuse mingling::StructuralData;\nuse serde::Serialize;\n\ndispatcher!(\"render\", CMDRender =\u003e EntryRender);\n\n#[derive(Default, StructuralData, Serialize, Groupped)]\nstruct ResultInfo {\n    name: String,\n    age: i32,\n}\n\n#[chain]\nfn render_info(args: EntryRender) -\u003e Next {\n    let (name, age) = args.pick::\u003cString\u003e(()).pick::\u003ci32\u003e(()).unpack();\n    ResultInfo { name, age }.to_chain()\n}\n\n#[renderer]\nfn render_info_result(info: ResultInfo) {\n    r_println!(\"{} is {} years old\", info.name, info.age);\n}\n\nfn main() {\n    let mut program = ThisProgram::new();\n    program.with_setup(StructuralRendererSetup);  // enables --json / --yaml\n    program.with_dispatcher(CMDRender);\n    let _ = program.exec();\n}\n```\n\nThen users can do:\n\n```bash\n$ myapp render Bob 22\nBob is 22 years old\n\n$ myapp render Bob 22 --json\n{\"name\":\"Bob\",\"age\":22}\n\n$ myapp render Bob 22 --yaml\nname: Bob\nage: 22\n```\n\n---\n\n### 14. Async Support\n\nEnable the `async` feature to use `async fn` inside `#[chain]`:\n\n```rust\n// Features: [\"async\", \"parser\"]\n// Dependencies:\n// tokio = { version = \"1\", features = [\"full\"] }\n\nuse std::time::Duration;\n\ndispatcher!(\"download\", CMDDownload =\u003e EntryDownload);\npack!(ResultDownloaded = String);\n\n#[chain]\npub async fn handle_download(args: EntryDownload) -\u003e Next {\n    let file = args.pick(()).unpack();\n    download_file(file).await\n}\n\nasync fn download_file(name: String) -\u003e ResultDownloaded {\n    tokio::time::sleep(Duration::from_secs(1)).await;\n    ResultDownloaded::new(name)\n}\n\n#[renderer]\nfn render_downloaded(result: ResultDownloaded) {\n    r_println!(\"\\\"{}\\\" downloaded.\", *result);\n}\n```\n\n\u003e [!NOTE]\n\u003e\n\u003e `#[renderer]` functions cannot be async. When `async` is enabled, `program.exec_and_exit().await` returns a Future.\n\n---\n\n### 15. Wrapping Up — `gen_program!()`\n\nAt the very end of your crate root (main.rs / lib.rs), call `gen_program!()` to generate the `ThisProgram` struct, the `Next` type alias, and all internal plumbing.\n\n```rust\nuse mingling::macros::gen_program;\n\ngen_program!();\n```\n\nIt must be placed **after** all your `dispatcher!`, `pack!`, `#[chain]`, `#[renderer]`, and `#[help]` declarations.\n\n---\n\n### Putting It All Together\n\nHere's a complete, runnable program:\n\n```rust\ndispatcher!(\"greet\", CMDGreet =\u003e EntryGreet);\n\nfn main() {\n    let mut program = ThisProgram::new();\n    program.with_dispatcher(CMDGreet);\n    program.exec_and_exit();\n}\n\npack!(ResultGreeting = String);\n\n#[chain]\nfn handle_greet(args: EntryGreet) -\u003e Next {\n    let greeting = args\n        .inner\n        .first()\n        .cloned()\n        .unwrap_or_else(|| \"World\".to_string());\n    ResultGreeting::new(greeting)\n}\n\n#[renderer]\nfn render_greeting(greeting: ResultGreeting) {\n    r_println!(\"Hello, {}!\", *greeting);\n}\n\ngen_program!();\n```\n\n```bash\n$ myapp greet\nHello, World!\n\n$ myapp greet Alice\nHello, Alice!\n```\n\n\u003ch1 align=\"center\"\u003e\n    🗺️ Roadmap 🗺️\n\u003c/h1\u003e\n\n- [x] Milestone.1 \"MVP\" 🎉\n  - [x] [[0.1.4](https://docs.rs/mingling/0.1.4/mingling/)] [`core`] [`structural_renderer`] **Mingling** can render data into serializable formats via `--json` and `--yaml` flags\n  - [x] [[0.1.5](https://docs.rs/mingling/0.1.5/mingling/)] [`core`] [`comp`] **Mingling** can dynamically invoke itself to provide completions for shells like `bash`, `zsh`, `fish`, and `pwsh`\n  - [x] [[0.1.6](https://docs.rs/mingling/0.1.6/mingling/)] [`core`] [`comp`] **Mingling** can gather more context for smarter completions\n  - [x] [[0.1.7](https://docs.rs/mingling/0.1.7/mingling/)] [`clap`] Provides a **Clap** compatibility layer, allowing **Mingling** to reuse its powerful parsing capabilities\n  - [x] [[0.1.7](https://docs.rs/mingling/0.1.7/mingling/)] [`core`] **Mingling** can intercept `-h` or `--help` flags to display custom help text for each subcommand\n  - [x] [[0.1.7](https://docs.rs/mingling/0.1.7/mingling/)] [`mling`] Provides a basic scaffolding tool (`mling`) for rapid development and debugging\n  - [x] [[0.1.8](https://docs.rs/mingling/0.1.8/mingling/)] [`core`] [`dispatch_tree`] Converts the subcommand list into a prefix tree to improve command matching speed\n  - [x] [[0.1.9](https://docs.rs/mingling/0.1.9/mingling/)] [`core`] [`dev_toolkits`] Provides debugging interfaces for developers to capture invocation information when issues arise (`InvokeStackDisplay`) (indirectly implemented via `ProgramHook`)\n  - [x] [[0.1.9](https://docs.rs/mingling/0.1.9/mingling/)] [`core`] [`repl`] Provides REPL capability (`program.exec_repl();`)\n  - [x] [[0.2.0](https://docs.rs/mingling/0.2.0/mingling/)] Complete documentation, tests, and examples\n\n- [ ] Milestone.2 \"More Comfortable Dev and User Experience\"\n  - [ ] [**0.3.0**] [`macros`] `r_println!` in `#[chain]` support.\n  - [ ] [**0.5.0**] [`mling`] Helpdoc Maker\n  - [ ] [**0.7.0**] [`picker`] A more efficient and intelligent argument parser\n\n- [ ] Milestone.3 \"Unplanned\"\n  - [ ] ...\n\n\u003ch1 align=\"center\"\u003e\n    🚫 Unplanned Features 🚫\n\u003c/h1\u003e\n\nWhile Mingling has several common CLI features that are **NOT PLANNED** to be directly included in the framework.\nThis is because the Rust ecosystem already has excellent and mature crates to handle these issues, and Mingling's design is intended to be used in combination with them.\n\n- **Colored Output**: To add color and styles (bold, italic, etc.) to terminal output, consider using crates like [`colored`](https://crates.io/crates/colored) or [`owo-colors`](https://crates.io/crates/owo-colors). You can integrate their types directly into your renderers.\n- **I18n**: To translate your CLI application, the [`rust-i18n`](https://crates.io/crates/rust-i18n) crate provides a powerful internationalization solution that you can use in your command logic and renderers.\n- **Progress Bars**: To display progress indicators, the [`indicatif`](https://crates.io/crates/indicatif) crate is the standard choice.\n- **TUI**: To build full-screen interactive terminal applications, it is recommended to use a framework like [`ratatui`](https://crates.io/crates/ratatui) (formerly `tui-rs`).\n\n\u003ch1 align=\"center\"\u003e\n    📄 Open Source License 📄\n\u003c/h1\u003e\n\nThis project is licensed under the MIT License.\n\nSee [LICENSE-MIT](LICENSE-MIT) or [LICENSE-APACHE](LICENSE-APACHE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmingling-rs%2Fmingling","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmingling-rs%2Fmingling","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmingling-rs%2Fmingling/lists"}