blob: f5e6b8f28ad6510f8b235da671025fc84d6bfeec (
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
|
<h1 align="center">关于 ProgramCollect</h1>
<p align="center">
了解 gen_program!() 是如何生成程序的
</p>
每个 Mingling 程序最后都有一行 `gen_program!()`。它在背后做了三件事,把整个程序的骨架搭建出来。
## gen_program!() 的三件事
### 1. 生成枚举
扫描当前模块中所有 `pack!`、`#[chain]`、`#[renderer]` 等宏标记的类型,为每个类型生成一个枚举变体。
这个枚举就是 `AnyOutput<G>` 中 `G` 的类型 —— 调度器靠枚举变体来区分管线中传递的不同数据。
### 2. 生成 ProgramCollect 实现
`ProgramCollect` 是一个 trait,定义了 **"每个枚举变体对应什么类型、由谁处理"** 的映射关系:
- **`do_chain`** —— 根据 `member_id` 调用对应的 `#[chain]` 函数,返回新的 `AnyOutput` 和 `NextProcess`
- **`render`** —— 根据 `member_id` 调用对应的 `#[renderer]` 函数,写入 `RenderResult`
- **`render_help`** —— 根据 `member_id` 调用对应的 `#[help]` 函数
- **`has_chain` / `has_renderer`** —— 判断某个变体有没有对应的处理函数
- **`build_dispatcher_not_found` / `build_renderer_not_found` / `build_empty_result`** —— 三个内置降级类型,处理边界情况
这套映射在运行时通过枚举匹配来完成——编译期只生成了枚举和匹配分支,实际的函数调用发生在运行时。
### 3. 生成 ThisProgram
生成 `ThisProgram` 类型别名,指向 `Program<生成的枚举>`。这就是为什么在 `main` 中可以直接写 `ThisProgram::new()`——它就是你整个程序的完整类型。
---
## 关于 `pathf` 和 `dispatch_tree` 下的差异
以上是默认行为,但在启用特定 feature 时会有变化:
### 1. `dispatch_tree` 特性
Dispatcher 的匹配不再使用 `Vec<Box<dyn Dispatcher>>` 做线性匹配,而是在编译期将子命令结构构建为前缀树(Trie)。
匹配复杂度从 `O(n)` 降到 `O(k)` —— `k` 是输入长度,与命令数量无关。
### 2. `pathf` 特性(Module Pathfinder)
默认情况下所有宏标记的类型必须在同一模块才能被 `gen_program!()` 收集到
启用 `pathf` 后,编译期自动扫描所有子模块,找到所有用宏标记的类型并生成完整的模块路径引用 —— 类型定义在深层子模块也无需手动 `use`。
<p align="center" style="font-size: 0.85em; color: gray;">
Written by @Weicao-CatilGrass
</p>
|