aboutsummaryrefslogtreecommitdiff
path: root/docs/pages/4-render-result.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/pages/4-render-result.md')
-rw-r--r--docs/pages/4-render-result.md151
1 files changed, 151 insertions, 0 deletions
diff --git a/docs/pages/4-render-result.md b/docs/pages/4-render-result.md
new file mode 100644
index 0000000..b1365b8
--- /dev/null
+++ b/docs/pages/4-render-result.md
@@ -0,0 +1,151 @@
+<h1 align="center">Rendering Results</h1>
+<p align="center">
+ Declare a renderer using the <code>#[renderer]</code> macro to output results.
+</p>
+
+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 an output function:
+
+```rust
+use std::io::Write;
+
+pack!(ResultName = String);
+#[renderer]
+fn render_name(name: ResultName) -> RenderResult {
+ let mut result = RenderResult::new();
+ writeln!(result, "Hello, {}!", *name).ok();
+ result
+}
+```
+
+A Renderer takes the result produced by a Chain and returns a `RenderResult`. Inside the function, create a `RenderResult`, write content using `write!` / `writeln!` (from [`std::io::Write`](https://doc.rust-lang.org/std/io/trait.Write.html)), and return it.
+
+## 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 std::io::Write;
+
+// 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)
+}
+
+// 4. Output results with a Renderer
+#[renderer]
+fn render_name(name: ResultName) -> RenderResult {
+ let mut result = RenderResult::new();
+ writeln!(result, "Hello, {}!", *name).ok();
+ result
+}
+
+// 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<String>`—it holds the user input that didn't match any command. You just need to write a Renderer for it:
+
+```rust
+use std::io::Write;
+
+#[renderer]
+fn render_dispatcher_not_found(err: ErrorDispatcherNotFound) -> RenderResult {
+ let mut result = RenderResult::new();
+ if err.inner.is_empty() {
+ writeln!(result, "Unknown command").ok();
+ } else {
+ writeln!(result, "Command not found: \"{}\"", err.inner.join(" ")).ok();
+ }
+ result
+}
+```
+
+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**.
+
+<p align="center" style="font-size: 0.85em; color: gray;">
+ Written by @Weicao-CatilGrass
+</p>