命名规范
让你的 Mingling 项目类型和函数名井井有条
## 前言
好的命名是大型 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`。
```
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);
}
```
Written by @Weicao-CatilGrass