1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
<h1 align="center">Rendering Results</h1>
<p align="center">
Use the <code>#[renderer]</code> macro to declare a renderer and output results
</p>
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<String>` — 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**.
<p align="center" style="font-size: 0.85em; color: gray;">
Written by @Weicao-CatilGrass
</p>
|