diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-08-17 03:27:46 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-08-17 03:27:46 +0800 |
| commit | c23c590330af83afb6e146bcd9b0a274b3689d22 (patch) | |
| tree | 34599cf2737ce4de22653c9ccae2d60a38a15842 /docs/_zh_CN/pages | |
| parent | 6980fbf2f9fb4c599d8dc6ff549a8b3288eb24e9 (diff) | |
refactor!: remove Node type and simplify dispatcher macro syntax
The `dispatcher!` macro no longer requires a `CMD*` dispatcher type
argument; the dispatcher struct is now generated internally as
`__Dispatcher{Pascal}`. The `Node` type, `node!` macro, and
`Dispatcher::node()` / `clone_dispatcher()` methods are removed.
Diffstat (limited to 'docs/_zh_CN/pages')
| -rw-r--r-- | docs/_zh_CN/pages/10-help.md | 4 | ||||
| -rw-r--r-- | docs/_zh_CN/pages/11-resource-system.md | 6 | ||||
| -rw-r--r-- | docs/_zh_CN/pages/13-hook.md | 2 | ||||
| -rw-r--r-- | docs/_zh_CN/pages/14-testing.md | 6 | ||||
| -rw-r--r-- | docs/_zh_CN/pages/2-define-a-dispatcher.md | 10 | ||||
| -rw-r--r-- | docs/_zh_CN/pages/3-define-a-chain.md | 8 | ||||
| -rw-r--r-- | docs/_zh_CN/pages/4-render-result.md | 2 | ||||
| -rw-r--r-- | docs/_zh_CN/pages/5-multiple-commands.md | 8 | ||||
| -rw-r--r-- | docs/_zh_CN/pages/6-argument-parse-picker.md | 24 | ||||
| -rw-r--r-- | docs/_zh_CN/pages/7-argument-parse-clap.md | 4 | ||||
| -rw-r--r-- | docs/_zh_CN/pages/9-error-handling.md | 6 | ||||
| -rw-r--r-- | docs/_zh_CN/pages/advanced/1-completion.md | 2 | ||||
| -rw-r--r-- | docs/_zh_CN/pages/advanced/2-structural-renderer.md | 4 | ||||
| -rw-r--r-- | docs/_zh_CN/pages/other/features.md | 16 | ||||
| -rw-r--r-- | docs/_zh_CN/pages/other/naming_rule.md | 28 |
15 files changed, 64 insertions, 66 deletions
diff --git a/docs/_zh_CN/pages/10-help.md b/docs/_zh_CN/pages/10-help.md index 99a81fd..6043abc 100644 --- a/docs/_zh_CN/pages/10-help.md +++ b/docs/_zh_CN/pages/10-help.md @@ -14,7 +14,7 @@ Mingling 里用 `#[help]` 宏给命令添加帮助文本。 ```rust @@@use mingling::macros::help; @@@use mingling::macros::buffer; -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); #[help(buffer)] fn help_greet(_entry: EntryGreet) { r_println!("Usage: greet [name]"); @@ -51,7 +51,7 @@ fn help_root(entry: EntryFallback) { ```rust @@@use mingling::macros::help; @@@use mingling::setup::BasicProgramSetup; -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); fn main() { let mut program = ThisProgram::new(); program.with_setup(BasicProgramSetup); diff --git a/docs/_zh_CN/pages/11-resource-system.md b/docs/_zh_CN/pages/11-resource-system.md index b127a3a..40d8cd9 100644 --- a/docs/_zh_CN/pages/11-resource-system.md +++ b/docs/_zh_CN/pages/11-resource-system.md @@ -30,7 +30,7 @@ fn main() { @@@use mingling::macros::buffer; @@@#[derive(Default, Clone)] @@@struct ResCurrentDir(String); -@@@dispatcher!("pwd", CMDPrintWorkingDir => EntryPrintWorkingDir); +@@@dispatcher!("pwd", EntryPrintWorkingDir); @@@pack!(ResultPath = String); // 通过 &T 注入只读资源 #[chain] @@ -52,7 +52,7 @@ fn render_path(result: ResultPath) { @@@use mingling::macros::buffer; @@@#[derive(Default, Clone)] @@@struct ResVisitCount(u32); -@@@dispatcher!("visit", CMDVisit => EntryVisit); +@@@dispatcher!("visit", EntryVisit); @@@pack!(ResultDone = ()); #[chain] fn handle_visit(_args: EntryVisit, counter: &mut ResVisitCount) -> Next { @@ -73,7 +73,7 @@ Chain 可以同时注入任意多个资源,框架按类型自动匹配: ```rust @@@#[derive(Default, Clone)] struct ResConfig(String); @@@#[derive(Default, Clone)] struct ResCounter(u32); -@@@dispatcher!("test", CMDTest => EntryTest); +@@@dispatcher!("test", EntryTest); @@@pack!(ResultDone = ()); // 同时注入只读 + 可修改 #[chain] diff --git a/docs/_zh_CN/pages/13-hook.md b/docs/_zh_CN/pages/13-hook.md index 2aa3ef7..811957c 100644 --- a/docs/_zh_CN/pages/13-hook.md +++ b/docs/_zh_CN/pages/13-hook.md @@ -54,7 +54,7 @@ Hook 覆盖了管线的完整生命周期: @@@use mingling::prelude::*; @@@use mingling::hook::ProgramHook; @@@ -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); @@@pack!(ResultName = String); @@@ @@@#[chain] fn handle_greet(args: EntryGreet) -> Next { diff --git a/docs/_zh_CN/pages/14-testing.md b/docs/_zh_CN/pages/14-testing.md index 3567aef..30f3fc4 100644 --- a/docs/_zh_CN/pages/14-testing.md +++ b/docs/_zh_CN/pages/14-testing.md @@ -35,7 +35,7 @@ fn test_render_name() { ```rust @@@use mingling::{assert_member_id, assert_render_result, unpack_chain_process}; -@@@dispatcher!("hello", CMDHello => EntryHello); +@@@dispatcher!("hello", EntryHello); @@@pack!(ResultName = String); @@@pack!(ErrorNoName = ()); @@@#[chain] @@ -77,7 +77,7 @@ fn test_handle_hello_with_name() { @@@use mingling::{assert_member_id, unpack_chain_process}; @@@use mingling::macros::entry; -@@@dispatcher!("hello", CMDHello => EntryHello); +@@@dispatcher!("hello", EntryHello); @@@pack!(ResultName = String); @@@#[chain] @@@fn handle_hello(args: EntryHello) -> Next { @@ -102,7 +102,7 @@ fn test_with_entry_macro() { @@@use mingling::{assert_render_result, unpack_chain_process}; @@@#[derive(Default, Clone)] @@@struct ResPrefix(String); -@@@dispatcher!("hello", CMDHello => EntryHello); +@@@dispatcher!("hello", EntryHello); @@@pack!(ResultGreeting = String); @@@ #[chain] diff --git a/docs/_zh_CN/pages/2-define-a-dispatcher.md b/docs/_zh_CN/pages/2-define-a-dispatcher.md index 4ec7c05..7ae26f1 100644 --- a/docs/_zh_CN/pages/2-define-a-dispatcher.md +++ b/docs/_zh_CN/pages/2-define-a-dispatcher.md @@ -19,13 +19,13 @@ Mingling 的管线从 Dispatcher 开始。 写法是固定的三个部分: ```rust -dispatcher!("命令路径", 分发器类型 => 入口类型); +dispatcher!("命令路径", 入口类型); ``` 看一个具体的例子: ```rust -dispatcher!("greet", CMDGreet => EntryGreet); +dispatcher!("greet", EntryGreet); ``` > [!NOTE] @@ -36,8 +36,8 @@ dispatcher!("greet", CMDGreet => EntryGreet); 如果你的程序有层级结构——比如 `remote add`、`remote rm`——只需要在命令名里加点号分隔: ```rust -dispatcher!("remote.add", CMDRemoteAdd => EntryRemoteAdd); -dispatcher!("remote.rm", CMDRemoteRm => EntryRemoteRm); +dispatcher!("remote.add", EntryRemoteAdd); +dispatcher!("remote.rm", EntryRemoteRm); ``` 用户在终端输入 `remote add` 时,Mingling 会依次匹配 `remote` 和 `add` 两个层级。 @@ -68,7 +68,7 @@ pub struct EntryGreet { // Features: ["extras"] // 省略 CMDType 和 EntryType,名字自动推导 dispatcher!("greet"); -// dispatcher!("greet", CMDGreet => EntryGreet); +// dispatcher!("greet", EntryGreet); ``` 这种写法会自动生成 `CMDGreet` 和 `EntryGreet`,效果跟显式声明完全一样。 diff --git a/docs/_zh_CN/pages/3-define-a-chain.md b/docs/_zh_CN/pages/3-define-a-chain.md index b1becc9..bf30b91 100644 --- a/docs/_zh_CN/pages/3-define-a-chain.md +++ b/docs/_zh_CN/pages/3-define-a-chain.md @@ -3,7 +3,7 @@ 使用 chain 宏声明链,并承接 Entry 输入 </p> -上一节我们声明了 `dispatcher!("greet", CMDGreet => EntryGreet)` +上一节我们声明了 `dispatcher!("greet", EntryGreet)` 现在用户输入 `greet` 时会被匹配并包装成 `EntryGreet`。 @@ -16,7 +16,7 @@ `#[chain]` 用来标记一个处理函数,格式非常直接: ```rust -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); pack!(ResultName = String); #[chain] @@ -77,7 +77,7 @@ pub struct ResultName { `EntryGreet` 的 `inner` 是一个 `Vec<String>`,你可以在 Chain 里自由地处理它: ```rust -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); @@@pack!(ResultName = String); #[chain] fn handle_greet(args: EntryGreet) -> Next { @@ -100,7 +100,7 @@ fn handle_greet(args: EntryGreet) -> Next { ```rust // 1. 声明命令 -dispatcher!("greet", CMDGreet => EntryGreet); +dispatcher!("greet", EntryGreet); // 2. 声明管线中的数据类型 pack!(ResultName = String); diff --git a/docs/_zh_CN/pages/4-render-result.md b/docs/_zh_CN/pages/4-render-result.md index 2dbb617..e3e49f5 100644 --- a/docs/_zh_CN/pages/4-render-result.md +++ b/docs/_zh_CN/pages/4-render-result.md @@ -52,7 +52,7 @@ fn render_name(name: ResultName) { use mingling::macros::buffer; // 1. 用 Dispatcher 声明命令 -dispatcher!("greet", CMDGreet => EntryGreet); +dispatcher!("greet", EntryGreet); // 2. 用 pack! 声明结果数据 pack!(ResultName = String); diff --git a/docs/_zh_CN/pages/5-multiple-commands.md b/docs/_zh_CN/pages/5-multiple-commands.md index b251232..e0a58e7 100644 --- a/docs/_zh_CN/pages/5-multiple-commands.md +++ b/docs/_zh_CN/pages/5-multiple-commands.md @@ -12,8 +12,8 @@ ```rust @@@use mingling::macros::buffer; // 声明两个命令 -dispatcher!("greet", CMDGreet => EntryGreet); -dispatcher!("add", CMDAdd => EntryAdd); +dispatcher!("greet", EntryGreet); +dispatcher!("add", EntryAdd); pack!(ResultGreeting = String); pack!(ResultSum = i32); @@ -62,8 +62,8 @@ Sum: 6 多层级的命令也是同理——每个点号分隔的层级都只是名字的一部分: ```rust -dispatcher!("remote.add", CMDRemoteAdd => EntryRemoteAdd); -dispatcher!("remote.rm", CMDRemoteRm => EntryRemoteRm); +dispatcher!("remote.add", EntryRemoteAdd); +dispatcher!("remote.rm", EntryRemoteRm); ``` 每个子命令的 Entry、Chain、Renderer 完全独立,互不干扰。 diff --git a/docs/_zh_CN/pages/6-argument-parse-picker.md b/docs/_zh_CN/pages/6-argument-parse-picker.md index 462a912..33f1f0d 100644 --- a/docs/_zh_CN/pages/6-argument-parse-picker.md +++ b/docs/_zh_CN/pages/6-argument-parse-picker.md @@ -26,7 +26,7 @@ features = ["parser"] ```rust // Features: ["parser"] -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); @@@pack!(ResultName = String); #[chain] @@ -42,7 +42,7 @@ fn handle_greet_entry(prev: EntryGreet) -> Next { ```rust // Features: ["parser"] -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); @@@pack!(ResultName = String); @@@#[chain] @@@fn handle_greet_entry(prev: EntryGreet) -> Next { @@ -55,7 +55,7 @@ let name = prev.pick_or((), "World").unpack(); ```rust // Features: ["parser"] -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); @@@pack!(ResultName = String); @@@#[chain] @@@fn handle_greet_entry(prev: EntryGreet) { @@ -76,7 +76,7 @@ let name = prev.pick_or((), "World").unpack(); ```rust // Features: ["parser"] -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); @@@pack!(ResultName = String); #[chain] @@ -90,7 +90,7 @@ fn handle_greet_entry(prev: EntryGreet) -> Next { ```rust // Features: ["parser"] -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); @@@pack!(ResultName = String); @@@#[chain] @@@fn handle_greet_entry(prev: EntryGreet) { @@ -113,7 +113,7 @@ fn handle_greet_entry(prev: EntryGreet) -> Next { ```rust // Features: ["parser"] -@@@dispatcher!("test", CMDTest => EntryTest); +@@@dispatcher!("test", EntryTest); @@@pack!(ResultInfo = (String, u8, u32)); #[chain] @@ -141,7 +141,7 @@ fn handle_test_entry(prev: EntryTest) -> Next { // Features: ["parser", "extras"] @@@use mingling::macros::buffer; @@@use mingling::macros::route; -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); @@@pack!(ResultName = String); @@@pack!(ErrorNoName = ()); @@ -200,7 +200,7 @@ let name = match pick_result { ````rust // Features: ["parser"] -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); @@@pack!(ResultName = String); #[chain] @@ -226,7 +226,7 @@ fn handle_greet_entry(prev: EntryGreet) -> Next { // Features: ["parser", "extras"] @@@use mingling::macros::buffer; @@@use mingling::macros::route; -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); @@@pack!(ResultName = String); @@@pack!(ErrorNameTooLong = usize); @@ -276,7 +276,7 @@ fn render_name(prev: ResultName) { ```rust // Features: ["parser"] @@@use mingling::parser::Yes; -@@@dispatcher!("test", CMDTest => EntryTest); +@@@dispatcher!("test", EntryTest); @@@pack!(ResultDone = ()); #[chain] @@ -329,7 +329,7 @@ impl Pickable for Address { Some(Address { ip, port }) } } -@@@dispatcher!("connect", CMDConnect => EntryConnect); +@@@dispatcher!("connect", EntryConnect); @@@pack!(ResultConnected = Address); #[chain] @@ -369,7 +369,7 @@ pub enum Fruits { } impl PickableEnum for Fruits {} -@@@dispatcher!("eat", CMDEat => EntryEat); +@@@dispatcher!("eat", EntryEat); @@@pack!(ResultFruit = Fruits); #[chain] diff --git a/docs/_zh_CN/pages/7-argument-parse-clap.md b/docs/_zh_CN/pages/7-argument-parse-clap.md index 83dddc3..e9475ae 100644 --- a/docs/_zh_CN/pages/7-argument-parse-clap.md +++ b/docs/_zh_CN/pages/7-argument-parse-clap.md @@ -27,7 +27,7 @@ features = ["derive", "color"] @@@ use mingling::macros::dispatcher_clap; @@@ use mingling::macros::buffer; #[derive(Default, clap::Parser, Grouped)] -#[dispatcher_clap("greet", CMDGreet, help = true, error = ErrorGreetParsed)] +#[dispatcher_clap("greet", help = true, error = ErrorGreetParsed)] pub struct EntryGreet { #[clap(default_value = "World")] name: String, @@ -63,7 +63,7 @@ fn render_greet_parse_failed(err: ErrorGreetParsed) { @@@use mingling::setup::BasicProgramSetup; @@@use mingling::macros::dispatcher_clap; @@@#[derive(Default, clap::Parser, Grouped)] -@@@#[dispatcher_clap("greet", CMDGreet)] +@@@#[dispatcher_clap("greet", )] @@@pub struct EntryGreet { @@@ name: String, @@@} diff --git a/docs/_zh_CN/pages/9-error-handling.md b/docs/_zh_CN/pages/9-error-handling.md index 222ef44..dce48d1 100644 --- a/docs/_zh_CN/pages/9-error-handling.md +++ b/docs/_zh_CN/pages/9-error-handling.md @@ -19,7 +19,7 @@ ## 用独立类型区分错误 ```rust -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); pack!(ResultGreeting = String); pack!(ErrorNameEmpty = String); @@ -39,7 +39,7 @@ fn handle_greet(args: EntryGreet) -> Next { ```rust @@@use mingling::macros::buffer; -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); @@@pack!(ResultGreeting = String); @@@pack!(ErrorNameEmpty = String); @@@#[chain] fn handle_greet(args: EntryGreet) -> Next { ResultGreeting::new(args.inner.first().cloned().unwrap_or_default()).to_render() } @@ -61,7 +61,7 @@ fn render_error_name_empty(err: ErrorNameEmpty) { ```rust @@@use mingling::macros::buffer; -dispatcher!("greet", CMDGreet => EntryGreet); +dispatcher!("greet", EntryGreet); pack!(ResultGreeting = String); pack!(ErrorNameEmpty = String); diff --git a/docs/_zh_CN/pages/advanced/1-completion.md b/docs/_zh_CN/pages/advanced/1-completion.md index c7dd678..28587b3 100644 --- a/docs/_zh_CN/pages/advanced/1-completion.md +++ b/docs/_zh_CN/pages/advanced/1-completion.md @@ -40,7 +40,7 @@ features = [ @@@use mingling::prelude::*; @@@use mingling::{ShellContext, Suggest, SuggestItem}; @@@use std::collections::BTreeSet; -@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@dispatcher!("greet", EntryGreet); #[completion(EntryGreet)] fn complete_greet(ctx: &ShellContext) -> Suggest { diff --git a/docs/_zh_CN/pages/advanced/2-structural-renderer.md b/docs/_zh_CN/pages/advanced/2-structural-renderer.md index 9723247..f2e1717 100644 --- a/docs/_zh_CN/pages/advanced/2-structural-renderer.md +++ b/docs/_zh_CN/pages/advanced/2-structural-renderer.md @@ -29,7 +29,7 @@ features = ["structural_renderer"] // serde = "1" @@@use mingling::macros::buffer; @@@use mingling::setup::StructuralRendererSetup; -@@@dispatcher!("render", CMDRender => EntryRender); +@@@dispatcher!("render", EntryRender); // pack_structural! 等价于 pack! + StructuralData pack_structural!(ResultInfo = (String, i32)); @@ -72,7 +72,7 @@ fn render_info(r: ResultInfo) { @@@use mingling::setup::StructuralRendererSetup; @@@use mingling::StructuralData; @@@use serde::Serialize; -@@@dispatcher!("render", CMDRender => EntryRender); +@@@dispatcher!("render", EntryRender); #[derive(Serialize, StructuralData, Grouped)] struct Info { diff --git a/docs/_zh_CN/pages/other/features.md b/docs/_zh_CN/pages/other/features.md index 7ed3f5f..4913945 100644 --- a/docs/_zh_CN/pages/other/features.md +++ b/docs/_zh_CN/pages/other/features.md @@ -151,14 +151,14 @@ build_comp_scripts("myprogram").unwrap(); 例如,允许 `dispatcher!("greet")` 的缩写形式,自动生成 `CMDGreet` / `EntryGreet`。 -| 宏 | 说明 | -| ------------------------------------------------------- | --------------------------------------------- | -| `empty_result!()` | 链中提前返回空结果的简写 | -| `entry!(Type, ["a", "b"])` | 构造入口类型的测试数据 | -| `group!(Type)` | 将外部类型注册为组成员,无需修改其定义 | -| `pack_err!(ErrorType)` / `pack_err!(ErrorType = Inner)` | 创建带自动 `name` 字段的错误类型 | -| `#[program_setup]` | 声明程序初始化函数 | -| `dispatcher!("cmd.path")` **缩写形式** | 省略 `CMDStruct => EntryStruct`,名字自动推导 | +| 宏 | 说明 | +| ------------------------------------------------------- | -------------------------------------- | +| `empty_result!()` | 链中提前返回空结果的简写 | +| `entry!(Type, ["a", "b"])` | 构造入口类型的测试数据 | +| `group!(Type)` | 将外部类型注册为组成员,无需修改其定义 | +| `pack_err!(ErrorType)` / `pack_err!(ErrorType = Inner)` | 创建带自动 `name` 字段的错误类型 | +| `#[program_setup]` | 声明程序初始化函数 | +| `dispatcher!("cmd.path")` **缩写形式** | 省略 `EntryStruct`,入口类型名自动推导 | <details> <summary> Details </summary> diff --git a/docs/_zh_CN/pages/other/naming_rule.md b/docs/_zh_CN/pages/other/naming_rule.md index 6e65ee4..1bca8f6 100644 --- a/docs/_zh_CN/pages/other/naming_rule.md +++ b/docs/_zh_CN/pages/other/naming_rule.md @@ -40,19 +40,17 @@ Res + 名称 ### 分发器 -分发器是命令的入口点,与 `Node` 名称一一对应。节点名用 `.` 分隔层级,分发器名用 `CMD` 前缀加 PascalCase。 +分发器是命令的入口点。命令名用 `.` 分隔层级,与用户输入的命令参数一一对应。 ``` -CMD + 命令层级 +命令名 ``` -| 节点 | 分发器 | -| ------------ | ----------------- | -| `greet` | `CMDGreet` | -| `remote.add` | `CMDRemoteAdd` | -| `remote.rm` | `CMDRemoteRemove` | - -即使节点是缩写,分发器的名称也要写全名。例如节点是 `remote.rm`,分发器是 `CMDRemoteRemove`,不是 `CMDRemoteRm`。 +| 命令 | +| ------------ | +| `greet` | +| `remote.add` | +| `remote.rm` | ### 入口 @@ -62,11 +60,11 @@ CMD + 命令层级 Entry + 命令层级 ``` -| 分发器 | 入口 | -| ----------------- | ------------------- | -| `CMDGreet` | `EntryGreet` | -| `CMDRemoteAdd` | `EntryRemoteAdd` | -| `CMDRemoteRemove` | `EntryRemoteRemove` | +| 命令 | 入口 | +| ------------ | ------------------- | +| `greet` | `EntryGreet` | +| `remote.add` | `EntryRemoteAdd` | +| `remote.rm` | `EntryRemoteRemove` | ### 状态 @@ -174,7 +172,7 @@ fn handle_remote_add(args: EntryRemoteAdd, cwd: &ResCurrentDir, db: &mut ResData @@@ pack!(ResultRemoteAdded = String); @@@ pack!(ErrorRepositoryNotFound = String); // 分发器 -dispatcher!("remote.add", CMDRemoteAdd => EntryRemoteAdd); +dispatcher!("remote.add", EntryRemoteAdd); // 入口 → 状态 #[chain] |
