aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-06-06 23:16:51 +0800
committer魏曹先生 <1992414357@qq.com>2026-06-06 23:16:51 +0800
commit81635b93c597b10282cb14e0873f5e3d22395186 (patch)
tree200fd2632653fefe751ab5bfa6ebc7a8d4161638
parenta16d1e6f204cc83104ad3dcc4f4266e304214044 (diff)
Rename `ExitCode` to `ResExitCode` and `REPL` to `ResREPL`
-rw-r--r--CHANGELOG.md13
-rw-r--r--docs/_zh_CN/_sidebar.md2
-rw-r--r--docs/_zh_CN/pages/other/naming_rule.md200
-rw-r--r--examples/example-exitcode/src/main.rs8
-rw-r--r--examples/example-repl-basic/src/main.rs5
-rw-r--r--mingling/src/example_docs.rs13
-rw-r--r--mingling/src/res.rs3
-rw-r--r--mingling/src/res/exit_code.rs6
-rw-r--r--mingling/src/setups/exit_code.rs6
-rw-r--r--mingling_core/src/lib.rs6
-rw-r--r--mingling_core/src/program.rs5
-rw-r--r--mingling_core/src/program/repl_exec.rs10
-rw-r--r--mingling_core/src/program/repl_exec/res.rs2
13 files changed, 252 insertions, 27 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4c86be4..95db724 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -43,6 +43,19 @@ program.with_setup(BasicProgramSetup);
1. **\[core\]** Changed the signature of `ProgramSetup::setup` from `fn setup(&mut self, program: &mut Program<C>) -> S` to `fn setup(self, program: &mut Program<C>)`, consuming `self` instead of taking a mutable reference. Correspondingly, `Program::with_setup` now accepts `S` by value (`&mut self, setup: S`) instead of by mutable reference (`&mut self, setup: &mut S`).
+2. **\[core\]** Consolidated resource naming for `ExitCode` and `REPL`:
+ - Renamed `ExitCode` to `ResExitCode` and moved `ResREPL` from the `mingling` root to `mingling::res::ResREPL` (the `mingling::ResREPL` re-export is removed).
+ - This aligns with the naming convention where resources are prefixed with `Res`.
+ - The corresponding setup `ExitCodeSetup` and resource injection remain unchanged.
+
+```rust
+// Before
+use mingling::{res::ExitCode, REPL};
+
+// After
+use mingling::{res::ResExitCode, res::ResREPL};
+```
+
### Release 0.1.9 (2026-05-29)
#### Fixes:
diff --git a/docs/_zh_CN/_sidebar.md b/docs/_zh_CN/_sidebar.md
index 8c854f9..5d0ebe1 100644
--- a/docs/_zh_CN/_sidebar.md
+++ b/docs/_zh_CN/_sidebar.md
@@ -1,3 +1,5 @@
- [Welcome!](README)
* [介绍](pages/1-intro)
* [初次上手!](pages/2-step1)
+* other
+ * [命名规范](pages/other/naming_rule)
diff --git a/docs/_zh_CN/pages/other/naming_rule.md b/docs/_zh_CN/pages/other/naming_rule.md
new file mode 100644
index 0000000..59dc9cd
--- /dev/null
+++ b/docs/_zh_CN/pages/other/naming_rule.md
@@ -0,0 +1,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>
diff --git a/examples/example-exitcode/src/main.rs b/examples/example-exitcode/src/main.rs
index e1f60ee..0e7a019 100644
--- a/examples/example-exitcode/src/main.rs
+++ b/examples/example-exitcode/src/main.rs
@@ -14,7 +14,7 @@
//! No name provided (with exit code 1)
//! ```
-use mingling::{prelude::*, res::ExitCode, setup::ExitCodeSetup};
+use mingling::{prelude::*, res::ResExitCode, setup::ExitCodeSetup};
fn main() {
let mut program = ThisProgram::new();
@@ -50,11 +50,11 @@ fn render_result_name(name: ResultName) {
r_println!("Hello, {}", *name);
}
-// Define renderer, render error message _____________ Inject exit code resource
+// Define renderer, render error message _______________ Inject exit code resource
// /
/// Renders the error when no name is provided |
-#[renderer] // vvvvvvvvvvvvv
-fn render_error_no_name_provided(_: ErrorNoNameProvided, ec: &mut ExitCode) {
+#[renderer] // vvvvvvvvvvvvvvvv
+fn render_error_no_name_provided(_: ErrorNoNameProvided, ec: &mut ResExitCode) {
ec.exit_code = 1;
// Prompt when no name is provided
diff --git a/examples/example-repl-basic/src/main.rs b/examples/example-repl-basic/src/main.rs
index d44c92a..abea141 100644
--- a/examples/example-repl-basic/src/main.rs
+++ b/examples/example-repl-basic/src/main.rs
@@ -10,8 +10,9 @@
use mingling::{
hook::ProgramHook,
prelude::*,
+ res::ResREPL,
setup::{BasicREPLOutputSetup, BasicREPLPromptSetup, BasicREPLReadlineSetup},
- this, REPL,
+ this,
};
use std::{env::current_dir, path::PathBuf};
@@ -145,7 +146,7 @@ fn render_list(list: ResultList) {
#[chain]
fn handle_exit(
_prev: EntryExit,
- repl: &mut REPL, // Import REPL resource, registered in `exec_repl`, usable directly
+ repl: &mut ResREPL, // Import REPL resource, registered in `exec_repl`, usable directly
) {
// Set the REPL exit flag; REPL will exit after this loop iteration
repl.exit = true;
diff --git a/mingling/src/example_docs.rs b/mingling/src/example_docs.rs
index 903abf8..3755553 100644
--- a/mingling/src/example_docs.rs
+++ b/mingling/src/example_docs.rs
@@ -1088,7 +1088,7 @@ pub mod example_error_handling {}
///
/// Source code (./src/main.rs)
/// ```ignore
-/// use mingling::{prelude::*, res::ExitCode, setup::ExitCodeSetup};
+/// use mingling::{prelude::*, res::ResExitCode, setup::ExitCodeSetup};
///
/// fn main() {
/// let mut program = ThisProgram::new();
@@ -1124,11 +1124,11 @@ pub mod example_error_handling {}
/// r_println!("Hello, {}", *name);
/// }
///
-/// // Define renderer, render error message _____________ Inject exit code resource
+/// // Define renderer, render error message _______________ Inject exit code resource
/// // /
/// /// Renders the error when no name is provided |
-/// #[renderer] // vvvvvvvvvvvvv
-/// fn render_error_no_name_provided(_: ErrorNoNameProvided, ec: &mut ExitCode) {
+/// #[renderer] // vvvvvvvvvvvvvvvv
+/// fn render_error_no_name_provided(_: ErrorNoNameProvided, ec: &mut ResExitCode) {
/// ec.exit_code = 1;
///
/// // Prompt when no name is provided
@@ -1521,8 +1521,9 @@ pub mod example_panic_unwind {}
/// use mingling::{
/// hook::ProgramHook,
/// prelude::*,
+/// res::ResREPL,
/// setup::{BasicREPLOutputSetup, BasicREPLPromptSetup, BasicREPLReadlineSetup},
-/// this, REPL,
+/// this,
/// };
/// use std::{env::current_dir, path::PathBuf};
///
@@ -1656,7 +1657,7 @@ pub mod example_panic_unwind {}
/// #[chain]
/// fn handle_exit(
/// _prev: EntryExit,
-/// repl: &mut REPL, // Import REPL resource, registered in `exec_repl`, usable directly
+/// repl: &mut ResREPL, // Import REPL resource, registered in `exec_repl`, usable directly
/// ) {
/// // Set the REPL exit flag; REPL will exit after this loop iteration
/// repl.exit = true;
diff --git a/mingling/src/res.rs b/mingling/src/res.rs
index d625f8f..982fe91 100644
--- a/mingling/src/res.rs
+++ b/mingling/src/res.rs
@@ -1,2 +1,5 @@
mod exit_code;
pub use exit_code::*;
+
+#[allow(unused_imports)]
+pub use mingling_core::core_res::*;
diff --git a/mingling/src/res/exit_code.rs b/mingling/src/res/exit_code.rs
index e90d067..93ce98b 100644
--- a/mingling/src/res/exit_code.rs
+++ b/mingling/src/res/exit_code.rs
@@ -9,7 +9,7 @@ use mingling_core::{ProgramCollect, this};
/// The exit code is stored globally per `ProgramCollect` type and can be
/// retrieved via [`exit_code()`] or updated via [`update_exit_code()`].
#[derive(Debug, Default, Clone, Copy)]
-pub struct ExitCode {
+pub struct ResExitCode {
/// The numeric exit code value.
pub exit_code: i32,
}
@@ -19,7 +19,7 @@ pub fn update_exit_code<C>(exit_code: i32)
where
C: ProgramCollect<Enum = C> + 'static,
{
- this::<C>().modify_res(|e: &mut ExitCode| e.exit_code = exit_code);
+ this::<C>().modify_res(|e: &mut ResExitCode| e.exit_code = exit_code);
}
/// Retrieves the globally stored exit code for the given `ProgramCollect` type.
@@ -29,7 +29,7 @@ pub fn exit_code<C>() -> i32
where
C: ProgramCollect<Enum = C> + 'static,
{
- match this::<C>().res::<ExitCode>() {
+ match this::<C>().res::<ResExitCode>() {
Some(e) => e.exit_code,
None => 0,
}
diff --git a/mingling/src/setups/exit_code.rs b/mingling/src/setups/exit_code.rs
index 88742d5..ed8204c 100644
--- a/mingling/src/setups/exit_code.rs
+++ b/mingling/src/setups/exit_code.rs
@@ -2,7 +2,7 @@ use std::marker::PhantomData;
use mingling_core::{ProgramCollect, hook::ProgramHook, setup::ProgramSetup, this};
-use crate::res::ExitCode;
+use crate::res::ResExitCode;
/// Provides the ability to control the program's exit code, which is returned when the program ends.
///
@@ -29,11 +29,11 @@ where
{
fn setup(self, program: &mut crate::Program<C>) {
// Insert resource
- program.with_resource(ExitCode { exit_code: 0 });
+ program.with_resource(ResExitCode { exit_code: 0 });
// Insert hook to override exit code before program ends
program.with_hook(ProgramHook::empty().on_finish(|| {
- let this = this::<C>().res_or_default::<ExitCode>();
+ let this = this::<C>().res_or_default::<ResExitCode>();
this.exit_code
}));
}
diff --git a/mingling_core/src/lib.rs b/mingling_core/src/lib.rs
index 373df52..6e1e90c 100644
--- a/mingling_core/src/lib.rs
+++ b/mingling_core/src/lib.rs
@@ -70,3 +70,9 @@ pub use crate::comp::*;
pub mod setup {
pub use crate::program::setup::ProgramSetup;
}
+
+#[doc(hidden)]
+pub mod core_res {
+ #[cfg(feature = "repl")]
+ pub use crate::program::repl_exec::res::ResREPL;
+}
diff --git a/mingling_core/src/program.rs b/mingling_core/src/program.rs
index 2e861e4..096f292 100644
--- a/mingling_core/src/program.rs
+++ b/mingling_core/src/program.rs
@@ -25,9 +25,8 @@ pub use collection::*;
mod once_exec;
#[cfg(feature = "repl")]
-mod repl_exec;
-#[cfg(feature = "repl")]
-pub use repl_exec::res::REPL;
+#[doc(hidden)]
+pub mod repl_exec;
mod single_instance;
pub use single_instance::*;
diff --git a/mingling_core/src/program/repl_exec.rs b/mingling_core/src/program/repl_exec.rs
index 033c5a3..d6b3f00 100644
--- a/mingling_core/src/program/repl_exec.rs
+++ b/mingling_core/src/program/repl_exec.rs
@@ -11,7 +11,7 @@ mod splitter;
use crate::error::{ProgramInternalExecuteError, ProgramPanic};
use crate::program::repl_exec::splitter::split_input_string;
use crate::{Program, ProgramCollect, RenderResult};
-use crate::{program::repl_exec::res::REPL, this};
+use crate::{program::repl_exec::res::ResREPL, this};
#[cfg(not(feature = "async"))]
impl<C> Program<C>
@@ -24,7 +24,7 @@ where
/// and displays the execution result or error message. It is suitable for scenarios requiring command-line interaction with the user.
pub fn exec_repl(mut self) {
// Inject default REPL resource
- self.with_resource(REPL::default());
+ self.with_resource(ResREPL::default());
self.run_hook_repl_on_begin();
@@ -48,7 +48,7 @@ where
}
p.run_hook_repl_post_exec();
- if this::<C>().res::<REPL>().unwrap().exit {
+ if this::<C>().res::<ResREPL>().unwrap().exit {
p.run_hook_repl_exit();
break;
}
@@ -73,7 +73,7 @@ where
/// Any panics during command execution will result in an abort rather than being caught and handled gracefully.
pub async fn exec_repl(mut self) {
// Inject default REPL resource
- self.with_resource(REPL::default());
+ self.with_resource(ResREPL::default());
self.run_hook_repl_on_begin();
@@ -94,7 +94,7 @@ where
}
p.run_hook_repl_post_exec();
- if this::<C>().res::<REPL>().unwrap().exit {
+ if this::<C>().res::<ResREPL>().unwrap().exit {
p.run_hook_repl_exit();
break;
}
diff --git a/mingling_core/src/program/repl_exec/res.rs b/mingling_core/src/program/repl_exec/res.rs
index 1295652..53e82d8 100644
--- a/mingling_core/src/program/repl_exec/res.rs
+++ b/mingling_core/src/program/repl_exec/res.rs
@@ -1,6 +1,6 @@
/// Internal resource for the REPL runtime, used to control the REPL's state during execution
#[derive(Default, Clone)]
-pub struct REPL {
+pub struct ResREPL {
/// Marks whether the REPL should exit after the current loop ends
pub exit: bool,
}