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