Rendering Results
Declare a renderer using the #[renderer] macro to output results.
Now we've created a Dispatcher and a Chain, and produced a Result type via `pack!`. The final step: **present the result to the user**.
## The `#[renderer]` Macro
Similar to `#[chain]`, `#[renderer]` marks a function that produces output:
```rust
@@@use mingling::macros::buffer;
@@@pack!(ResultName = String);
#[renderer(buffer)]
fn render_name(name: ResultName) {
r_println!("Hello, {}!", *name);
}
```
A Renderer receives the result produced by a Chain and returns a `RenderResult`. Inside the function, you create a `RenderResult`, write content with `r_print!` / `r_println!`, and finally return it.
## The `buffer` Extension
If you find explicitly creating and returning a `RenderResult` too verbose, you can use `#[renderer(buffer)]` to inject a buffer directly into your function.
```rust
use mingling::macros::buffer;
@@@pack!(ResultName = String);
#[renderer(buffer)]
fn render_name(name: ResultName) {
r_println!("Hello, {}!", *name);
}
```
This gives your renderer function a more concise syntax, but also introduces an implicit mechanism: it injects a mutable `RenderResult` variable named `__render_result_buffer` into the function. When the `r_print!` macro is used without an explicit `RenderResult`, it will append output to that buffer by convention.
## The `RenderResult` Type
`RenderResult` is a buffer type that holds rendered text and an exit code. Instead of writing directly to the terminal, it writes content into an internal buffer. This approach gives us:
1. **Exit code support**—you can set the program to exit with a specific exit code
2. **Testability**—rendered output can be captured and asserted against
3. **Post-processing**—the result can be captured and further processed uniformly
## A Complete Runnable Program
Putting all three tutorials together, here's your first complete Mingling program:
```rust
use mingling::macros::buffer;
// 1. Declare commands with a Dispatcher
dispatcher!("greet", CMDGreet => EntryGreet);
// 2. Declare result data with pack!
pack!(ResultName = String);
// 3. Handle logic with a Chain
#[chain]
fn handle_greet(args: EntryGreet) -> Next {
let name = args.inner
.first()
.cloned()
.unwrap_or_else(|| "World".to_string());
ResultName::new(name).into()
}
// 4. Output results with a Renderer
#[renderer(buffer)]
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. Use gen_program! to generate the full 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 a non-existent command:
```bash
cargo run -- great
```
```text
# No output!
```
## Adding a Fallback
`gen_program!()` auto-generates an `ErrorDispatcherNotFound` type wrapping `Vec`—it holds the user input that didn't match any command. You just need to write a Renderer for it:
```rust
use mingling::macros::buffer;
#[renderer(buffer)]
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 non-existent command again:
```bash
cargo run -- great
```
```text
Command not found: "great"
```
## Congratulations
You've completed your first full Mingling program! Let's recap what you've learned:
| Concept | Macro / Function | One-liner |
| -------------- | ---------------- | --------------------------------------- |
| Declare cmds | `dispatcher!` | Tell the program what the user can type |
| Handle logic | `#[chain]` | What to do when args are received |
| Output results | `#[renderer]` | How to present results to the user |
| Type wrapping | `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, completions, REPL, etc., but the core skeleton stays the same: **Dispatcher → Chain → Renderer**.
Written by @Weicao-CatilGrass