blob: 543ee55c7b97d5e5d15cfdafccff6c326c6c0347 (
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
|
<h1 align="center">钩子系统</h1>
<p align="center">
如何使用 ProgramHook 向程序内部插入行为
</p>
Hook 让你在管线的各个生命周期节点插入自定义逻辑 —— 在 dispatch 之前、chain 之后、render 前后、程序退出时 ……
你可以把横切关注点(日志、鉴权、指标收集)写在 hook 里,而不是散落在各处的业务代码中。
## 基本用法
`ProgramHook` 采用 builder 模式构造:
```rust
@@@use mingling::hook::ProgramHook;
fn main() {
let mut program = ThisProgram::new();
program.with_hook(
ProgramHook::empty()
.on_pre_chain(|info| {
println!("before chain: {}", info.input);
})
.on_post_render(|info| {
println!("after render: {}", info.result);
}),
);
program.exec_and_exit();
}
```
> [!TIP]
> `ProgramHook::empty()` 创建一个空 hook,然后链式调用 `.on_*()` 方法注册你关心的生命周期节点。没有注册的节点不会执行。
## 生命周期节点
Hook 覆盖了管线的完整生命周期:
| 阶段 | Hook | 触发时机 |
| ------------ | ------------------ | ------------- |
| **Dispatch** | `on_begin` | 执行开始 |
| | `on_pre_dispatch` | Dispatch 之前 |
| | `on_post_dispatch` | Dispatch 之后 |
| **Chain** | `on_pre_chain` | Chain 执行前 |
| | `on_post_chain` | Chain 执行后 |
| **Render** | `on_pre_render` | Render 执行前 |
| | `on_post_render` | Render 执行后 |
| **Finish** | `on_finish` | 程序退出前 |
每个 hook 回调接收对应的 `Hook*Info` 结构体,里面包含当前上下文的信息(输入类型、参数、渲染结果等)。
## 实际例子:记录操作日志
```rust
@@@use mingling::prelude::*;
@@@use mingling::hook::ProgramHook;
@@@
@@@dispatcher!("greet", CMDGreet => EntryGreet);
@@@pack!(ResultName = String);
@@@
@@@#[chain] fn handle_greet(args: EntryGreet) -> Next {
@@@ ResultName::new(args.inner.first().cloned().unwrap_or_default()).to_render()
@@@}
@@@#[renderer] fn render_name(r: ResultName) { r_println!("Hello, {}!", *r); }
fn main() {
let mut program = ThisProgram::new();
// 记录每次 chain 执行前后的信息
program.with_hook(
ProgramHook::empty()
.on_pre_chain(|info| {
eprintln!("[hook] executing chain for: {}", info.input);
})
.on_post_chain(|info| {
eprintln!("[hook] chain output: {}", info.output.member_id);
}),
);
program.with_dispatcher(CMDGreet);
program.exec_and_exit();
}
```
运行效果:
```text
[hook] executing chain for: EntryGreet
[hook] chain output: ResultName
Hello, World!
```
## 通过 Hook 控制行为
Hook 不仅用来 "看",还可以用 `ProgramControlUnit` 改变程序行为:
| 变体 | 效果 |
| -------------------------- | ----------------------------------------- |
| `Continue` | 什么都不做,继续执行 |
| `OverrideExitCode(i32)` | 覆盖退出码 |
| `RouteToChain(AnyOutput)` | 将当前数据替换为新的,重新进入 Chain 循环 |
| `RouteToRender(AnyOutput)` | 跳过后续 Chain,直接渲染 |
> [!NOTE]
> Hook 可以注册多个,按注册顺序执行。
<p align="center" style="font-size: 0.85em; color: gray;">
Written by @Weicao-CatilGrass
</p>
|