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 @@@use mingling::macros::buffer; // 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).into() } #[chain] fn handle_add(args: EntryAdd) -> Next { let sum: i32 = args.inner.iter().filter_map(|s| s.parse::().ok()).sum(); ResultSum::new(sum).into() } #[renderer(buffer)] fn render_greet(result: ResultGreeting) { r_println!("Hello, {}!", *result); } #[renderer(buffer)] fn render_sum(result: ResultSum) { r_println!("Sum: {}", *result); } fn main() { let mut program = ThisProgram::new(); program.with_dispatcher(CMDGreet); program.with_dispatcher(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 ``` ## 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