diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-03-14 18:33:20 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-03-14 18:33:20 +0800 |
| commit | 17e7b28f162b3ed75683948144237ee17f81f7a5 (patch) | |
| tree | 9af1e089b517ba73f601b2ec9eacffbf0f6d2485 /src/cmds/README_zh_CN.md | |
| parent | 707a1f7c747a08e2ce77df88edc1e72eae9cbebc (diff) | |
Add converter module and update documentation
Diffstat (limited to 'src/cmds/README_zh_CN.md')
| -rw-r--r-- | src/cmds/README_zh_CN.md | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/src/cmds/README_zh_CN.md b/src/cmds/README_zh_CN.md new file mode 100644 index 0000000..42823a8 --- /dev/null +++ b/src/cmds/README_zh_CN.md @@ -0,0 +1,266 @@ +# 命令开发指南 + +本文档详细介绍了如何在 JVCS CLI 中开发新的命令。命令系统采用模块化设计,分为多个组件,每个组件有明确的职责。 + +## 目录结构 + +``` +src/cmds/ +├── arg/ # 命令行参数定义 +├── cmd/ # 命令实现 +├── collect/ # 资源收集信息 +├── converter/ # 数据转换器 +├── in/ # 输入数据结构 +├── out/ # 输出数据结构 +├── override/ # 渲染器重写 +└── renderer/ # 渲染器实现 +``` + +## 命令组件 + +### 1. Argument +- 实现 `clap::Parser` trait +- 命名规范:`JV{CommandName}Argument` +- 位置:`src/cmds/arg/{command_name}.rs` + +示例:sum 命令的参数定义 +```rust +// src/cmds/arg/sum.rs +use clap::Parser; + +#[derive(Parser, Debug)] +pub struct JVSumArgument { + /// 要相加的数字 + pub numbers: Vec<i32>, + + /// 不输出结果 + #[arg(long)] + pub no_output: bool, +} +``` + +### 2. Input +- 无生命周期的结构体 +- 命名规范:`JV{CommandName}Input` +- 位置:`src/cmds/in/{command_name}.rs` +- 在 `prepare` 阶段由 `Argument` 转换而来 + +示例:sum 命令的输入结构 +```rust +// src/cmds/in/sum.rs +pub struct JVSumInput { + pub numbers: Vec<i32>, + pub should_output: bool, +} +``` + +### 3. Collect +- 无生命周期的结构体 +- 命名规范:`JV{CommandName}Collect` +- 位置:`src/cmds/collect/{command_name}.rs` +- 用于收集执行命令所需的本地资源信息 + +示例:sum 命令的资源收集 +```rust +// src/cmds/collect/sum.rs +pub struct JVSumCollect { + pub count: usize, +} +``` + +### 4. Output +- 实现 `serde::Serialize` trait +- 命名规范:`JV{CommandName}Output` +- 位置:`src/cmds/out/{command_name}.rs` + +示例:sum 命令的输出结构 +```rust +// src/cmds/out/sum.rs +use serde::Serialize; + +#[derive(Serialize)] +pub struct JVSumOutput { + pub result: i32, +} +``` + +## 命令执行阶段 + +### 1. prepare 阶段 +- 将 `Argument` 转换为稳定的 `Input` 信息 +- 检测输入格式错误并提前失败 +- 对输入进行格式化处理(如标志取反) + +示例:sum 命令的 prepare 函数 +```rust +async fn prepare(args: &JVSumArgument, ctx: &JVCommandContext) -> Result<JVSumInput, CmdPrepareError> { + trace!("开始准备 sum 命令,参数数量: {}", args.numbers.len()); + debug!("no_output: {}, should_output: {}", args.no_output, should_output); + + Ok(JVSumInput { + numbers: args.numbers.clone(), + should_output = !args.no_output, + }) +} +``` + +### 2. collect 阶段 +- 根据 `Argument` 的信息读取需要用到的资源 +- 资源加载错误时提前失败 +- 将收集的资源信息传入 `exec` 阶段 + +示例:sum 命令的 collect 函数 +```rust +async fn collect(args: &JVSumArgument, ctx: &JVCommandContext) -> Result<JVSumCollect, CmdPrepareError> { + trace!("收集 sum 命令资源"); + + Ok(JVSumCollect { + count: args.numbers.len(), + }) +} +``` + +### 3. exec 阶段 +- 将 `prepare` 和 `collect` 阶段收集的信息绑定到核心 API +- 将结果整理为 `Output` 输出 +- **必须使用** `cmd_output!(JVSomeOutput => output)` 语法实现输出 + +示例:sum 命令的 exec 函数 +```rust +#[exec] +async fn exec( + input: JVSumInput, + collect: JVSumCollect, +) -> Result<(Box<dyn std::any::Any + Send + 'static>, TypeId), CmdExecuteError> { + trace!("执行 sum 命令,处理 {} 个数字", collect.count); + + // 计算总和 + let result = input.numbers.iter().sum(); + debug!("计算结果: {}", result); + + // 根据 should_output 决定输出类型 + if input.should_output { + cmd_output!(JVSumOutput => JVSumOutput { result }) + } else { + // 使用 JVNoneOutput 表示不输出结果 + cmd_output!(JVNoneOutput => JVNoneOutput) + } +} +``` + +## 渲染器 + +每个 `Output` 需要对应一个渲染器,用于将输出数据渲染为用户可读的格式。 + +### 渲染器实现要求 +- 实现异步的 `render` 函数 +- 输入为对应的 `Output` 值 +- 输出为 `Result<JVRenderResult, CmdRenderError>` +- **必须使用** `#[result_renderer(JV{CommandName}Renderer)]` 宏 + +示例:sum 命令的渲染器 +```rust +// src/cmds/renderer/sum.rs +use render_system_macros::result_renderer; + +use crate::{ + cmds::out::sum::JVSumOutput, + r_println, + systems::{cmd::errors::CmdRenderError, render::renderer::JVRenderResult}, +}; + +#[result_renderer(JVSumRenderer)] +pub async fn render(data: &JVSumOutput) -> Result<JVRenderResult, CmdRenderError> { + trace!("渲染 sum 命令结果"); + + let mut r = JVRenderResult::default(); + r_println!(r, "Result: {}", data.result); + Ok(r) +} +``` + +## 开发流程 + +1. **规划命令结构** + - 确定命令名称和参数 + - 设计输入/输出数据结构 + +2. **创建组件文件** + - 在相应目录创建 `.rs` 文件 + - 实现 Argument、Input、Collect、Output 结构体 + +3. **实现命令逻辑** + - 在 `cmd/` 目录创建命令实现文件 + - 使用命令模板(通过 `cargo doc --no-deps` 生成文档查看完整模板) + - 实现 `prepare`、`collect`、`exec` 函数 + +4. **实现渲染器** + - 在 `renderer/` 目录创建渲染器文件 + - 使用 `#[result_renderer]` 宏 + +5. **测试命令** + - 使用 `cargo build` 检查编译错误 + - 运行命令测试功能 + +## 命名规范 + +| 组件类型 | 命名规范 | 示例 | +|---------|---------|------| +| 命令 | `JV{CommandName}Command` | `JVSumCommand` | +| 参数 | `JV{CommandName}Argument` | `JVSumArgument` | +| 输入 | `JV{CommandName}Input` | `JVSumInput` | +| 收集 | `JV{CommandName}Collect` | `JVSumCollect` | +| 输出 | `JV{CommandName}Output` | `JVSumOutput` | +| 渲染器 | `JV{CommandName}Renderer` | `JVSumRenderer` | + +## 日志输出 + +在命令开发中,可以使用 `log` 进行调试: + +- `trace!("消息")` - 最详细的调试信息 +- `debug!("消息")` - 调试信息 +- `info!("消息")` - 一般信息 +- `warn!("消息")` - 警告信息 +- `error!("消息")` - 错误信息 + +## 最佳实践 + +1. **错误处理** + - 在 `prepare` 阶段验证输入 + - 在 `collect` 阶段检查资源可用性 + +2. **输入格式化** + - 在 `prepare` 阶段对用户输入进行标准化 + - 确保 `Input` 结构是干净、稳定的 + +3. **资源管理** + - 在 `collect` 阶段获取所有需要的资源 + - 避免在 `exec` 阶段进行文件系统操作 + +4. **输出设计** + - 输出结构应包含足够的信息供渲染器使用 + - 考虑不同输出格式的需求 + +## 示例命令参考 + +查看现有命令实现以获取更多灵感: +- `helpdoc` 命令:`src/cmds/cmd/helpdoc.rs` +- `sheetdump` 命令:`src/cmds/cmd/sheetdump.rs` +- `workspace` 命令:`src/cmds/cmd/workspace.rs` + +## 调试与测试 + +1. **生成文档查看模板** + ```bash + cargo doc --no-deps + ``` + 文档生成在 `.temp/target/doc/` 目录,查看 `macro.command_template.html` 获取完整命令模板。 + +2. **运行命令测试** + ```bash + # 构建并部署 + ./scripts/dev/dev_deploy.sh + # 或 Windows + .\scripts\dev\dev_deploy.ps1 + + jvn sum 1 2 |
