aboutsummaryrefslogtreecommitdiff
path: root/docs/pages
diff options
context:
space:
mode:
Diffstat (limited to 'docs/pages')
-rw-r--r--docs/pages/10-help.md26
-rw-r--r--docs/pages/11-resource-system.md18
-rw-r--r--docs/pages/14-testing.md2
-rw-r--r--docs/pages/3-define-a-chain.md2
-rw-r--r--docs/pages/4-render-result.md53
-rw-r--r--docs/pages/5-multiple-commands.md17
-rw-r--r--docs/pages/6-argument-parse-picker.md116
-rw-r--r--docs/pages/7-argument-parse-clap.md34
-rw-r--r--docs/pages/9-error-handling.md34
-rw-r--r--docs/pages/advanced/2-structural-renderer.md22
-rw-r--r--docs/pages/concepts/3-any-output.md10
-rw-r--r--docs/pages/other/features.md18
-rw-r--r--docs/pages/other/naming_rule.md21
13 files changed, 179 insertions, 194 deletions
diff --git a/docs/pages/10-help.md b/docs/pages/10-help.md
index c17f410..1e3ea78 100644
--- a/docs/pages/10-help.md
+++ b/docs/pages/10-help.md
@@ -13,18 +13,17 @@ Write a help function directly for an Entry:
```rust
@@@use mingling::macros::help;
+@@@use mingling::macros::buffer;
@@@dispatcher!("greet", CMDGreet => EntryGreet);
-#[help]
-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
+#[help(buffer)]
+fn help_greet(_entry: EntryGreet) {
+ r_println!("Usage: greet [name]");
+ r_println!("Say hello to someone.");
}
```
> [!NOTE]
-> Help functions also use `writeln!` into a `RenderResult`, because `#[help]` follows the rendering pipeline — it's a short-circuit render triggered early by the `--help` flag, not logic outside the pipeline.
+> Help functions also use `r_println!` into a `RenderResult`, because `#[help]` follows the rendering pipeline — it's a short-circuit render triggered early by the `--help` flag, not logic outside the pipeline.
## Global Help
@@ -32,14 +31,13 @@ You can also write help for `ErrorDispatcherNotFound` as the "root help":
```rust
@@@use mingling::macros::help;
+@@@use mingling::macros::buffer;
// Triggered when user passes --help directly
-#[help]
-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
+#[help(buffer)]
+fn help_root(entry: ErrorDispatcherNotFound) {
+ r_println!("Usage: my-cli <command>");
+ r_println!("Commands:");
+ r_println!(" greet Say hello");
}
```
diff --git a/docs/pages/11-resource-system.md b/docs/pages/11-resource-system.md
index 9491212..0e2bd19 100644
--- a/docs/pages/11-resource-system.md
+++ b/docs/pages/11-resource-system.md
@@ -27,6 +27,7 @@ Since `ResCurrentDir` implements both `Default` and `Clone`, the framework autom
In a Chain or Renderer, simply declare the resource in the parameter list:
```rust
+@@@use mingling::macros::buffer;
@@@#[derive(Default, Clone)]
@@@struct ResCurrentDir(String);
@@@dispatcher!("pwd", CMDPrintWorkingDir => EntryPrintWorkingDir);
@@ -37,11 +38,9 @@ fn handle_pwd(_args: EntryPrintWorkingDir, cwd: &ResCurrentDir) -> Next {
ResultPath::new(cwd.0.clone()).to_render()
}
-#[renderer]
-fn render_path(result: ResultPath) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "{}", *result).ok();
- r
+#[renderer(buffer)]
+fn render_path(result: ResultPath) {
+ r_println!("{}", *result);
}
```
@@ -50,6 +49,7 @@ fn render_path(result: ResultPath) -> RenderResult {
Use `&mut T` to inject a mutable resource:
```rust
+@@@use mingling::macros::buffer;
@@@#[derive(Default, Clone)]
@@@struct ResVisitCount(u32);
@@@dispatcher!("visit", CMDVisit => EntryVisit);
@@ -60,11 +60,9 @@ fn handle_visit(_args: EntryVisit, counter: &mut ResVisitCount) -> Next {
ResultDone::default().into()
}
-#[renderer]
-fn render_done(_done: ResultDone, counter: &ResVisitCount) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "visit count is : {}", counter.0).ok();
- r
+#[renderer(buffer)]
+fn render_done(_done: ResultDone, counter: &ResVisitCount) {
+ r_println!("visit count is : {}", counter.0);
}
```
diff --git a/docs/pages/14-testing.md b/docs/pages/14-testing.md
index fa196b0..65fedc9 100644
--- a/docs/pages/14-testing.md
+++ b/docs/pages/14-testing.md
@@ -16,7 +16,7 @@ Renderer is the easiest to test — call the function, assert the result:
#[renderer]
fn render_greet(result: ResultName) -> RenderResult {
let mut r = RenderResult::new();
- writeln!(r, "Hello, {}!", *result).ok();
+ r_println!(r, "Hello, {}!", *result);
r
}
diff --git a/docs/pages/3-define-a-chain.md b/docs/pages/3-define-a-chain.md
index b2822bc..450522b 100644
--- a/docs/pages/3-define-a-chain.md
+++ b/docs/pages/3-define-a-chain.md
@@ -48,7 +48,7 @@ You've probably guessed it — `pack!(ResultName = String)` defines a type that
```rust
// pack!(ResultName = String) generates code roughly like this
-#[derive(Groupped)]
+#[derive(Grouped)]
pub struct ResultName {
pub inner: String,
}
diff --git a/docs/pages/4-render-result.md b/docs/pages/4-render-result.md
index ca1e563..9e72d09 100644
--- a/docs/pages/4-render-result.md
+++ b/docs/pages/4-render-result.md
@@ -7,21 +7,34 @@ Now we've created a Dispatcher and a Chain, and produced a Result type via `pack
## The `#[renderer]` Macro
-Similar to `#[chain]`, `#[renderer]` marks an output function:
+Similar to `#[chain]`, `#[renderer]` marks a function that produces output:
```rust
-use std::io::Write;
+@@@use mingling::macros::buffer;
+@@@pack!(ResultName = String);
+#[renderer(buffer)]
+fn render_name(name: ResultName) {
+ r_println!("Hello, {}!", *name);
+}
+```
-pack!(ResultName = String);
-#[renderer]
-fn render_name(name: ResultName) -> RenderResult {
- let mut result = RenderResult::new();
- writeln!(result, "Hello, {}!", *name).ok();
- result
+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);
}
```
-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.
+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
@@ -36,7 +49,7 @@ A Renderer takes the result produced by a Chain and returns a `RenderResult`. In
Putting all three tutorials together, here's your first complete Mingling program:
```rust
-use std::io::Write;
+use mingling::macros::buffer;
// 1. Declare commands with a Dispatcher
dispatcher!("greet", CMDGreet => EntryGreet);
@@ -55,11 +68,9 @@ fn handle_greet(args: EntryGreet) -> Next {
}
// 4. Output results with a Renderer
-#[renderer]
-fn render_name(name: ResultName) -> RenderResult {
- let mut result = RenderResult::new();
- writeln!(result, "Hello, {}!", *name).ok();
- result
+#[renderer(buffer)]
+fn render_name(name: ResultName) {
+ r_println!("Hello, {}!", *name);
}
// 5. Assemble and run the program in main
@@ -108,17 +119,15 @@ cargo run -- great
`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;
+use mingling::macros::buffer;
-#[renderer]
-fn render_dispatcher_not_found(err: ErrorDispatcherNotFound) -> RenderResult {
- let mut result = RenderResult::new();
+#[renderer(buffer)]
+fn render_dispatcher_not_found(err: ErrorDispatcherNotFound) {
if err.inner.is_empty() {
- writeln!(result, "Unknown command").ok();
+ r_println!("Unknown command");
} else {
- writeln!(result, "Command not found: \"{}\"", err.inner.join(" ")).ok();
+ r_println!("Command not found: \"{}\"", err.inner.join(" "));
}
- result
}
```
diff --git a/docs/pages/5-multiple-commands.md b/docs/pages/5-multiple-commands.md
index d9a335a..0b71e49 100644
--- a/docs/pages/5-multiple-commands.md
+++ b/docs/pages/5-multiple-commands.md
@@ -10,6 +10,7 @@ Real-world CLIs rarely have just one command. Let's extend our previous greet pr
Work in the same project:
```rust
+@@@use mingling::macros::buffer;
// Declare two commands
dispatcher!("greet", CMDGreet => EntryGreet);
dispatcher!("add", CMDAdd => EntryAdd);
@@ -29,18 +30,14 @@ fn handle_add(args: EntryAdd) -> Next {
ResultSum::new(sum).into()
}
-#[renderer]
-fn render_greet(result: ResultGreeting) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "Hello, {}!", *result).ok();
- r
+#[renderer(buffer)]
+fn render_greet(result: ResultGreeting) {
+ r_println!("Hello, {}!", *result);
}
-#[renderer]
-fn render_sum(result: ResultSum) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "Sum: {}", *result).ok();
- r
+#[renderer(buffer)]
+fn render_sum(result: ResultSum) {
+ r_println!("Sum: {}", *result);
}
fn main() {
diff --git a/docs/pages/6-argument-parse-picker.md b/docs/pages/6-argument-parse-picker.md
index 398cd3c..01f1c37 100644
--- a/docs/pages/6-argument-parse-picker.md
+++ b/docs/pages/6-argument-parse-picker.md
@@ -3,7 +3,7 @@
Use Picker to perform basic argument parsing
</p>
-In previous tutorials, we extracted args manually from `EntryGreet.inner` (`Vec<String>`).
+In previous tutorials, we manually extracted parameters from `EntryGreet.inner` (`Vec<String>`).
```rust
@@@ fn main() {
@@ -12,7 +12,7 @@ let name = args.first().cloned().unwrap_or_else(|| "World".to_string());
@@@ }
```
-But this approach doesn't scale well for many params. Mingling provides `Picker` — a chaining API to extract and convert args.
+But this approach doesn't scale well when there are many params. Mingling provides `Picker` — a chained API for extracting and transforming params.
To enable `Picker`, update your `Cargo.toml`:
@@ -22,7 +22,7 @@ To enable `Picker`, update your `Cargo.toml`:
features = ["parser"]
```
-Now let's look at `Picker` in action:
+Now let's see how `Picker` is written:
```rust
// Features: ["parser"]
@@ -36,9 +36,9 @@ fn handle_greet_entry(prev: EntryGreet) -> Next {
}
```
-`AsPicker` implements `pick`, `pick_or`, and `pick_or_route` for any type that can convert to `Vec<String>`: they semantically **pick** args from a string list and convert them to structured data.
+`AsPicker` implements `pick`, `pick_or`, and `pick_or_route` for all types convertible to `Vec<String>`. These functions semantically **pick** params from the string list and convert them into structured data.
-Breaking down the example above:
+For the code above:
```rust
// Features: ["parser"]
@@ -62,17 +62,17 @@ Its semantics are:
@@@let name: String =
prev.pick_or((), "World").unpack();
// ~~~~ ~~~~~~~ ~~ ~~~~~~~ ~~~~~~~~
-// | | | | |_ unpack to String
+// | | | | |_ unpack as String
// | | | |__________ default value "World"
// | | |______________ pick the first positional arg (no flag)
// | |______________________ pick or use default
-// |___________________________ from previous input
+// |___________________________ from the previous input
@@@}
```
-## Parsing Flag Args
+## Parsing Flag Arguments
-If your program needs to parse flag args (e.g., `greet --name Alice`), do this:
+If your program needs to parse flag arguments (e.g. `greet --name Alice`), do this:
```rust
// Features: ["parser"]
@@ -97,19 +97,19 @@ Its semantics:
@@@let name: String =
prev.pick_or(["--name", "-n"], "World").unpack();
// ~~~~ ~~~~~~~ ~~~~~~~~~~~~~~~~ ~~~~~~~ ~~~~~~~~
-// | | | | |_ unpack to String
+// | | | | |_ unpack as String
// | | | |__________ default value "World"
-// | | |____________________________ pick arg after "--name" or "-n"
+// | | |____________________________ pick the value after "--name" or "-n"
// | |____________________________________ pick or use default
-// |_________________________________________ from previous input
+// |_________________________________________ from the previous input
@@@}
```
## About `.unpack()`
-You may have noticed that `Picker` calls `.unpack()` at the end of parsing. It converts the accumulated parse results into structured info.
+You may have noticed that `Picker` calls `.unpack()` at the end of parsing. It converts the collected results into structured info.
-For a single pick, `.unpack()` returns a single value; for multiple picks, it returns a tuple:
+For a single pick, `.unpack()` returns the value directly; for multiple picks, it returns a tuple:
```rust
// Features: ["parser"]
@@ -129,16 +129,17 @@ fn handle_test_entry(prev: EntryTest) -> Next {
```
> [!IMPORTANT]
-> `Picker` is very sensitive to parse order, esp. for positional args (they're parsed sequentially). If you need to parse positional args, make sure all **flag args** have been picked and consumed first.
+> `Picker` is sensitive to parse order, especially for positional args — it parses sequentially. If you need to parse positional args, make sure all **flag arguments** are picked and consumed first.
## Handling Edge Cases with `pick_or_route`
-As the old saying goes: "Never trust your users." To handle missing required args, type mismatches, etc., `pick_or_route` routes the execution chain to a dedicated error-handling type.
+As the saying goes: "never trust your users." To handle missing required params, type mismatches, etc., `pick_or_route` routes the chain to a dedicated error handler.
-A simple example:
+Here's a simple example:
```rust
// Features: ["parser", "extra_macros"]
+@@@use mingling::macros::buffer;
@@@use mingling::macros::route;
@@@dispatcher!("greet", CMDGreet => EntryGreet);
@@@pack!(ResultName = String);
@@ -150,29 +151,20 @@ fn handle_greet_entry(prev: EntryGreet) -> Next {
.pick_or_route(["--name", "-n"], ErrorNoName::default())
.unpack();
- // Use route! macro to unpack pick_result
+ // Use route! macro to expand pick_result
let name = route!(pick_result);
ResultName::new(name).into()
}
-#[renderer]
-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) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "Hello, {}!", *prev).ok();
- r
+#[renderer(buffer)]
+fn render_greet(result: ResultName) {
+ r_println!("Hello, {}!", *result);
}
```
-With `pick_or_route`, the code gets a bit more complex: `.unpack()` no longer returns the value directly, but `Result<Value, Route>`.
+With `pick_or_route`, the code becomes more involved: `.unpack()` no longer returns the value directly, but `Result<Value, Route>`.
-However, Mingling's `extra_macros` feature provides the `route!` macro to simplify unwrapping — it just omits some boilerplate:
+However, **Mingling**'s `extra_macros` feature provides the `route!` macro for simplified expansion. It's not complex — it just reduces boilerplate:
```rust
// Features: ["parser", "extra_macros"]
@@ -204,7 +196,7 @@ let name = match pick_result {
## Post-processing Extracted Values
-After picking user input, you can use `after` to process the value immediately:
+After picking user input with `pick`, you can use `after` to process it immediately:
```rust
// Features: ["parser"]
@@ -215,7 +207,7 @@ After picking user input, you can use `after` to process the value immediately:
fn handle_greet_entry(prev: EntryGreet) -> Next {
let name = prev
.pick_or(["--name", "-n"], "World")
- // Format immediately after extracting --name
+ // Format immediately after picking --name
.after(|name: String| {
name.replace(['-', '_', '.'], " ")
.to_lowercase()
@@ -228,10 +220,11 @@ fn handle_greet_entry(prev: EntryGreet) -> Next {
}
```
-Similarly, use `after_or_route` to handle format errors in input args:
+Similarly, you can use `after_or_route` to handle input format errors:
```rust
// Features: ["parser", "extra_macros"]
+@@@use mingling::macros::buffer;
@@@use mingling::macros::route;
@@@dispatcher!("greet", CMDGreet => EntryGreet);
@@@pack!(ResultName = String);
@@ -254,35 +247,31 @@ fn handle_greet_entry(prev: EntryGreet) -> Next {
ResultName::new(name).into()
}
-#[renderer]
-fn render_name_too_long(prev: ErrorNameTooLong) -> RenderResult {
- let mut r = RenderResult::new();
+#[renderer(buffer)]
+fn render_name_too_long(prev: ErrorNameTooLong) {
let len = *prev;
- writeln!(r, "Error: name too long (length: {} > 32)", len).ok();
- r
+ r_println!("Error: name too long (length: {} > 32)", len);
}
-#[renderer]
-fn render_name(prev: ResultName) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "Hello, {}!", *prev).ok();
- r
+#[renderer(buffer)]
+fn render_name(prev: ResultName) {
+ r_println!("Hello, {}!", *prev);
}
```
## Boolean Parsing
-`Picker` can parse bools too, with two modes: implicit and explicit.
+`Picker` can also parse booleans, in two modes:
| Mode | Format |
| -------- | ----------------------------------- |
| Implicit | `--confirmed` |
| Explicit | `--confirm true` or `--confirm yes` |
-- Using `.pick::<bool>(flag)` → implicit: flag present means `true`
-- Using `.pick::<Yes>(flag)` or `.pick::<True>(flag)` → explicit
+- `.pick::<bool>(flag)` uses implicit mode: the flag being present means `true`
+- `.pick::<Yes>(flag)` or `.pick::<True>(flag)` uses explicit mode
-Implicit is fine for most cases, but for important confirmations, explicit logic is more semantic.
+Implicit mode is generally sufficient, but for important confirmations, explicit logic is more idiomatic.
```rust
// Features: ["parser"]
@@ -302,7 +291,7 @@ fn handle_entry(prev: EntryTest) -> Next {
## Special Usage: `usize` Parsing
-Mingling provides a special `usize` feature: parsing strings like `25G`, `32mib`, etc.
+**Mingling** provides a special `usize` feature: parsing strings like `25G`, `32mib`, etc.
```rust
// Features: ["parser"]
@@ -315,12 +304,13 @@ fn parse_size() {
}
```
-## Custom Parseable Types
+## Custom Pickable Types
-Implement the `Pickable` trait to make your type parseable by `Picker` — this is where Picker's extensibility comes from.
+You can make your types pickable by `Picker` using the `Pickable` trait — this is where `Picker`'s extensibility comes from.
```rust
// Features: ["parser"]
+@@@use mingling::macros::buffer;
@@@use mingling::parser::{Pickable, Argument};
@@@use mingling::Flag;
#[derive(Default, Clone)]
@@ -348,12 +338,9 @@ fn handle_connect_entry(prev: EntryConnect) -> Next {
ResultConnected::new(address).into()
}
-#[renderer]
-fn render_connected(prev: ResultConnected) -> RenderResult {
- let mut r = RenderResult::new();
- let addr = prev.inner;
- writeln!(r, "Connected: IP: {} PORT: {}", addr.ip, addr.port).ok();
- r
+#[renderer(buffer)]
+fn render_connected(addr: ResultConnected) {
+ r_println!("Connected: IP: {} PORT: {}", addr.ip, addr.port);
}
```
@@ -366,10 +353,11 @@ Connected: IP: 127.0.0.1 PORT: 8080
## Auto-implementing Pickable for Enums
-To implement `Pickable` for an enum, just make it implement `EnumTag`, then implement `PickableEnum`:
+To make an enum `Pickable`, just implement `EnumTag` on it, then implement `PickableEnum`:
```rust
// Features: ["parser"]
+@@@use mingling::macros::buffer;
@@@use mingling::parser::PickableEnum;
@@@use mingling::EnumTag;
#[derive(Debug, Default, EnumTag)]
@@ -390,15 +378,13 @@ fn handle_eat_entry(prev: EntryEat) -> Next {
ResultFruit::new(fruit).into()
}
-#[renderer]
-fn render_fruit(prev: ResultFruit) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "Picked fruit: {:?}", *prev).ok();
- r
+#[renderer(buffer)]
+fn render_fruit(prev: ResultFruit) {
+ r_println!("Picked fruit: {:?}", *prev);
}
```
-That covers all the features of `Picker`.
+That covers all the usages of `Picker`.
<p align="center" style="font-size: 0.85em; color: gray;">
Written by @Weicao-CatilGrass
diff --git a/docs/pages/7-argument-parse-clap.md b/docs/pages/7-argument-parse-clap.md
index b11e00e..dcebd65 100644
--- a/docs/pages/7-argument-parse-clap.md
+++ b/docs/pages/7-argument-parse-clap.md
@@ -25,7 +25,8 @@ Add `#[dispatcher_clap]` on a `clap::Parser` struct to auto-generate a Dispatche
// Dependencies:
// clap = "4"
@@@ use mingling::macros::dispatcher_clap;
-#[derive(Default, clap::Parser, Groupped)]
+@@@ use mingling::macros::buffer;
+#[derive(Default, clap::Parser, Grouped)]
#[dispatcher_clap("greet", CMDGreet, help = true, error = ErrorGreetParsed)]
pub struct EntryGreet {
#[clap(default_value = "World")]
@@ -34,23 +35,19 @@ pub struct EntryGreet {
repeat: i32,
}
-#[renderer]
-fn render_greet(greet: EntryGreet) -> RenderResult {
- let mut r = RenderResult::new();
+#[renderer(buffer)]
+fn render_greet(greet: EntryGreet) {
let count = greet.repeat.max(0) as usize;
- write!(r, "Hello, ").ok();
+ r_print!("Hello, ");
for _ in 0..count {
- write!(r, "{} ", greet.name).ok();
+ r_print!("{} ", greet.name);
}
- writeln!(r, "!").ok();
- r
+ r_println!("!");
}
-#[renderer]
-fn render_greet_parse_failed(err: ErrorGreetParsed) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "{}", *err).ok();
- r
+#[renderer(buffer)]
+fn render_greet_parse_failed(err: ErrorGreetParsed) {
+ r_println!("{}", *err);
}
```
@@ -62,18 +59,17 @@ If you need `--help` support, register `BasicProgramSetup` in main and set the c
// Features: ["clap"]
// Dependencies:
// clap = "4"
+@@@use mingling::macros::buffer;
@@@use mingling::setup::BasicProgramSetup;
@@@use mingling::macros::dispatcher_clap;
-@@@#[derive(Default, clap::Parser, Groupped)]
+@@@#[derive(Default, clap::Parser, Grouped)]
@@@#[dispatcher_clap("greet", CMDGreet)]
@@@pub struct EntryGreet {
@@@ name: String,
@@@}
-@@@#[renderer]
-@@@fn render_greet(greet: EntryGreet) -> RenderResult {
-@@@ let mut r = RenderResult::new();
-@@@ write!(r, "Hello, {}!", greet.name).ok();
-@@@ r
+@@@#[renderer(buffer)]
+@@@fn render_greet(greet: EntryGreet) {
+@@@ r_println!("Hello, {}!", greet.name);
@@@}
fn main() {
let mut program = ThisProgram::new();
diff --git a/docs/pages/9-error-handling.md b/docs/pages/9-error-handling.md
index 3e004f2..f6e05e3 100644
--- a/docs/pages/9-error-handling.md
+++ b/docs/pages/9-error-handling.md
@@ -38,23 +38,20 @@ fn handle_greet(args: EntryGreet) -> Next {
Then write separate Renderers:
```rust
+@@@use mingling::macros::buffer;
@@@dispatcher!("greet", CMDGreet => EntryGreet);
@@@pack!(ResultGreeting = String);
@@@pack!(ErrorNameEmpty = String);
@@@#[chain] fn handle_greet(args: EntryGreet) -> Next { ResultGreeting::new(args.inner.first().cloned().unwrap_or_default()).to_render() }
-#[renderer]
-fn render_greeting(result: ResultGreeting) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "Hello, {}!", *result).ok();
- r
+#[renderer(buffer)]
+fn render_greet(result: ResultGreeting) {
+ r_println!("Hello, {}!", *result);
}
-#[renderer]
-fn render_error_name_empty(err: ErrorNameEmpty) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "Error: {}", *err).ok();
- r
+#[renderer(buffer)]
+fn render_error_name_empty(err: ErrorNameEmpty) {
+ r_println!("Error: {}", *err);
}
```
@@ -63,6 +60,7 @@ Each Renderer does its own job; what the user sees depends on what the Chain ret
## Complete Example
```rust
+@@@use mingling::macros::buffer;
dispatcher!("greet", CMDGreet => EntryGreet);
pack!(ResultGreeting = String);
@@ -78,18 +76,14 @@ fn handle_greet(args: EntryGreet) -> Next {
}
}
-#[renderer]
-fn render_greeting(result: ResultGreeting) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "Hello, {}!", *result).ok();
- r
+#[renderer(buffer)]
+fn render_greet(result: ResultGreeting) {
+ r_println!("Hello, {}!", *result);
}
-#[renderer]
-fn render_error_name_empty(err: ErrorNameEmpty) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "Error: {}", *err).ok();
- r
+#[renderer(buffer)]
+fn render_error_name_empty(err: ErrorNameEmpty) {
+ r_println!("Error: {}", *err);
}
fn main() {
diff --git a/docs/pages/advanced/2-structural-renderer.md b/docs/pages/advanced/2-structural-renderer.md
index fab7530..b54af22 100644
--- a/docs/pages/advanced/2-structural-renderer.md
+++ b/docs/pages/advanced/2-structural-renderer.md
@@ -27,6 +27,7 @@ After enabling `StructuralRendererSetup`, use `pack_structural!` instead of `pac
// Features: ["structural_renderer"]
// Dependencies:
// serde = "1"
+@@@use mingling::macros::buffer;
@@@use mingling::setup::StructuralRendererSetup;
@@@dispatcher!("render", CMDRender => EntryRender);
@@ -40,11 +41,9 @@ fn handle_render(args: EntryRender) -> Next {
ResultInfo::new((name, age)).into()
}
-#[renderer]
-fn render_info(r: ResultInfo) -> RenderResult {
- let mut result = RenderResult::new();
- writeln!(result, "{:?}", *r).ok();
- result
+#[renderer(buffer)]
+fn render_info(r: ResultInfo) {
+ r_println!("{:?}", *r);
}
```
@@ -62,19 +61,20 @@ When the user passes `--json`, the framework automatically serializes the render
## Customizing Output Structure
-The default output from `pack_structural!` includes an `inner` field. For full control over the output structure, define the type manually with `#[derive(StructuralData, Serialize, Groupped)]`:
+The default output from `pack_structural!` includes an `inner` field. For full control over the output structure, define the type manually with `#[derive(StructuralData, Serialize, Grouped)]`:
```rust
// Features: ["structural_renderer"]
// Dependencies:
// serde = "1"
+@@@use mingling::macros::buffer;
@@@use mingling::prelude::*;
@@@use mingling::setup::StructuralRendererSetup;
@@@use mingling::StructuralData;
@@@use serde::Serialize;
@@@dispatcher!("render", CMDRender => EntryRender);
-#[derive(Serialize, StructuralData, Groupped)]
+#[derive(Serialize, StructuralData, Grouped)]
struct Info {
name: String,
age: i32,
@@ -87,11 +87,9 @@ fn handle_render(args: EntryRender) -> Next {
Info { name, age }.to_render()
}
-#[renderer]
-fn render_info(info: Info) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "{} is {} years old", info.name, info.age).ok();
- r
+#[renderer(buffer)]
+fn render_info(info: Info) {
+ r_println!("{} is {} years old", info.name, info.age);
}
@@@
@@@fn main() {
diff --git a/docs/pages/concepts/3-any-output.md b/docs/pages/concepts/3-any-output.md
index f780377..f02805f 100644
--- a/docs/pages/concepts/3-any-output.md
+++ b/docs/pages/concepts/3-any-output.md
@@ -20,7 +20,7 @@ AnyOutput<G>
Here `G` is the program enum generated by `gen_program!()` (i.e., `ThisProgram` as you know it).
-Each type annotated with `pack!` or `#[derive(Groupped)]` is assigned to one variant of this enum.
+Each type annotated with `pack!` or `#[derive(Grouped)]` is assigned to one variant of this enum.
## ChainProcess: Data + Routing
@@ -38,17 +38,17 @@ This is why a Chain function returns `ChainProcess` instead of raw data—it bun
The dispatcher reads `NextProcess` to decide whether to continue the loop or exit to rendering.
-## Groupped: Who Is Who
+## Grouped: Who Is Who
-How does the dispatcher know whether an `AnyOutput` holds a `ResultName` or an `ErrorUserBlocked`? The answer is the `Groupped` trait:
+How does the dispatcher know whether an `AnyOutput` holds a `ResultName` or an `ErrorUserBlocked`? The answer is the `Grouped` trait:
```
-trait Groupped<G> {
+trait Grouped<G> {
fn member_id() -> G;
}
```
-When you use `pack!(ResultName = String)`, the macro automatically implements `Groupped` for `ResultName`, and `member_id()` returns the corresponding enum variant. The dispatcher looks at `member_id` and finds the matching Chain or Renderer.
+When you use `pack!(ResultName = String)`, the macro automatically implements `Grouped` for `ResultName`, and `member_id()` returns the corresponding enum variant. The dispatcher looks at `member_id` and finds the matching Chain or Renderer.
`to_chain()` and `to_render()` are essentially convenience methods on `AnyOutput` that construct `ChainProcess::Ok(any, Chain)` and `ChainProcess::Ok(any, Renderer)` respectively.
diff --git a/docs/pages/other/features.md b/docs/pages/other/features.md
index 325bb75..7994d4c 100644
--- a/docs/pages/other/features.md
+++ b/docs/pages/other/features.md
@@ -171,7 +171,7 @@ fn handle_hello(args: EntryHello) {}
### `group!`
Registers an external type as a member of the program group without modifying its definition.
-The type's simple name is used as the enum variant, just like `pack!` or `#[derive(Groupped)]`.
+The type's simple name is used as the enum variant, just like `pack!` or `#[derive(Grouped)]`.
```rust
// Features: ["extra_macros"]
@@ -274,12 +274,24 @@ See [example](https://mingling-rs.github.io/mingling/docs/example-viewer.html?na
**Description:**
-Enables the parser module, providing text parsing and analysis capabilities.
+Enables the argument parser module, providing argument parsing functionality.
-When enabled, you can use `Picker` for zero-cost argument extraction, supporting methods like `pick()` and `pick_or()`.
+When enabled, you can use `Picker` for simple argument extraction, supporting methods like `pick()` and `pick_or()`.
See [example](https://mingling-rs.github.io/mingling/docs/example-viewer.html?name=example-argument-parse)
+## Feature `picker`
+
+**Description:**
+
+Introduces the `arg-picker` dependency, providing more advanced argument parsing capabilities for Mingling.
+
+It can coexist with the `parser` and `clap` features, but it is recommended not to enable it alongside the `parser` feature, as their APIs are very similar.
+
+`picker` is an argument parser independent of Mingling and does not rely on the built-in argument extraction API of `mingling_core`.
+
+See [example](https://mingling-rs.github.io/mingling/docs/example-viewer.html?name=example-argument-picker)
+
## Feature `repl`
**Description:**
diff --git a/docs/pages/other/naming_rule.md b/docs/pages/other/naming_rule.md
index 2ede61f..1175ae0 100644
--- a/docs/pages/other/naming_rule.md
+++ b/docs/pages/other/naming_rule.md
@@ -96,7 +96,7 @@ Result + Content
| `ResultGreetSomeone` | Greeting result |
| `ResultFruitList` | Fruit list result |
-Result structs are expected to be consumed by the Renderer, and their internal structure should be designed for rendering aesthetics. Generally use `#[derive(Groupped)]` instead of `pack!()` wrapping for more flexible field control.
+Result structs are expected to be consumed by the Renderer, and their internal structure should be designed for rendering aesthetics. Generally use `#[derive(Grouped)]` instead of `pack!()` wrapping for more flexible field control.
### Error
@@ -166,6 +166,7 @@ fn handle_remote_add(args: EntryRemoteAdd, cwd: &ResCurrentDir, db: &mut ResData
## Complete Example
```rust
+@@@use mingling::macros::buffer;
@@@ #[derive(Default, Clone)]
@@@ struct ResDatabase { }
@@@ impl ResDatabase { fn has_remote(&self, remote: &String) -> bool { true } }
@@ -192,21 +193,17 @@ fn handle_state_operation_remotes(state: StateOperationRemotes, db: &ResDatabase
}
// Result rendering
-#[renderer]
-fn render_remote_added(result: ResultRemoteAdded) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "Remote added: {}", result.inner).ok();
- r
+
+#[renderer(buffer)]
+fn render_remote_added(result: ResultRemoteAdded) {
+ r_println!("Remote added: {}", result.inner);
}
// Error rendering
-#[renderer]
-fn render_error_repository_not_found(err: ErrorRepositoryNotFound) -> RenderResult {
- let mut r = RenderResult::new();
- writeln!(r, "Error: remote '{}' not found", err.inner).ok();
- r
+#[renderer(buffer)]
+fn render_error_repository_not_found(err: ErrorRepositoryNotFound) {
+ r_println!("Error: remote '{}' not found", err.inner);
}
-
```
<p align="center" style="font-size: 0.85em; color: gray;">