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">Naming Conventions</h1>
<p align="center">
Keep your <b>Mingling</b> project types and function names organized
</p>
## Preface
Good naming is the foundation of large CLI projects. Mingling's pipeline pattern introduces several new types (Entry, State, Result, Error, Resource). Without consistent conventions, projects quickly end up with generic names like `Data`, `Info`, or `Context` that make it impossible to tell different responsibilities apart.
The naming conventions below come from real-world usage in production projects. They are not enforced by the framework, but following them is highly recommended.
## Type Naming
### Resource
Resources are global instances shared with all chains and renderers through the resource injection system.
```
Res + Name
```
| Example | Description |
| --------------- | ------------------------- |
| `ResCurrentDir` | Current working directory |
| `ResExitCode` | Process exit code |
| `ResREPL` | REPL state |
### Setup
Setups are initialization steps executed at program startup, registered via `with_setup`.
```
Name + Setup
```
| Example | Description |
| ---------------------- | ---------------------------------------------------------- |
| `BasicSetup` | Basic initialization (`--quiet`, `--help`, `--confirm`) |
| `StructuralRendererSetup` | structural renderer initialization (`--json`, `--yaml`, etc.) |
### Dispatcher
Dispatchers are the entry points of commands, corresponding one-to-one with `Node` names. Node names use `.` to separate levels, dispatcher names use the `CMD` prefix with PascalCase.
```
CMD + Command Hierarchy
```
| Node | Dispatcher |
| ------------ | ----------------- |
| `greet` | `CMDGreet` |
| `remote.add` | `CMDRemoteAdd` |
| `remote.rm` | `CMDRemoteRemove` |
Even if a node is an abbreviation, the dispatcher name should use the full name. For example, the node is `remote.rm`, but the dispatcher is `CMDRemoteRemove`, not `CMDRemoteRm`.
### Entry
Entries are the pipeline starting types created by dispatchers, wrapping `Vec<String>`.
```
Entry + Command Hierarchy
```
| Dispatcher | Entry |
| ----------------- | ------------------- |
| `CMDGreet` | `EntryGreet` |
| `CMDRemoteAdd` | `EntryRemoteAdd` |
| `CMDRemoteRemove` | `EntryRemoteRemove` |
### State
States are intermediate pipeline types between Entry and Result, representing data after partial processing.
```
State + Description
```
| Example | Description |
| ----------------------- | ----------------------------- |
| `StateOperationRemotes` | Operating on remote repo list |
| `StateCheckRepository` | Checking repository status |
Typical pipeline chain: `EntryRemoteAdd → StateOperationRemotes → StateCheckRepository`
### Result
Results are the final types sent to the Renderer via `to_render()` in the pipeline.
```
Result + Content
```
| Example | Description |
| -------------------- | ----------------- |
| `ResultGreetSomeone` | Greeting result |
| `ResultFruitList` | Fruit list result |
Result structs are expected to be consumed by the Renderer, and their internal structure should be designed for rendering aesthetics. Generally use `#[derive(Groupped)]` instead of `pack!()` wrapping for more flexible field control.
### Error
Errors are different from Results. Errors can be sent via `to_chain()` or `to_render()` to route execution to the error handling path.
```
Error + Description
```
| Example | Description |
| ------------------------- | -------------------- |
| `ErrorRepositoryNotFound` | Repository not found |
`StateOperationRemotes → ErrorRepositoryNotFound`
## Function Naming
### Chain Functions
| Processing Type | Naming Pattern | Example |
| --------------- | ----------------------------------------- | ------------------------------------------------------------------ |
| Entry | `handle_` + entry name (snake_case) | `handle_remote_add(prev: EntryRemoteAdd)` |
| State | `handle_state_` + state name (snake_case) | `handle_state_operation_remotes(prev: StateOperationRemotes)` |
| Error | `handle_error_` + error name (snake_case) | `handle_error_repository_not_found(prev: ErrorRepositoryNotFound)` |
| Result | ❌ Do not write chains for Result | — |
### Renderer Functions
| Processing Type | Naming Pattern | Example |
| --------------- | ----------------------------------- | ------------------------------------------------------------------ |
| Entry | `render_entry_` + entry name | `render_entry_remote_add(prev: EntryRemoteAdd)` |
| State | ❌ Do not write renderers for State | — |
| Error | `render_error_` + error name | `render_error_repository_not_found(prev: ErrorRepositoryNotFound)` |
| Result | `render_` + result name | `render_greet_someone(prev: ResultGreetSomeone)` |
**Principle**: Do not write renderers for intermediate types that users won't see or that don't need independent rendering.
---
## Function Signature Parameter Naming
| Type | Recommended Parameter Name |
| -------------------- | --------------------------------------- |
| Entry | `args` |
| State | `prev` (or descriptive like `remotes`) |
| Result | `result` (or descriptive like `fruits`) |
| Error | `err` |
| Resource (immutable) | `cwd`, `db`, `config`, etc. |
| Resource (mutable) | `counter`, `cache`, `session`, etc. |
```rust
// NOT VERIFIED
#[chain]
fn handle_remote_add(args: EntryRemoteAdd, cwd: &ResCurrentDir, db: &mut ResDatabase) -> Next {
// args: entry data
// cwd: injected immutable resource
// db: injected mutable resource
}
```
---
## Complete Example
```rust
// NOT VERIFIED
// Dispatcher
dispatcher!("remote.add", CMDRemoteAdd => EntryRemoteAdd);
// Entry → State
#[chain]
fn handle_remote_add(args: EntryRemoteAdd) -> Next {
StateOperationRemotes::new(...).to_chain()
}
// State → Error or Result
#[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()
}
}
// Result rendering
#[renderer]
fn render_remote_added(result: ResultRemoteAdded) {
r_println!("Remote added: {}", result.name);
}
// Error rendering
#[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>
|