Rendering Results

Use the #[renderer] macro to declare a renderer and output results

Now that we've created a Dispatcher and a Chain, and produced a Result type via `pack!`, there's one final step: **presenting the result to the user**. ## The `#[renderer]` Macro Similar to `#[chain]`, `#[renderer]` marks an output function: ```rust pack!(ResultName = String); #[renderer] fn render_name(name: ResultName) { r_println!("Hello, {}!", *name); } ``` A Renderer takes the result produced by a Chain and outputs it using `r_println!`. What's the difference between `r_println!` and the usual `println!`? ## The `r_println!` and `r_print!` Macros `r_println!` and `r_print!` are printing macros provided by Mingling. They write content into a `RenderResult` instead of printing directly to the terminal. This offers several benefits: 1. **RenderResult holds an exit code** — you can make the program exit with a specific code 2. **Easier testing** — you can capture rendered output and make assertions 3. **Post-processing** — you can capture results and apply uniform text post-processing > [!TIP] > For simple printing, you can think of it as a drop-in replacement for `println!`. Using `r_println!` instead of `println!` is a safe choice. ## A Complete Runnable Program Putting the content of all three tutorials together, here's your first complete Mingling program: ```rust // 1. Declare a command with Dispatcher dispatcher!("greet", CMDGreet => EntryGreet); // 2. Declare result data with pack! pack!(ResultName = String); // 3. Handle logic with Chain #[chain] fn handle_greet(args: EntryGreet) -> Next { let name = args.inner .first() .cloned() .unwrap_or_else(|| "World".to_string()); ResultName::new(name) } // 4. Output results with Renderer #[renderer] fn render_name(name: ResultName) { r_println!("Hello, {}!", *name); } // 5. Assemble and run the program in main fn main() { let mut program = ThisProgram::new(); program.with_dispatcher(CMDGreet); program.exec_and_exit(); } // 6. Generate the complete program with gen_program! gen_program!(); ``` ## Try It Out ```bash ~# cargo run -- greet Alice ``` ```text Hello, Alice! ``` Try without arguments: ```bash ~# cargo run -- greet ``` ```text Hello, World! ``` Try an unknown command: ```bash cargo run -- great ``` ```text # No output! ``` ## Add a Fallback `gen_program!()` auto-generates an `ErrorDispatcherNotFound` type that wraps `Vec` — it holds the user's unmatched input. You just need to write a Renderer for it: ```rust #[renderer] fn render_dispatcher_not_found(err: ErrorDispatcherNotFound) { if err.inner.is_empty() { r_println!("Unknown command"); } else { r_println!("Command not found: \"{}\"", err.inner.join(" ")); } } ``` With that added, try the unknown command again: ```bash cargo run -- great ``` ```text Command not found: "great" ``` ## Congratulations You've completed your first full Mingling program! Here's a recap of what you've learned: | Concept | Macro/Function | In a Nutshell | | --------------- | ---------------- | ------------------------------------- | | Declare command | `dispatcher!` | Tell the program what users can input | | Handle logic | `#[chain]` | What to do with the arguments | | Output results | `#[renderer]` | How to present results to users | | Type wrapper | `pack!` | Give your data a meaningful name | | Program entry | `gen_program!()` | Auto-generate the pipeline wiring | In real projects you'll also use advanced features like resource injection, hooks, autocompletion, REPL, etc., but the core skeleton stays the same: **Dispatcher → Chain → Renderer**.

Written by @Weicao-CatilGrass