> [!WARNING]
>
> **Note**: Mingling is still under active development, and its API may change. Feel free to try it out and give us feedback!
> **Hint**: This note will be removed in version `0.5.0`
What is Mingling?
[`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.
> Its name comes from the Chinese Pinyin **"Mìng Lìng"**, meaning **"Command"**.
### Mingling's Core Capabilities
1. **Separation of Concerns, Clear Logic**: Mingling decouples logic by responsibility, helping you organize your CLI program more clearly.
See example: [Example](https://github.com/mingling-rs/mingling/blob/main/examples/example-basic/src/main.rs)
2. **"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.
3. **Fully Dynamic Completion System**: With the `comp` feature, you can flexibly implement dynamic completion logic for any subcommand.
See examples: [Example](https://github.com/mingling-rs/mingling/blob/main/examples/example-completion/src/main.rs)
4. **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.
See examples: [Example](https://github.com/mingling-rs/mingling/blob/main/examples/example-dispatch-tree/src/main.rs)
5. **Lightweight Dependencies, On-Demand Importing**: Minimal core dependencies keep builds fast; enhanced features are imported on demand through fine-grained feature flags.
6. **Structured Output**: Enabling the `structural_renderer` feature adds support for flags like `--json` and `--yaml`, providing structured output capabilities.
See examples: [Example](https://github.com/mingling-rs/mingling/blob/main/examples/example-structural-renderer/src/main.rs)
---
**💡 To learn more, check out the following links:**
- 📖 [Mainpage](https://mingling-rs.github.io/mingling/)
- 📖 [Examples](https://mingling-rs.github.io/mingling/docs/examples.html)
- 📖 [docs.rs](https://docs.rs/mingling/latest/mingling/)
- 📖 Helpdoc [EN](https://mingling-rs.github.io/mingling/docs/doc.html#/) [中文](https://mingling-rs.github.io/mingling/docs/_zh_CN/index.html#/)
Getting Started
Add Mingling to your `Cargo.toml`:
```toml
[dependencies.mingling]
version = "0.2.0"
features = []
```
Or use the github version
```toml
[dependencies.mingling]
git = "https://github.com/mingling-rs/mingling.git"
tag = "unreleased"
features = []
```
Or use the [template project](https://github.com/mingling-rs/mingling-template):
```bash
cargo generate --git mingling-rs/mingling-template
```
Writing with Mingling
### The Big Picture
Mingling organizes your CLI program into three distinct phases:
```
User Input → [Dispatcher] → Entry → [Chain(s)] → Result → [Renderer] → Output
```
**Step1: Input** — The user's raw arguments flow in.
**Step2: Dispatch** — A **Dispatcher** picks them up and wraps them into an **Entry** type.
**Step3: Chain** — The entry is handed off to a **Chain** function, which processes it.
**Step4: Render** — A **Renderer** takes that result and writes it to the terminal.
> [!NOTE]
> A Chain can produce a **State** type to be passed to the next Chain for further processing,
>
> or it can produce a **Result** type to be handed off to a Renderer.
Everything in this pipeline is a **plain Rust function** with an attribute macro on top.
You never need to manually implement traits or construct boilerplate.
---
### 1. Defining Commands — `dispatcher!`
The 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` that holds the raw arguments).
```rust
use mingling::prelude::*;
// "command.name" Dispatcher EntryType
// │ │ │
dispatcher!("greet", CMDGreet => EntryGreet);
// Nested subcommand: `remote add`
dispatcher!("remote.add", CMDRemoteAdd => EntryRemoteAdd);
```
Then in `main()`, register the dispatcher with the program:
```rust
dispatcher!("greet", CMDGreet => EntryGreet);
fn main() {
let mut program = ThisProgram::new();
program.with_dispatcher(CMDGreet);
program.exec_and_exit();
}
```
Mingling also supports an abbreviated form (with the `extra_macros` feature):
```rust
// Features: ["extra_macros"]
// Auto-generates CMDGreet / EntryGreet from "greet"
dispatcher!("greet");
```
---
### 2. The Chain — "#[chain]" — Where Logic Lives
The `#[chain]` attribute turns a plain function into an execution step. Think of it as "the logic that transforms one typed value into another."
```rust
dispatcher!("greet", CMDGreet => EntryGreet);
pack!(ResultGreeting = String);
#[chain]
fn handle_greet(args: EntryGreet) -> Next {
let greeting = args
.inner
.first()
.cloned()
.unwrap_or_else(|| "World".to_string());
ResultGreeting::new(greeting)
}
```
Key points:
- The return type is `Next` — a type alias for `ChainProcess`.
- You chain results by calling `.to_chain()` on any `pack!`-ed type.
- You can have **multiple chain functions** for the same command, each transforming the data further.
- With the `async` feature, chain functions can be `async fn`.
---
### 3. The Renderer — "#[renderer]" — How Output Works
The `#[renderer]` attribute turns a function into an output handler. It receives the final result of a chain and writes it to the terminal.
```rust
pack!(ResultGreeting = String);
#[renderer]
fn render_greeting(greeting: ResultGreeting) {
r_println!("Hello, {}!", *greeting);
}
```
Inside 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.
You can write renderers for **any type** in your program, including error types:
```rust
use mingling::prelude::*;
#[renderer]
fn render_dispatcher_not_found(err: ErrorDispatcherNotFound) {
r_println!("Command not found: [{}]", err.join(" "));
}
```
---
### 4. Parsing Arguments — The Picker
Mingling 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.
```rust
// Features: ["parser"]
use mingling::parser::Picker;
dispatcher!("greet", CMDGreet => EntryGreet);
pack!(ResultGreeting = String);
#[chain]
fn handle_greet(args: EntryGreet) -> Next {
let (name, count) = Picker::new(args.inner)
.pick::(()) // positional: first string
.pick_or::(["-r", "--repeat"], 1) // optional flag with default
.unpack();
ResultGreeting::new(format!("{} x{}", name, count))
}
```
With the `parser` feature, the `AsPicker` trait provides a shorthand directly on entries:
```rust
// Features: ["parser"]
dispatcher!("greet", CMDGreet => EntryGreet);
pack!(ResultGreeting = String);
#[chain]
fn handle(args: EntryGreet) -> Next {
let (name, count) = args
.pick::