From 13408e79b940e9a33ca593ed30d1b20c54e01234 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Tue, 30 Jun 2026 18:05:05 +0800 Subject: feat(docs): add Chinese and English documentation for Mingling tutorials Add comprehensive documentation covering Declare a Dispatcher, Declare a Chain, Rendering Results, Multi-Command Program, Argument Parsing with Picker and Clap, Program Setup, Error Handling, Help Info, Resource System, Exit Code Control, Hook System, Testing, Completion, Structural Rendering, and Core Concepts --- docs/pages/5-multiple-commands.md | 109 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 docs/pages/5-multiple-commands.md (limited to 'docs/pages/5-multiple-commands.md') diff --git a/docs/pages/5-multiple-commands.md b/docs/pages/5-multiple-commands.md new file mode 100644 index 0000000..9ad7ef4 --- /dev/null +++ b/docs/pages/5-multiple-commands.md @@ -0,0 +1,109 @@ +

Multi-Command Program

+

+ Adding multiple commands to a single program +

+ +Real-world CLIs rarely have just one command. Let's extend our previous greet program by adding a second command, and see what a multi-command program looks like. + +## Adding a Second Command + +Work in the same project: + +```rust +// Declare two commands +dispatcher!("greet", CMDGreet => EntryGreet); +dispatcher!("add", CMDAdd => EntryAdd); + +pack!(ResultGreeting = String); +pack!(ResultSum = i32); + +#[chain] +fn handle_greet(args: EntryGreet) -> Next { + let name = args.inner.first().cloned().unwrap_or_else(|| "World".to_string()); + ResultGreeting::new(name) +} + +#[chain] +fn handle_add(args: EntryAdd) -> Next { + let sum: i32 = args.inner.iter().filter_map(|s| s.parse::().ok()).sum(); + ResultSum::new(sum) +} + +#[renderer] +fn render_greet(result: ResultGreeting) { + r_println!("Hello, {}!", *result); +} + +#[renderer] +fn render_sum(result: ResultSum) { + r_println!("Sum: {}", *result); +} + +fn main() { + let mut program = ThisProgram::new(); + program.with_dispatchers((CMDGreet, CMDAdd)); + program.exec_and_exit(); +} + +gen_program!(); +``` + +Both commands share the same pipeline model, but each has its own path: + +```text +> my-cli greet Alice +Hello, Alice! +> my-cli add 1 2 3 +Sum: 6 +``` + +## Registering Multiple Dispatchers + +Notice `with_dispatchers`? When you need to register multiple dispatchers, just pass them as a tuple: + +```rust +@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("add", CMDAdd => EntryAdd); +@@@pack!(ResultGreeting = String); +@@@pack!(ResultSum = i32); +@@@#[chain] fn handle_greet(_args: EntryGreet) -> Next { ResultGreeting::new("ok".into()) } +@@@#[renderer] fn render_greet(_greeting: ResultGreeting) { r_println!("hi"); } +@@@#[chain] fn handle_add(_args: EntryAdd) -> Next { ResultSum::new(0) } +@@@#[renderer] fn render_sum(_sum: ResultSum) { r_println!("sum"); } +fn main() { + let mut program = ThisProgram::new(); + program.with_dispatchers((CMDGreet, CMDAdd)); + program.exec_and_exit(); +} +``` + +This is equivalent to registering them one by one, same effect. + +> [!TIP] +> The tuple supports up to 7 dispatchers. For more than 7, chain `with_dispatcher` calls instead. + +## Subcommands + +Multi-level commands work the same way—each dot-separated level is just part of the name: + +```rust +dispatcher!("remote.add", CMDRemoteAdd => EntryRemoteAdd); +dispatcher!("remote.rm", CMDRemoteRm => EntryRemoteRm); +``` + +Each subcommand's Entry, Chain, and Renderer are completely independent and don't interfere. + +## Type Independence + +Notice we used two different `pack!` macros: + +- `pack!(ResultGreeting = String)` +- `pack!(ResultSum = i32)` + +They are independent types, and `gen_program!()` assigns them different enum variants. + +The dispatcher will never route `ResultGreeting` data to `render_sum` — **type safety is guaranteed from the naming stage**. + +

+ Written by @Weicao-CatilGrass +

-- cgit