aboutsummaryrefslogtreecommitdiff
path: root/docs/pages
diff options
context:
space:
mode:
Diffstat (limited to 'docs/pages')
-rw-r--r--docs/pages/10-help.md18
-rw-r--r--docs/pages/11-resource-system.md12
-rw-r--r--docs/pages/13-hook.md2
-rw-r--r--docs/pages/14-testing.md9
-rw-r--r--docs/pages/4-render-result.md81
-rw-r--r--docs/pages/5-multiple-commands.md16
-rw-r--r--docs/pages/6-argument-parse-picker.md38
-rw-r--r--docs/pages/7-argument-parse-clap.md23
-rw-r--r--docs/pages/9-error-handling.md24
-rw-r--r--docs/pages/advanced/2-structural-renderer.md12
-rw-r--r--docs/pages/other/naming_rule.md12
11 files changed, 151 insertions, 96 deletions
diff --git a/docs/pages/10-help.md b/docs/pages/10-help.md
index 2f3b74f..7ef1f26 100644
--- a/docs/pages/10-help.md
+++ b/docs/pages/10-help.md
@@ -15,9 +15,11 @@ Write a help function directly for an Entry:
@@@use mingling::macros::help;
@@@dispatcher!("greet", CMDGreet => EntryGreet);
#[help]
-fn help_greet(_entry: EntryGreet) {
- r_println!("Usage: greet [name]");
- r_println!("Say hello to someone.");
+fn help_greet(entry: EntryGreet) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Usage: greet [name]").ok();
+ writeln!(r, "Say hello to someone.").ok();
+ r
}
```
@@ -32,10 +34,12 @@ You can also write help for `ErrorDispatcherNotFound` as the "root help":
@@@use mingling::macros::help;
// Triggered when user passes --help directly
#[help]
-fn help_root(entry: ErrorDispatcherNotFound) {
- r_println!("Usage: my-cli <command>");
- r_println!("Commands:");
- r_println!(" greet Say hello");
+fn help_root(entry: ErrorDispatcherNotFound) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Usage: my-cli <command>").ok();
+ writeln!(r, "Commands:").ok();
+ writeln!(r, " greet Say hello").ok();
+ r
}
```
diff --git a/docs/pages/11-resource-system.md b/docs/pages/11-resource-system.md
index b54938a..bc2197f 100644
--- a/docs/pages/11-resource-system.md
+++ b/docs/pages/11-resource-system.md
@@ -38,8 +38,10 @@ fn handle_pwd(_args: EntryPrintWorkingDir, cwd: &ResCurrentDir) -> Next {
}
#[renderer]
-fn render_path(result: ResultPath) {
- r_println!("{}", *result);
+fn render_path(result: ResultPath) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "{}", *result).ok();
+ r
}
```
@@ -59,8 +61,10 @@ fn handle_visit(_args: EntryVisit, counter: &mut ResVisitCount) -> Next {
}
#[renderer]
-fn render_done(_done: ResultDone, counter: &ResVisitCount) {
- r_println!("visit count is : {}", counter.0);
+fn render_done(_done: ResultDone, counter: &ResVisitCount) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "visit count is : {}", counter.0).ok();
+ r
}
```
diff --git a/docs/pages/13-hook.md b/docs/pages/13-hook.md
index 0c5e460..26f8712 100644
--- a/docs/pages/13-hook.md
+++ b/docs/pages/13-hook.md
@@ -60,7 +60,7 @@ Each hook callback receives a corresponding `Hook*Info` struct containing contex
@@@#[chain] fn handle_greet(args: EntryGreet) -> Next {
@@@ ResultName::new(args.inner.first().cloned().unwrap_or_default()).to_render()
@@@}
-@@@#[renderer] fn render_name(r: ResultName) { r_println!("Hello, {}!", *r); }
+@@@#[renderer] fn render_name(r: ResultName) -> RenderResult { RenderResult::new() }
fn main() {
let mut program = ThisProgram::new();
diff --git a/docs/pages/14-testing.md b/docs/pages/14-testing.md
index 789da96..fa196b0 100644
--- a/docs/pages/14-testing.md
+++ b/docs/pages/14-testing.md
@@ -13,16 +13,17 @@ Renderer is the easiest to test — call the function, assert the result:
```rust
@@@pack!(ResultName = String);
-// Returns String instead of ()
#[renderer]
-fn render_name(name: ResultName) -> String {
- r_println!("Hello, {}!", *name);
+fn render_greet(result: ResultName) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Hello, {}!", *result).ok();
+ r
}
#[test]
fn test_render_name() {
let result = render_name(ResultName::new("Alice".to_string()));
- assert_eq!(result, "Hello, Alice!\n");
+ assert_eq!(result.to_string().as_str(), "Hello, Alice!\n");
}
```
diff --git a/docs/pages/4-render-result.md b/docs/pages/4-render-result.md
index 5093a52..b1365b8 100644
--- a/docs/pages/4-render-result.md
+++ b/docs/pages/4-render-result.md
@@ -1,47 +1,50 @@
<h1 align="center">Rendering Results</h1>
<p align="center">
- Use the <code>#[renderer]</code> macro to declare a renderer and output results
+ Declare a renderer using the <code>#[renderer]</code> macro to 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**.
+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) {
- r_println!("Hello, {}!", *name);
+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 outputs it using `r_println!`. What's the difference between `r_println!` and the usual `println!`?
-
-## The `r_println!` and `r_print!` Macros
+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.
-`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:
+## The `RenderResult` Type
-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
+`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:
-> [!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.
+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 the content of all three tutorials together, here's your first complete Mingling program:
+Putting all three tutorials together, here's your first complete Mingling program:
```rust
-// 1. Declare a command with Dispatcher
+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 Chain
+// 3. Handle logic with a Chain
#[chain]
fn handle_greet(args: EntryGreet) -> Next {
let name = args.inner
@@ -51,10 +54,12 @@ fn handle_greet(args: EntryGreet) -> Next {
ResultName::new(name)
}
-// 4. Output results with Renderer
+// 4. Output results with a Renderer
#[renderer]
-fn render_name(name: ResultName) {
- r_println!("Hello, {}!", *name);
+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
@@ -64,7 +69,7 @@ fn main() {
program.exec_and_exit();
}
-// 6. Generate the complete program with gen_program!
+// 6. Use gen_program! to generate the full program
gen_program!();
```
@@ -88,7 +93,7 @@ Try without arguments:
Hello, World!
```
-Try an unknown command:
+Try a non-existent command:
```bash
cargo run -- great
@@ -98,22 +103,26 @@ cargo run -- great
# No output!
```
-## Add a Fallback
+## Adding 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:
+`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) {
+fn render_dispatcher_not_found(err: ErrorDispatcherNotFound) -> RenderResult {
+ let mut result = RenderResult::new();
if err.inner.is_empty() {
- r_println!("Unknown command");
+ writeln!(result, "Unknown command").ok();
} else {
- r_println!("Command not found: \"{}\"", err.inner.join(" "));
+ writeln!(result, "Command not found: \"{}\"", err.inner.join(" ")).ok();
}
+ result
}
```
-With that added, try the unknown command again:
+With that added, try the non-existent command again:
```bash
cargo run -- great
@@ -125,17 +134,17 @@ Command not found: "great"
## Congratulations
-You've completed your first full Mingling program! Here's a recap of what you've learned:
+You've completed your first full Mingling program! Let's recap 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 |
+| 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, autocompletion, REPL, etc., but the core skeleton stays the same: **Dispatcher → Chain → Renderer**.
+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
diff --git a/docs/pages/5-multiple-commands.md b/docs/pages/5-multiple-commands.md
index 9ad7ef4..a56f7b0 100644
--- a/docs/pages/5-multiple-commands.md
+++ b/docs/pages/5-multiple-commands.md
@@ -30,13 +30,17 @@ fn handle_add(args: EntryAdd) -> Next {
}
#[renderer]
-fn render_greet(result: ResultGreeting) {
- r_println!("Hello, {}!", *result);
+fn render_greet(result: ResultGreeting) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Hello, {}!", *result).ok();
+ r
}
#[renderer]
-fn render_sum(result: ResultSum) {
- r_println!("Sum: {}", *result);
+fn render_sum(result: ResultSum) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Sum: {}", *result).ok();
+ r
}
fn main() {
@@ -67,9 +71,9 @@ Notice `with_dispatchers`? When you need to register multiple dispatchers, just
@@@pack!(ResultGreeting = String);
@@@pack!(ResultSum = i32);
@@@#[chain] fn handle_greet(_args: EntryGreet) -> Next { ResultGreeting::new("ok".into()) }
-@@@#[renderer] fn render_greet(_greeting: ResultGreeting) { r_println!("hi"); }
+@@@#[renderer] fn render_greet(_greeting: ResultGreeting) -> RenderResult { RenderResult::new() }
@@@#[chain] fn handle_add(_args: EntryAdd) -> Next { ResultSum::new(0) }
-@@@#[renderer] fn render_sum(_sum: ResultSum) { r_println!("sum"); }
+@@@#[renderer] fn render_sum(_sum: ResultSum) -> RenderResult { RenderResult::new() }
fn main() {
let mut program = ThisProgram::new();
program.with_dispatchers((CMDGreet, CMDAdd));
diff --git a/docs/pages/6-argument-parse-picker.md b/docs/pages/6-argument-parse-picker.md
index 7e91af8..c80c25c 100644
--- a/docs/pages/6-argument-parse-picker.md
+++ b/docs/pages/6-argument-parse-picker.md
@@ -156,13 +156,17 @@ fn handle_greet_entry(prev: EntryGreet) -> Next {
}
#[renderer]
-fn render_no_name(_prev: ErrorNoName) {
- r_println!("Error: No name provided.");
+fn render_no_name(_prev: ErrorNoName) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Error: No name provided.").ok();
+ r
}
#[renderer]
-fn render_name(prev: ResultName) {
- r_println!("Hello, {}!", *prev);
+fn render_name(prev: ResultName) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Hello, {}!", *prev).ok();
+ r
}
```
@@ -251,14 +255,18 @@ fn handle_greet_entry(prev: EntryGreet) -> Next {
}
#[renderer]
-fn render_name_too_long(prev: ErrorNameTooLong) {
+fn render_name_too_long(prev: ErrorNameTooLong) -> RenderResult {
+ let mut r = RenderResult::new();
let len = *prev;
- r_println!("Error: name too long (length: {} > 32)", len);
+ writeln!(r, "Error: name too long (length: {} > 32)", len).ok();
+ r
}
#[renderer]
-fn render_name(prev: ResultName) {
- r_println!("Hello, {}!", *prev);
+fn render_name(prev: ResultName) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Hello, {}!", *prev).ok();
+ r
}
```
@@ -315,7 +323,7 @@ Implement the `Pickable` trait to make your type parseable by `Picker` — this
// Features: ["parser"]
@@@use mingling::parser::{Pickable, Argument};
@@@use mingling::Flag;
-#[derive(Default)]
+#[derive(Default, Clone)]
pub struct Address {
ip: String,
port: u16,
@@ -341,9 +349,11 @@ fn handle_connect_entry(prev: EntryConnect) -> Next {
}
#[renderer]
-fn render_connected(prev: ResultConnected) {
+fn render_connected(prev: ResultConnected) -> RenderResult {
+ let mut r = RenderResult::new();
let addr = prev.inner;
- r_println!("Connected: IP: {} PORT: {}", addr.ip, addr.port);
+ writeln!(r, "Connected: IP: {} PORT: {}", addr.ip, addr.port).ok();
+ r
}
```
@@ -381,8 +391,10 @@ fn handle_eat_entry(prev: EntryEat) -> Next {
}
#[renderer]
-fn render_fruit(prev: ResultFruit) {
- r_println!("Picked fruit: {:?}", *prev);
+fn render_fruit(prev: ResultFruit) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Picked fruit: {:?}", *prev).ok();
+ r
}
```
diff --git a/docs/pages/7-argument-parse-clap.md b/docs/pages/7-argument-parse-clap.md
index e912c38..b11e00e 100644
--- a/docs/pages/7-argument-parse-clap.md
+++ b/docs/pages/7-argument-parse-clap.md
@@ -35,18 +35,22 @@ pub struct EntryGreet {
}
#[renderer]
-fn render_greet(greet: EntryGreet) {
+fn render_greet(greet: EntryGreet) -> RenderResult {
+ let mut r = RenderResult::new();
let count = greet.repeat.max(0) as usize;
- r_print!("Hello, ");
+ write!(r, "Hello, ").ok();
for _ in 0..count {
- r_print!("{} ", greet.name);
+ write!(r, "{} ", greet.name).ok();
}
- r_println!("!");
+ writeln!(r, "!").ok();
+ r
}
#[renderer]
-fn render_greet_parse_failed(err: ErrorGreetParsed) {
- r_println!("{}", *err);
+fn render_greet_parse_failed(err: ErrorGreetParsed) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "{}", *err).ok();
+ r
}
```
@@ -66,10 +70,11 @@ If you need `--help` support, register `BasicProgramSetup` in main and set the c
@@@ name: String,
@@@}
@@@#[renderer]
-@@@fn render_greet(greet: EntryGreet) {
-@@@ r_println!("Hello, {}!", greet.name);
+@@@fn render_greet(greet: EntryGreet) -> RenderResult {
+@@@ let mut r = RenderResult::new();
+@@@ write!(r, "Hello, {}!", greet.name).ok();
+@@@ r
@@@}
-@@@
fn main() {
let mut program = ThisProgram::new();
program.with_setup(BasicProgramSetup);
diff --git a/docs/pages/9-error-handling.md b/docs/pages/9-error-handling.md
index ec1f422..3e004f2 100644
--- a/docs/pages/9-error-handling.md
+++ b/docs/pages/9-error-handling.md
@@ -44,13 +44,17 @@ Then write separate Renderers:
@@@#[chain] fn handle_greet(args: EntryGreet) -> Next { ResultGreeting::new(args.inner.first().cloned().unwrap_or_default()).to_render() }
#[renderer]
-fn render_greeting(result: ResultGreeting) {
- r_println!("Hello, {}!", *result);
+fn render_greeting(result: ResultGreeting) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Hello, {}!", *result).ok();
+ r
}
#[renderer]
-fn render_error_name_empty(err: ErrorNameEmpty) {
- r_println!("Error: {}", *err);
+fn render_error_name_empty(err: ErrorNameEmpty) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Error: {}", *err).ok();
+ r
}
```
@@ -75,13 +79,17 @@ fn handle_greet(args: EntryGreet) -> Next {
}
#[renderer]
-fn render_greeting(result: ResultGreeting) {
- r_println!("Hello, {}!", *result);
+fn render_greeting(result: ResultGreeting) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Hello, {}!", *result).ok();
+ r
}
#[renderer]
-fn render_error_name_empty(err: ErrorNameEmpty) {
- r_println!("Error: {}", *err);
+fn render_error_name_empty(err: ErrorNameEmpty) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Error: {}", *err).ok();
+ r
}
fn main() {
diff --git a/docs/pages/advanced/2-structural-renderer.md b/docs/pages/advanced/2-structural-renderer.md
index 09c86d1..f31f758 100644
--- a/docs/pages/advanced/2-structural-renderer.md
+++ b/docs/pages/advanced/2-structural-renderer.md
@@ -41,8 +41,10 @@ fn handle_render(args: EntryRender) -> Next {
}
#[renderer]
-fn render_info(r: ResultInfo) {
- r_println!("{:?}", *r);
+fn render_info(r: ResultInfo) -> RenderResult {
+ let mut result = RenderResult::new();
+ writeln!(result, "{:?}", *r).ok();
+ result
}
```
@@ -86,8 +88,10 @@ fn handle_render(args: EntryRender) -> Next {
}
#[renderer]
-fn render_info(info: Info) {
- r_println!("{} is {} years old", info.name, info.age);
+fn render_info(info: Info) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "{} is {} years old", info.name, info.age).ok();
+ r
}
@@@
@@@fn main() {
diff --git a/docs/pages/other/naming_rule.md b/docs/pages/other/naming_rule.md
index 21d7947..2ede61f 100644
--- a/docs/pages/other/naming_rule.md
+++ b/docs/pages/other/naming_rule.md
@@ -193,14 +193,18 @@ fn handle_state_operation_remotes(state: StateOperationRemotes, db: &ResDatabase
// Result rendering
#[renderer]
-fn render_remote_added(result: ResultRemoteAdded) {
- r_println!("Remote added: {}", result.inner);
+fn render_remote_added(result: ResultRemoteAdded) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Remote added: {}", result.inner).ok();
+ r
}
// Error rendering
#[renderer]
-fn render_error_repository_not_found(err: ErrorRepositoryNotFound) {
- r_println!("Error: remote '{}' not found", err.inner);
+fn render_error_repository_not_found(err: ErrorRepositoryNotFound) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "Error: remote '{}' not found", err.inner).ok();
+ r
}
```