aboutsummaryrefslogtreecommitdiff
path: root/docs/_zh_CN/pages/other/naming_rule.md
blob: 59dc9cd998962cae146a2f8552ec520ddad673e2 (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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
<h1 align="center">命名规范</h1>
<p align="center">
    让你的 <b>Mingling</b> 项目类型和函数名井井有条
</p>

## 前言

好的命名是大型 CLI 项目的基石。Mingling 的管线模式引入了几类新的类型(Entry、State、Result、Error、Resource),如果不加约束,项目里很快就会出现 `Data``Info``Context` 这类无法区分职责的名字。

下面这套命名规范来自实践,已在生产项目中使用。它不是框架的强制规则,但建议跟随。

## 类型命名

### 资源

资源是通过资源注入系统共享给所有 chain 和 renderer 的全局实例。

```
Res + 名称
```
 
| 示例 | 说明 |
|------|------|
| `ResCurrentDir` | 当前工作目录 |
| `ResExitCode` | 进程退出码 |
| `ResREPL` | REPL 状态 |

### 构建

构建是在程序启动时执行的初始化步骤,由 `with_setup` 注册。

```
名称 + Setup
```
 
| 示例 | 说明 |
|------|------|
| `BasicSetup` | 基础初始化(`--quiet`、`--help`、`--confirm`) |
| `GeneralRendererSetup` | 通用渲染器初始化(`--json`、`--yaml` 等) |

### 分发器

分发器是命令的入口点,与 `Node` 名称一一对应。节点名用 `.` 分隔层级,分发器名用 `CMD` 前缀加 PascalCase。

```
CMD + 命令层级
```
 
| 节点 | 分发器 |
|------|--------|
| `greet` | `CMDGreet` |
| `remote.add` | `CMDRemoteAdd` |
| `remote.rm` | `CMDRemoteRemove` |

即使节点是缩写,分发器的名称也要写全名。例如节点是 `remote.rm`,分发器是 `CMDRemoteRemove`,不是 `CMDRemoteRm`### 入口

入口是分发器创建的管线起始类型,包裹 `Vec<String>````
Entry + 命令层级
```
 
| 分发器 | 入口 |
|--------|------|
| `CMDGreet` | `EntryGreet` |
| `CMDRemoteAdd` | `EntryRemoteAdd` |
| `CMDRemoteRemove` | `EntryRemoteRemove` |

### 状态

状态是介于入口和结果之间的管线中间类型,代表经过部分处理后的数据。

```
State + 描述
```
 
| 示例 | 说明 |
|------|------|
| `StateOperationRemotes` | 正在操作远程仓库列表 |
| `StateCheckRepository` | 正在检查仓库状态 |

典型的管线链:`EntryRemoteAdd → StateOperationRemotes → StateCheckRepository`

### 结果

结果是管线中由 `to_render()` 发往 Renderer 的最终类型。

```
Result + 内容
```
 
| 示例 | 说明 |
|------|------|
| `ResultGreetSomeone` | 问候结果 |
| `ResultFruitList` | 水果列表结果 |

结果结构体期望被 Renderer 消费,内部结构应该为了渲染美观而设计。一般用 `#[derive(Groupped)]` 代替 `pack!()` 包装,以获得更灵活的字段控制。

### 错误

错误与结果不同。错误可以被 `to_chain()``to_render()` 发送,用于将执行路由到错误处理路径。

```
Error + 描述
```
 
| 示例 | 说明 |
|------|------|
| `ErrorRepositoryNotFound` | 仓库未找到 |

`StateOperationRemotes → ErrorRepositoryNotFound`



## 函数命名

### Chain 函数

| 处理类型 | 命名模式 | 示例 |
|----------|----------|------|
| Entry | `handle_` + entry 名(snake_case) | `handle_remote_add(prev: EntryRemoteAdd)` |
| State | `handle_state_` + state 名(snake_case) | `handle_state_operation_remotes(prev: StateOperationRemotes)` |
| Error | `handle_error_` + error 名(snake_case) | `handle_error_repository_not_found(prev: ErrorRepositoryNotFound)` |
| Result | ❌ 不要为 Result 写 chain | — |

### Renderer 函数

| 处理类型 | 命名模式 | 示例 |
|----------|----------|------|
| Entry | `render_entry_` + entry 名 | `render_entry_remote_add(prev: EntryRemoteAdd)` |
| State | ❌ 不要为 State 写 renderer | — |
| Error | `render_error_` + error 名 | `render_error_repository_not_found(prev: ErrorRepositoryNotFound)` |
| Result | `render_` + result 名 | `render_greet_someone(prev: ResultGreetSomeone)` |

**原则**:不要为不会被用户看到或不需要独立渲染的中间类型写 renderer。

---

## 函数签名参数命名

| 类型 | 推荐参数名 |
|------|------------|
| Entry | `args` |
| State | `prev`(或具名如 `remotes`) |
| Result | `result`(或具名如 `fruits`) |
| Error | `err` |
| 资源(不可变) | `cwd``db``config` 等 |
| 资源(可变) | `counter``cache``session` 等 |

```rust
#[chain]
fn handle_remote_add(args: EntryRemoteAdd, cwd: &ResCurrentDir, db: &mut ResDatabase) -> Next {
    // args: 入口数据
    // cwd:  注入的不可变资源
    // db:   注入的可变资源
}
```
 
---

## 完整示例

```rust
// 分发器
dispatcher!("remote.add", CMDRemoteAdd => EntryRemoteAdd);
 
// 入口 → 状态
#[chain]
fn handle_remote_add(args: EntryRemoteAdd) -> Next {
    StateOperationRemotes::new(...).to_chain()
}
 
// 状态 → 错误或结果
#[chain]
fn handle_state_operation_remotes(state: StateOperationRemotes, db: &ResDatabase) -> Next {
    if db.has_remote(&state.name) {
        ErrorRepositoryNotFound::new(...).to_render()
    } else {
        ResultRemoteAdded::new(...).to_render()
    }
}
 
// 结果渲染
#[renderer]
fn render_remote_added(result: ResultRemoteAdded) {
    r_println!("Remote added: {}", result.name);
}
 
// 错误渲染
#[renderer]
fn render_error_repository_not_found(err: ErrorRepositoryNotFound) {
    r_println!("Error: remote '{}' not found", err.name);
}
```
 
<p align="center" style="font-size: 0.85em; color: gray;">
    Written by @Weicao-CatilGrass
</p>