From f31347533c2b13f58eeae29ffc3910ca5e2f04d5 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Sat, 2 May 2026 00:52:41 +0800 Subject: Add documentation for fallback mechanisms --- docs/_sidebar.md | 1 + docs/pages/2-implementing-fallbacks.md | 139 +++++++++++++++++++++++++++ docs/pages/2-implementing-fallbacks_zh_CN.md | 139 +++++++++++++++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 docs/pages/2-implementing-fallbacks.md create mode 100644 docs/pages/2-implementing-fallbacks_zh_CN.md (limited to 'docs') diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 6bde411..cb85688 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -1,4 +1,5 @@ - [Welcome!](README) * [Creating your first Program](pages/1-creating-your-first-program) * [创建您的第一个程序](pages/1-creating-your-first-program_zh_CN) +* [Implementing Fallbacks](pages/2-implementing-fallbacks) * [实现回退机制](pages/2-implementing-fallbacks_zh_CN) diff --git a/docs/pages/2-implementing-fallbacks.md b/docs/pages/2-implementing-fallbacks.md new file mode 100644 index 0000000..a376580 --- /dev/null +++ b/docs/pages/2-implementing-fallbacks.md @@ -0,0 +1,139 @@ +

Implementing Fallbacks

+

+ Handling error cases in your program using a fallback mechanism +

+ +## Recap + + In the last post, we introduced how to develop a basic CLI program using **Mingling**: you can use the `"greet"` subcommand to output `"Hello, World!"`, or use `"greet Alice"` to output `"Hello, Alice!"` + + But what happens when the user does not enter `"greet"`? Let's type a command and find out ⌨️ + +```bash +~> your-bin hello +~> your-bin hello Alice +``` + + **It does nothing!** 👆 + + Let me explain why: **Mingling** doesn't presume to act; it will not output anything to the terminal no matter what happens (except for `panic!` under `unwind`) + + This means that if you need to actively do something when your CLI program encounters an error, you have to state it explicitly. + + Fortunately, **Mingling** provides a convenient interface for this functionality: inside the `gen_program!` macro, two `FallBack` types are generated + +|Type|When it occurs|How it occurs| +|-|-|-| +|RendererNotFound|When a renderer cannot be found for scheduling|Scheduled as a `Chain`| +|DispatcherNotFound|When a command is entered but no dispatcher matches|Scheduled as a `Chain`| + +### The `DispatcherNotFound` Type + + Let's first focus on the `DispatcherNotFound` type. It is produced as follows: + +```rust +// 1. Define the `greet` command +dispatcher!("greet", GreetCommand => GreetEntry); + +fn main() { + // ->> User enters "hello Alice" + let mut program = ThisProgram::new(); + + // 2. Import the `greet` command + program.with_dispatcher(GreetCommand); + + // 3. Execute the program + program.exec(); +} + +// ... + +// 5. Receive the DispatcherNotFound dispatch +#[renderer] +fn dispatcher_not_found(prev: DispatcherNotFound) { + // 6. Output + r_println!( + "Cannot match any command! Current input: \"{}\"", + prev.join(" ") + ); +} + +// 4. Cannot match any dispatcher named `hello` +// Forward the user's arguments as-is to DispatcherNotFound +gen_program!(); +``` + + The output of the above program is: + +```bash +~> omg hello +Cannot match any command! Current input: "hello" + +~> omg hello Alice +Cannot match any command! Current input: "hello Alice" +``` + + Now, if the user enters a command that doesn't match, **Mingling** will output the appropriate message! + +## The `RendererNotFound` Type + + `RendererNotFound` can be produced in two ways: + + 1. The type was explicitly dispatched to a `Renderer` (using the `.to_render()` function), but the type does not have a renderer implementation + 2. The type was dispatched to a `Chain`, but the type has neither a chain nor a renderer implementation + + Generally, `RendererNotFound` **should not occur in business logic**: its dispatch means your type needs to be rendered but can't be. You can use this type to pinpoint which type is missing a renderer implementation ✏️ + +```rust +dispatcher!("greet", GreetCommand => GreetEntry); + +fn main() { + let mut program = ThisProgram::new(); + + program.with_dispatcher(GreetCommand); + program.exec(); +} + +pack!(ResultGreetSomeone = String); + +#[chain] +fn handle_greet_entry(prev: GreetEntry) -> NextProcess { + let args = prev.inner; + let name = args.first().cloned().unwrap_or_else(|| "World".to_string()); + + ResultGreetSomeone::new(name) +} + +// Let's intentionally remove the renderer implementation for `ResultGreetSomeone` +// #[renderer] +// fn render_greet_someone(prev: ResultGreetSomeone) { +// r_println!("Hello, {}!", *prev); +// } + +#[renderer] +fn renderer_not_found(prev: RendererNotFound) { + if *prev == "DispatcherNotFound" { + return; // Exclude the "DispatcherNotFound" type + } + + // Trigger `panic!` when a renderer is not found + panic!("Renderer \"{}\" not found!", *prev); +} + +gen_program!(); + +``` + + The output of the above program is: + +```bash +~> your-bin greet Alice + +thread 'main' (90772) panicked at src/bin/your-bin.rs:30:5: +Renderer "ResultGreetSomeone" not found! +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +``` + +

+ Written by @Weicao-CatilGrass +

diff --git a/docs/pages/2-implementing-fallbacks_zh_CN.md b/docs/pages/2-implementing-fallbacks_zh_CN.md new file mode 100644 index 0000000..071a5d1 --- /dev/null +++ b/docs/pages/2-implementing-fallbacks_zh_CN.md @@ -0,0 +1,139 @@ +

实现回退机制

+

+ 使用回退机制处理程序的错误情况 +

+ +## 书接上文 + + 在上文中,我们介绍了如何使用 **Mingling** 开发基本的命令行程序:你可以使用 `"greet"` 子命令输出 `"Hello, World!"`,也可以使用 `"greet Alice"` 输出 `"Hello, Alice!"` + + 而当用户并未输入 `"greet"` 时呢?让我们键入命令尝试一下 ⌨️ + +```bash +~> your-bin hello +~> your-bin hello Alice +``` + + **它没有任何反应!** 👆 + + 让我来解释为什么:**Mingling** 不自作主张,无论发生什么它都不会输出内容到终端(除了 `unwind` 下的 `panic!`) + + 这意味着,如果您需要在命令行程序出错时能够主动地做些什么,你就得显式声明。 + + 好在 **Mingling** 提供了较为方便的接口实现该功能:在 `gen_program!` 宏中,会生成两个 `FallBack` 类型 + +|类型|发生时机|发生方式| +|-|-|-| +|RendererNotFound|调度无法找到的渲染器时|作为 `Chain` 调度| +|DispatcherNotFound|输入命令但无法匹配分发器|作为 `Chain` 调度| + +### `DispatcherNotFound` 类型 + + 首先让我们关注 `DispatcherNotFound` 类型,它的产生方式如下: + +```rust +// 1. 定义 `greet` 命令 +dispatcher!("greet", GreetCommand => GreetEntry); + +fn main() { + // ->> 用户输入 "hello Alice" + let mut program = ThisProgram::new(); + + // 2. 导入 `greet` 命令 + program.with_dispatcher(GreetCommand); + + // 3. 执行程序 + program.exec(); +} + +// ... + +// 5. 接收 DispatcherNotFound 调度 +#[renderer] +fn dispatcher_not_found(prev: DispatcherNotFound) { + // 6. 输出 + r_println!( + "Cannot match any command! Current input: \"{}\"", + prev.join(" ") + ); +} + +// 4. 无法匹配到任何名为 `hello` 的分发器 +// 将用户参数原样分发到 DispatcherNotFound +gen_program!(); +``` + + 上述程序的运行效果为: + +```bash +~> omg hello +Cannot match any command! Current input: "hello" + +~> omg hello Alice +Cannot match any command! Current input: "hello Alice" +``` + + 现在若用户输入了不匹配的命令,**Mingling** 将会输出对应的内容! + +## `RendererNotFound` 类型 + + `RendererNotFound` 有两种可能产生: + + 1. 该类型被显式分发到了 `Renderer` (使用 `.to_render()` 函数),但是该类型未实现渲染器 + 2. 该类型被分发到了 `Chain`,但是该类型未实现链,也未实现渲染器 + + 一般来说,`RendererNotFound` **不应该在业务逻辑中产生**:它被调度意味着您的类型需要被渲染但是并不能被渲染。您可以使用该类型来定位哪个类型缺失渲染器实现 ✏️ + +```rust +dispatcher!("greet", GreetCommand => GreetEntry); + +fn main() { + let mut program = ThisProgram::new(); + + program.with_dispatcher(GreetCommand); + program.exec(); +} + +pack!(ResultGreetSomeone = String); + +#[chain] +fn handle_greet_entry(prev: GreetEntry) -> NextProcess { + let args = prev.inner; + let name = args.first().cloned().unwrap_or_else(|| "World".to_string()); + + ResultGreetSomeone::new(name) +} + +// 让我们故意去除 `ResultGreetSomeone` 类型的渲染器实现 +// #[renderer] +// fn render_greet_someone(prev: ResultGreetSomeone) { +// r_println!("Hello, {}!", *prev); +// } + +#[renderer] +fn renderer_not_found(prev: RendererNotFound) { + if *prev == "DispatcherNotFound" { + return; // 排除 "DispatcherNotFound" 类型 + } + + // 当未找到渲染器时触发 `panic!` + panic!("Renderer \"{}\" not found!", *prev); +} + +gen_program!(); + +``` + + 上述程序的运行效果为: + +```bash +~> your-bin greet Alice + +thread 'main' (90772) panicked at src/bin/your-bin.rs:30:5: +Renderer "ResultGreetSomeone" not found! +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +``` + +

+ Written by @Weicao-CatilGrass +

-- cgit