diff options
Diffstat (limited to 'docs/pages')
| -rw-r--r-- | docs/pages/10-help.md | 26 | ||||
| -rw-r--r-- | docs/pages/11-resource-system.md | 18 | ||||
| -rw-r--r-- | docs/pages/14-testing.md | 2 | ||||
| -rw-r--r-- | docs/pages/3-define-a-chain.md | 2 | ||||
| -rw-r--r-- | docs/pages/4-render-result.md | 53 | ||||
| -rw-r--r-- | docs/pages/5-multiple-commands.md | 17 | ||||
| -rw-r--r-- | docs/pages/6-argument-parse-picker.md | 116 | ||||
| -rw-r--r-- | docs/pages/7-argument-parse-clap.md | 34 | ||||
| -rw-r--r-- | docs/pages/9-error-handling.md | 34 | ||||
| -rw-r--r-- | docs/pages/advanced/2-structural-renderer.md | 22 | ||||
| -rw-r--r-- | docs/pages/concepts/3-any-output.md | 10 | ||||
| -rw-r--r-- | docs/pages/other/features.md | 18 | ||||
| -rw-r--r-- | docs/pages/other/naming_rule.md | 21 |
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;"> |
