blob: a4d5b0a3e4ef6432f072077bd8a36bb6b4d53777 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
<h1 align="center">错误处理</h1>
<p align="center">
将错误优雅地展示给用户
</p>
管线里不只有成功路径。当输入有误、资源找不到、操作失败时,你需要一个地方来处理这些"意外",而不是让程序 panic。
## 两个路径:成功 vs 错误
回顾管线模型,Chain 的返回值是 `Next`,它有两个去向:
| 路由 | 含义 |
| -------------- | ---------------------------- |
| `.to_render()` | 出结果了,交给 Renderer 展示 |
| `.to_chain()` | 还没处理完,交给下一个 Chain |
错误类型的值也可以走任意一条路——你可以选择直接渲染错误信息,也可以交给下一个 Chain 尝试恢复。
## 用独立类型区分错误
```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:
```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);
}
```
两个 Renderer 各司其职,用户看到什么取决于 Chain 返回了什么。
## 完整的例子
```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!();
```
运行效果:
```text
~# my-cli greet Alice
Hello, Alice!
~# my-cli greet
Error: name is required
```
## 关于 `pack_err!`
如果你启用了 `extra_macros`,还可以用 `pack_err!` 快速声明带有自动 `name` 字段的错误类型:
```rust
// Features: ["extra_macros"]
pack_err!(ErrorNotFound);
// 生成: struct ErrorNotFound { pub name: String }
```
详见 [特性列表](pages/other/features)。
<p align="center" style="font-size: 0.85em; color: gray;">
Written by @Weicao-CatilGrass
</p>
|