aboutsummaryrefslogtreecommitdiff
path: root/docs/pages/9-error-handling.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/pages/9-error-handling.md')
-rw-r--r--docs/pages/9-error-handling.md120
1 files changed, 120 insertions, 0 deletions
diff --git a/docs/pages/9-error-handling.md b/docs/pages/9-error-handling.md
new file mode 100644
index 0000000..ec1f422
--- /dev/null
+++ b/docs/pages/9-error-handling.md
@@ -0,0 +1,120 @@
+<h1 align="center">Error Handling</h1>
+<p align="center">
+ Gracefully present errors to the user
+</p>
+
+A pipeline isn't just the happy path. When input is invalid, a resource isn't found, or an operation fails, you need a place to handle these "surprises" instead of letting the program panic.
+
+## Two Paths: Success vs. Error
+
+Recall the pipeline model: Chain's return value is `Next`, which has two destinations:
+
+| Route | Meaning |
+| -------------- | ------------------------------------------- |
+| `.to_render()` | Got a result, hand it to a Renderer to show |
+| `.to_chain()` | Not done yet, hand it to the next Chain |
+
+Error values can also take either path—you can render the error msg directly, or pass it to the next Chain for potential recovery.
+
+## Distinguish Errors with Dedicated Types
+
+```rust
+@@@dispatcher!("greet", CMDGreet => EntryGreet);
+pack!(ResultGreeting = String);
+pack!(ErrorNameEmpty = String);
+
+#[chain]
+fn handle_greet(args: EntryGreet) -> Next {
+ let name = args.inner.first().cloned().unwrap_or_default();
+
+ if name.is_empty() {
+ ErrorNameEmpty::new("name is required".to_string()).to_render()
+ } else {
+ ResultGreeting::new(name).to_render()
+ }
+}
+```
+
+Then write separate Renderers:
+
+```rust
+@@@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) {
+ r_println!("Hello, {}!", *result);
+}
+
+#[renderer]
+fn render_error_name_empty(err: ErrorNameEmpty) {
+ r_println!("Error: {}", *err);
+}
+```
+
+Each Renderer does its own job; what the user sees depends on what the Chain returned.
+
+## Complete Example
+
+```rust
+dispatcher!("greet", CMDGreet => EntryGreet);
+
+pack!(ResultGreeting = String);
+pack!(ErrorNameEmpty = String);
+
+#[chain]
+fn handle_greet(args: EntryGreet) -> Next {
+ let name = args.inner.first().cloned().unwrap_or_default();
+ if name.is_empty() {
+ ErrorNameEmpty::new("name is required".to_string()).to_render()
+ } else {
+ ResultGreeting::new(name).to_render()
+ }
+}
+
+#[renderer]
+fn render_greeting(result: ResultGreeting) {
+ r_println!("Hello, {}!", *result);
+}
+
+#[renderer]
+fn render_error_name_empty(err: ErrorNameEmpty) {
+ r_println!("Error: {}", *err);
+}
+
+fn main() {
+ let mut program = ThisProgram::new();
+ program.with_dispatcher(CMDGreet);
+ program.exec_and_exit();
+}
+
+gen_program!();
+```
+
+Output:
+
+```text
+~# my-cli greet Alice
+Hello, Alice!
+
+~# my-cli greet
+Error: name is required
+```
+
+## About `pack_err!`
+
+If you've enabled `extra_macros`, you can use `pack_err!` to quickly declare an error type with an auto-generated `name` field:
+
+```rust
+// Features: ["extra_macros"]
+pack_err!(ErrorNotFound);
+// Generates: struct ErrorNotFound { pub name: String }
+```
+
+See [Feature List](pages/other/features) for details.
+
+<p align="center" style="font-size: 0.85em; color: gray;">
+ Written by @Weicao-CatilGrass
+</p>