From c385dcf15930d2754b68c2794986ad30fc65c141 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Mon, 20 Jul 2026 09:50:28 +0800 Subject: docs: migrate renderer examples to buffer macro and update docs Update all documentation and code examples to use the new `#[renderer(buffer)]` pattern with `r_println!` instead of manually constructing `RenderResult` with `writeln!`. --- GETTING_STARTED.md | 9 +- README.md | 31 +++--- docs/_zh_CN/pages/10-help.md | 26 +++-- docs/_zh_CN/pages/11-resource-system.md | 18 ++-- docs/_zh_CN/pages/14-testing.md | 2 +- docs/_zh_CN/pages/4-render-result.md | 49 +++++---- docs/_zh_CN/pages/5-multiple-commands.md | 17 ++- docs/_zh_CN/pages/6-argument-parse-picker.md | 45 ++++---- docs/_zh_CN/pages/7-argument-parse-clap.md | 30 +++--- docs/_zh_CN/pages/9-error-handling.md | 34 +++--- .../_zh_CN/pages/advanced/2-structural-renderer.md | 18 ++-- docs/_zh_CN/pages/other/naming_rule.md | 17 ++- docs/pages/10-help.md | 26 +++-- docs/pages/11-resource-system.md | 18 ++-- docs/pages/14-testing.md | 2 +- docs/pages/4-render-result.md | 53 ++++++---- docs/pages/5-multiple-commands.md | 17 ++- docs/pages/6-argument-parse-picker.md | 116 +++++++++------------ docs/pages/7-argument-parse-clap.md | 30 +++--- docs/pages/9-error-handling.md | 34 +++--- docs/pages/advanced/2-structural-renderer.md | 18 ++-- docs/pages/other/naming_rule.md | 19 ++-- mingling/src/lib.rs | 10 +- 23 files changed, 298 insertions(+), 341 deletions(-) diff --git a/GETTING_STARTED.md b/GETTING_STARTED.md index bb2175f..1aad8d5 100644 --- a/GETTING_STARTED.md +++ b/GETTING_STARTED.md @@ -577,6 +577,7 @@ With the `structural_renderer` feature, users can add `--json` or `--yaml` flags // serde = "1" use mingling::prelude::*; +use mingling::macros::buffer; use mingling::setup::picker::StructuralRendererSetup; use mingling::Grouped; use mingling::StructuralData; @@ -600,11 +601,9 @@ fn render_info(args: EntryRender) -> Next { ResultInfo { name, age }.to_chain() } -#[renderer] -fn render_info_result(info: ResultInfo) -> RenderResult { - let mut result = RenderResult::new(); - writeln!(result, "{} is {} years old", info.name, info.age).ok(); - result +#[renderer(buffer)] +fn render_info_result(info: ResultInfo) { + r_println!("{} is {} years old", info.name, info.age); } fn main() { diff --git a/README.md b/README.md index cb7efb0..2c21198 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,9 @@ You can use this approach to separate computation from result rendering, like th ```rust // Features: ["picker"] +use mingling::macros::buffer; +use mingling::prelude::*; + dispatcher!("calc", CMDCalculate => EntryCalculate); pack!(StateSumNumbers = Vec); pack!(ResultNumber = i32); @@ -82,11 +85,9 @@ fn handle_state_sum_numbers(sum: StateSumNumbers) -> ResultNumber { } // Renderer: return the render result and let the framework handle output -#[renderer] -fn render_number(number: ResultNumber) -> RenderResult { - let mut result = RenderResult::new(); - writeln!(result, "Number: {}", *number).ok(); - result +#[renderer(buffer)] +fn render_number(number: ResultNumber) { + r_println!("Number: {}", *number); } ``` @@ -117,16 +118,16 @@ features = [] ## Roadmap - [x] Milestone.1 "MVP" 🎉 - - [x] [[0.1.4](https://docs.rs/mingling/0.1.4/mingling/)] [`core`] [`structural_renderer`] **Mingling** can render data into serializable formats via `--json` and `--yaml` flags - - [x] [[0.1.5](https://docs.rs/mingling/0.1.5/mingling/)] [`core`] [`comp`] **Mingling** can dynamically invoke itself to provide completions for shells like `bash`, `zsh`, `fish`, and `pwsh` - - [x] [[0.1.6](https://docs.rs/mingling/0.1.6/mingling/)] [`core`] [`comp`] **Mingling** can gather more context for smarter completions - - [x] [[0.1.7](https://docs.rs/mingling/0.1.7/mingling/)] [`clap`] Provides a **Clap** compatibility layer, allowing **Mingling** to reuse its powerful parsing capabilities - - [x] [[0.1.7](https://docs.rs/mingling/0.1.7/mingling/)] [`core`] **Mingling** can intercept `-h` or `--help` flags to display custom help text for each subcommand - - [x] [[0.1.7](https://docs.rs/mingling/0.1.7/mingling/)] [`mling`] Provides a basic scaffolding tool (`mling`) for rapid development and debugging - - [x] [[0.1.8](https://docs.rs/mingling/0.1.8/mingling/)] [`core`] [`dispatch_tree`] Converts the subcommand list into a prefix tree to improve command matching speed - - [x] [[0.1.9](https://docs.rs/mingling/0.1.9/mingling/)] [`core`] [`dev_toolkits`] Provides debugging interfaces for developers to capture invocation information when issues arise (`InvokeStackDisplay`) (indirectly implemented via `ProgramHook`) - - [x] [[0.1.9](https://docs.rs/mingling/0.1.9/mingling/)] [`core`] [`repl`] Provides REPL capability (`program.exec_repl();`) - - [x] [[0.2.0](https://docs.rs/mingling/0.2.0/mingling/)] Complete documentation, tests, and examples + - [x] [[0.1.4](https://docs.rs/mingling/0.1.4/mingling/)] [`core`] [`structural_renderer`] **Mingling** can render data into serializable formats via `--json` and `--yaml` flags + - [x] [[0.1.5](https://docs.rs/mingling/0.1.5/mingling/)] [`core`] [`comp`] **Mingling** can dynamically invoke itself to provide completions for shells like `bash`, `zsh`, `fish`, and `pwsh` + - [x] [[0.1.6](https://docs.rs/mingling/0.1.6/mingling/)] [`core`] [`comp`] **Mingling** can gather more context for smarter completions + - [x] [[0.1.7](https://docs.rs/mingling/0.1.7/mingling/)] [`clap`] Provides a **Clap** compatibility layer, allowing **Mingling** to reuse its powerful parsing capabilities + - [x] [[0.1.7](https://docs.rs/mingling/0.1.7/mingling/)] [`core`] **Mingling** can intercept `-h` or `--help` flags to display custom help text for each subcommand + - [x] [[0.1.7](https://docs.rs/mingling/0.1.7/mingling/)] [`mling`] Provides a basic scaffolding tool (`mling`) for rapid development and debugging + - [x] [[0.1.8](https://docs.rs/mingling/0.1.8/mingling/)] [`core`] [`dispatch_tree`] Converts the subcommand list into a prefix tree to improve command matching speed + - [x] [[0.1.9](https://docs.rs/mingling/0.1.9/mingling/)] [`core`] [`dev_toolkits`] Provides debugging interfaces for developers to capture invocation information when issues arise (`InvokeStackDisplay`) (indirectly implemented via `ProgramHook`) + - [x] [[0.1.9](https://docs.rs/mingling/0.1.9/mingling/)] [`core`] [`repl`] Provides REPL capability (`program.exec_repl();`) + - [x] [[0.2.0](https://docs.rs/mingling/0.2.0/mingling/)] Complete documentation, tests, and examples - [ ] Milestone.2 "More Comfortable Dev and User Experience" - [ ] [`mling` / `mingling-cli`] - [ ] **Mingling** Linter diff --git a/docs/_zh_CN/pages/10-help.md b/docs/_zh_CN/pages/10-help.md index 8cee2aa..1c4e410 100644 --- a/docs/_zh_CN/pages/10-help.md +++ b/docs/_zh_CN/pages/10-help.md @@ -13,18 +13,17 @@ Mingling 里用 `#[help]` 宏给命令添加帮助文本。 ```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] -> 帮助函数同样通过 `writeln!` 向 `RenderResult` 写入内容,因为 `#[help]` 遵循渲染管线 —— 它是由 `--help` 标志提前触发的短路渲染,而不是管线之外的逻辑。 +> 帮助函数同样通过 `r_println!` 向 `RenderResult` 写入内容,因为 `#[help]` 遵循渲染管线 —— 它是由 `--help` 标志提前触发的短路渲染,而不是管线之外的逻辑。 ## 全局帮助 @@ -32,14 +31,13 @@ fn help_greet(_entry: EntryGreet) -> RenderResult { ```rust @@@use mingling::macros::help; +@@@use mingling::macros::buffer; // 用户直接输入 --help 时触发 -#[help] -fn help_root(entry: ErrorDispatcherNotFound) -> RenderResult { - let mut r = RenderResult::new(); - writeln!(r, "Usage: my-cli ").ok(); - writeln!(r, "Commands:").ok(); - writeln!(r, " greet Say hello").ok(); - r +#[help(buffer)] +fn help_root(entry: ErrorDispatcherNotFound) { + r_println!("Usage: my-cli "); + r_println!("Commands:"); + r_println!(" greet Say hello"); } ``` diff --git a/docs/_zh_CN/pages/11-resource-system.md b/docs/_zh_CN/pages/11-resource-system.md index a8aab4d..b127a3a 100644 --- a/docs/_zh_CN/pages/11-resource-system.md +++ b/docs/_zh_CN/pages/11-resource-system.md @@ -27,6 +27,7 @@ fn main() { 在 Chain 或 Renderer 中,只需在参数列表里声明你要的资源: ```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 { 用 `&mut T` 注入可修改资源: ```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/_zh_CN/pages/14-testing.md b/docs/_zh_CN/pages/14-testing.md index 621d43e..031be59 100644 --- a/docs/_zh_CN/pages/14-testing.md +++ b/docs/_zh_CN/pages/14-testing.md @@ -16,7 +16,7 @@ Renderer 是最容易测试的——调用函数,断言返回结果: #[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/_zh_CN/pages/4-render-result.md b/docs/_zh_CN/pages/4-render-result.md index 81c5102..8b29fca 100644 --- a/docs/_zh_CN/pages/4-render-result.md +++ b/docs/_zh_CN/pages/4-render-result.md @@ -10,18 +10,31 @@ 跟 `#[chain]` 类似,`#[renderer]` 用于标记一个输出函数: ```rust -use std::io::Write; +@@@use mingling::macros::buffer; +@@@pack!(ResultName = String); +#[renderer(buffer)] +fn render_name(name: ResultName) { + r_println!("Hello, {}!", *name); +} +``` + +Renderer 接收 Chain 产出的结果,然后返回一个 `RenderResult`。在函数内部,创建 `RenderResult`,用 `r_print!` / `r_println!` 写入内容,最后返回它。 + +## `buffer` 拓展 + +若您觉得显式创建并返回 `RenderResult` 过于繁琐,可以使用 `#[renderer(buffer)]` 在原始函数内植入一个缓冲区。 + +```rust +use mingling::macros::buffer; @@@pack!(ResultName = String); -#[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); } ``` -Renderer 接收 Chain 产出的结果,然后返回一个 `RenderResult`。在函数内部,创建 `RenderResult`,用 `write!` / `writeln!`(来自 [`std::io::Write`](https://doc.rust-lang.org/std/io/trait.Write.html))写入内容,最后返回它。 +这样,你的 renderer 函数就获得了更简洁的语法,但也引入了一个隐含机制:它会向函数内注入一个名为 `__render_result_buffer` 的可变 `RenderResult`。当 `r_print!` 宏没有显式指定 `RenderResult` 时,它便会按照约定向该缓冲区追加输出。 ## `RenderResult` 类型 @@ -36,7 +49,7 @@ Renderer 接收 Chain 产出的结果,然后返回一个 `RenderResult`。在 把三篇教程的内容合在一起,你的第一个 Mingling 程序就完整了: ```rust -use std::io::Write; +use mingling::macros::buffer; // 1. 用 Dispatcher 声明命令 dispatcher!("greet", CMDGreet => EntryGreet); @@ -55,11 +68,9 @@ fn handle_greet(args: EntryGreet) -> Next { } // 4. 用 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. 在 main 函数内装配程序并运行 @@ -108,17 +119,15 @@ cargo run -- great `gen_program!()` 自动生成了一个 `ErrorDispatcherNotFound` 类型,包裹 `Vec`——它存的是用户输入的那些没匹配到的命令。你只需要给它写一个 Renderer: ```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/_zh_CN/pages/5-multiple-commands.md b/docs/_zh_CN/pages/5-multiple-commands.md index 38ce2cf..ff6596d 100644 --- a/docs/_zh_CN/pages/5-multiple-commands.md +++ b/docs/_zh_CN/pages/5-multiple-commands.md @@ -10,6 +10,7 @@ 继续在同一个项目里操作: ```rust +@@@use mingling::macros::buffer; // 声明两个命令 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/_zh_CN/pages/6-argument-parse-picker.md b/docs/_zh_CN/pages/6-argument-parse-picker.md index 4bf162c..f0609ed 100644 --- a/docs/_zh_CN/pages/6-argument-parse-picker.md +++ b/docs/_zh_CN/pages/6-argument-parse-picker.md @@ -139,6 +139,7 @@ fn handle_test_entry(prev: EntryTest) -> Next { ```rust // Features: ["parser", "extra_macros"] +@@@use mingling::macros::buffer; @@@use mingling::macros::route; @@@dispatcher!("greet", CMDGreet => EntryGreet); @@@pack!(ResultName = String); @@ -155,11 +156,9 @@ fn handle_greet_entry(prev: EntryGreet) -> Next { ResultName::new(name).into() } -#[renderer] -fn render_greet(result: ResultName) -> RenderResult { - let mut r = RenderResult::new(); - writeln!(r, "Hello, {}!", *result).ok(); - r +#[renderer(buffer)] +fn render_greet(result: ResultName) { + r_println!("Hello, {}!", *result); } ``` @@ -225,6 +224,7 @@ fn handle_greet_entry(prev: EntryGreet) -> Next { ```rust // Features: ["parser", "extra_macros"] +@@@use mingling::macros::buffer; @@@use mingling::macros::route; @@@dispatcher!("greet", CMDGreet => EntryGreet); @@@pack!(ResultName = String); @@ -247,19 +247,15 @@ 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); } ``` @@ -314,6 +310,7 @@ fn parse_size() { ```rust // Features: ["parser"] +@@@use mingling::macros::buffer; @@@use mingling::parser::{Pickable, Argument}; @@@use mingling::Flag; #[derive(Default, Clone)] @@ -341,11 +338,9 @@ fn handle_connect_entry(prev: EntryConnect) -> Next { ResultConnected::new(address).into() } -#[renderer] -fn render_connected(addr: ResultConnected) -> RenderResult { - let mut r = RenderResult::new(); - 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); } ``` @@ -362,6 +357,7 @@ Connected: IP: 127.0.0.1 PORT: 8080 ```rust // Features: ["parser"] +@@@use mingling::macros::buffer; @@@use mingling::parser::PickableEnum; @@@use mingling::EnumTag; #[derive(Debug, Default, EnumTag)] @@ -382,11 +378,9 @@ 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); } ``` @@ -395,3 +389,4 @@ fn render_fruit(prev: ResultFruit) -> RenderResult {

Written by @Weicao-CatilGrass

+```` diff --git a/docs/_zh_CN/pages/7-argument-parse-clap.md b/docs/_zh_CN/pages/7-argument-parse-clap.md index 21f886c..fda93f3 100644 --- a/docs/_zh_CN/pages/7-argument-parse-clap.md +++ b/docs/_zh_CN/pages/7-argument-parse-clap.md @@ -25,6 +25,7 @@ features = ["derive", "color"] // Dependencies: // clap = "4" @@@ use mingling::macros::dispatcher_clap; +@@@ use mingling::macros::buffer; #[derive(Default, clap::Parser, Grouped)] #[dispatcher_clap("greet", CMDGreet, help = true, error = ErrorGreetParsed)] pub struct EntryGreet { @@ -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,6 +59,7 @@ fn render_greet_parse_failed(err: ErrorGreetParsed) -> RenderResult { // Features: ["clap"] // Dependencies: // clap = "4" +@@@use mingling::macros::buffer; @@@use mingling::setup::BasicProgramSetup; @@@use mingling::macros::dispatcher_clap; @@@#[derive(Default, clap::Parser, Grouped)] @@ -69,11 +67,9 @@ fn render_greet_parse_failed(err: ErrorGreetParsed) -> RenderResult { @@@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/_zh_CN/pages/9-error-handling.md b/docs/_zh_CN/pages/9-error-handling.md index 07c82da..4ce61ab 100644 --- a/docs/_zh_CN/pages/9-error-handling.md +++ b/docs/_zh_CN/pages/9-error-handling.md @@ -38,23 +38,20 @@ fn handle_greet(args: EntryGreet) -> Next { 然后各自写 Renderer: ```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_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_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 @@ fn render_error_name_empty(err: ErrorNameEmpty) -> RenderResult { ## 完整的例子 ```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_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_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/_zh_CN/pages/advanced/2-structural-renderer.md b/docs/_zh_CN/pages/advanced/2-structural-renderer.md index 70bed79..a498dac 100644 --- a/docs/_zh_CN/pages/advanced/2-structural-renderer.md +++ b/docs/_zh_CN/pages/advanced/2-structural-renderer.md @@ -27,6 +27,7 @@ features = ["structural_renderer"] // 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); } ``` @@ -68,6 +67,7 @@ fn render_info(r: ResultInfo) -> RenderResult { // Features: ["structural_renderer"] // Dependencies: // serde = "1" +@@@use mingling::macros::buffer; @@@use mingling::prelude::*; @@@use mingling::setup::StructuralRendererSetup; @@@use mingling::StructuralData; @@ -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/_zh_CN/pages/other/naming_rule.md b/docs/_zh_CN/pages/other/naming_rule.md index c854ba1..6e65ee4 100644 --- a/docs/_zh_CN/pages/other/naming_rule.md +++ b/docs/_zh_CN/pages/other/naming_rule.md @@ -166,6 +166,7 @@ fn handle_remote_add(args: EntryRemoteAdd, cwd: &ResCurrentDir, db: &mut ResData ## 完整示例 ```rust +@@@use mingling::macros::buffer; @@@ #[derive(Default, Clone)] @@@ struct ResDatabase { } @@@ impl ResDatabase { fn has_remote(&self, remote: &String) -> bool { true } } @@ -192,19 +193,15 @@ fn handle_state_operation_remotes(state: StateOperationRemotes, db: &ResDatabase } // 结果渲染 -#[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); } // 错误渲染 -#[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); } ``` 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 ").ok(); - writeln!(r, "Commands:").ok(); - writeln!(r, " greet Say hello").ok(); - r +#[help(buffer)] +fn help_root(entry: ErrorDispatcherNotFound) { + r_println!("Usage: my-cli "); + 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/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`—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

-In previous tutorials, we extracted args manually from `EntryGreet.inner` (`Vec`). +In previous tutorials, we manually extracted parameters from `EntryGreet.inner` (`Vec`). ```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`: 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`. 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`. +With `pick_or_route`, the code becomes more involved: `.unpack()` no longer returns the value directly, but `Result`. -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::(flag)` → implicit: flag present means `true` -- Using `.pick::(flag)` or `.pick::(flag)` → explicit +- `.pick::(flag)` uses implicit mode: the flag being present means `true` +- `.pick::(flag)` or `.pick::(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`.

Written by @Weicao-CatilGrass diff --git a/docs/pages/7-argument-parse-clap.md b/docs/pages/7-argument-parse-clap.md index 86fce35..dcebd65 100644 --- a/docs/pages/7-argument-parse-clap.md +++ b/docs/pages/7-argument-parse-clap.md @@ -25,6 +25,7 @@ Add `#[dispatcher_clap]` on a `clap::Parser` struct to auto-generate a Dispatche // Dependencies: // clap = "4" @@@ use mingling::macros::dispatcher_clap; +@@@ use mingling::macros::buffer; #[derive(Default, clap::Parser, Grouped)] #[dispatcher_clap("greet", CMDGreet, help = true, error = ErrorGreetParsed)] pub struct EntryGreet { @@ -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,6 +59,7 @@ 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, Grouped)] @@ -69,11 +67,9 @@ If you need `--help` support, register `BasicProgramSetup` in main and set the c @@@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 910b197..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); } ``` @@ -68,6 +67,7 @@ The default output from `pack_structural!` includes an `inner` field. For full c // Features: ["structural_renderer"] // Dependencies: // serde = "1" +@@@use mingling::macros::buffer; @@@use mingling::prelude::*; @@@use mingling::setup::StructuralRendererSetup; @@@use mingling::StructuralData; @@ -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/other/naming_rule.md b/docs/pages/other/naming_rule.md index 089a711..1175ae0 100644 --- a/docs/pages/other/naming_rule.md +++ b/docs/pages/other/naming_rule.md @@ -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); } - ```

diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs index d1691fb..b6c9a5a 100644 --- a/mingling/src/lib.rs +++ b/mingling/src/lib.rs @@ -223,6 +223,12 @@ pub mod prelude { /// Like `pack!` but also marks the type for structured output #[cfg(all(feature = "macros", feature = "structural_renderer"))] pub use mingling_macros::pack_structural; + /// `r_print!` - Prints text to a `RenderResult` buffer (without newline). + /// See the macro documentation for implicit vs. explicit buffer usage. + pub use mingling_macros::r_print; + /// `r_println!` - Prints text to a `RenderResult` buffer (with newline). + /// See the macro documentation for implicit vs. explicit buffer usage. + pub use mingling_macros::r_println; /// Re-export of the `completion` macro for generating completion entries. #[cfg(all(feature = "macros", feature = "comp"))] @@ -237,8 +243,4 @@ pub mod prelude { #[cfg(feature = "picker")] pub use crate::picker::EntryPicker; - - /// Used to enable the `writeln!` macro for `RenderResult` - #[cfg(feature = "core")] - pub use std::io::Write; } -- cgit