创建组件 (create-cmpnnt)
用于在文学化 org-babel 风格项目中创建新组件(cmpnnt)——命令、过程、子命令、Yew 组件或整个 crate。该模式基于 ~/.claude/CLAUDE.md 中的文档,并取材自 code/ads-back 的实际文件(crates/api/create-card.org、crates/api/retrve-card.org、crates/cli/main.org、crates/svc/main.org、根目录下的 config.org、env.org)。
第 0 步 — 确定变体
默认情况下(无显式参数),从用户请求本身确定变体;如果存在歧义,通过 AskUserQuestion 提问澄清,而非默默猜测:
- A. 在现有 Crate 中添加过程/命令 —— 典型情况(例如在
crates/api中添加新的rocket处理器,或在crates/db中添加库函数)。创建一个新的.org文件,config.org不变。 - B. 为现有 CLI 添加子命令 —— 新文件
<子命令>.org(模块)+ 插入到该 Crate 现有main.org文件中(mod、Command:Елементи、Варіанти)。 - C.
Yew组件(ads-front)—— 结构更接近crates/app/marker.org:包含一个“属性”部分(带有组件属性)和Html块。使用相同的骨架(声明在定义之前,每个概念一个标题),但块术语不同。 - D. 新 Crate(“联邦
cargo”)—— 当组件逻辑上不属于任何现有 Crate 时。此时,除了组件本身,还需要创建新 Crate 的config.org、env.org、main.org,并将其注册到根目录config.org中(参见第 4 步)。
不要“以防万一”指定变体 D —— 仅当用户显式要求新 Crate,或者现有 Crate 在逻辑上都不合适(且经用户确认)时使用。
第 1 步 — .org 文件的语言
语言取决于具体情况,而非固定不变。在创建文件之前,检查目标目录中相邻 .org 文件的语言(code/ads-back/env.org 为英语,大多数 crates/api/*.org 为乌克兰语):如果目录具有明显的语言多数,则使用该语言编写;如果这是一个新目录或相邻文件的语言不明确,则向用户提出一个问题,不要猜测。
第 2 步 — 公共骨架(所有变体)
无论变体如何,每个具有自身 :tangle 目标的新 .org 文件:
-
#+title: ...— 组件名称(使用第 1 步中确定的语言)。 -
如有必要,添加一个引用规范文档的段落(
[[https://...][规范]]),如create-card.org中所示。 -
第一个标题(
* 模块/* 文件/* Crate— 名称仿照同一目录中相邻文件的示例,并非严格固定),包含一个骨架代码块:#+begin_src rust :noweb yes :mkdirp yes :tangle src/文件名.rs <<声明>> <<定义>> #+end_src -
骨架之后立即紧跟
* 执行者部分(用于过程/命令),包含两个代码块::noweb-ref 声明(引入,包括来自ads_api的类型Request/Response)和:noweb-ref 定义(函数pub fn run(...)本身,其主体主要包含<<动作>>,如果需要事务,则位于db::cnnctn().transaction(...)内部)。 -
每个后续的逻辑细节(检查、获取字段、插入表等)作为一个独立的
*/**标题,具有自身的:noweb-ref 声明(如果需要新的use)和:noweb-ref 动作——按此顺序(声明在使用之前,与所有其他地方一致)。不要将所有逻辑收集到一个块中——每个有意义的步骤都应有一个标题,如create-card.org所示(那里有 15 个以上的独立标题用于一个过程)。 -
如果一个步骤产生了一个辅助函数(例如
create-card.org中的upsert_phone)——使用独立的:noweb-ref 定义用于函数签名,其主体通过一个命名的noweb-ref(upsert-phone:动作),以及**/***子标题用于其内部的每个动作。
变体 B(CLI 子命令)的不同之处在于不创建新的骨架代码块;相反,将三个 noweb 块添加到现有的 main.org 中,无需新的骨架标题::noweb-ref 声明(mod 新子命令;)、:noweb-sep ",\n" :noweb-ref Command:Елементи(带参数字段的 Command 枚举变体),以及 :noweb yes :noweb-sep ",\n" :noweb-ref 变体(Command::变体 { ... } => crate::模块::run(...))——每个块位于自己的 * 标题下,标题按操作命名(例如 * 创建组织),实现本身位于一个按照公共骨架(第 2 步,变体 A)创建的单独新子命令 .org 文件中。
变体 C(Yew 组件)在部分名称上有所不同:用“属性”部分(struct ...Props,一个属性 = 一个 ** 子部分,:noweb-sep ",\n" :noweb-ref 名称Props:Властивості)和“组件”部分(#[function_component(...)],主体为 <<名称:Стан>>、<<名称:Ефекти>>、<<名称:Обробники>>、html! { <<名称:Html>> })替代“执行者”。请参考 code/ads-front/crates/app/marker.org 作为此变体的实际示例。
第 3 步 — 典型的 main 函数(变体 D,初始版本)
新 Crate 的第一个版本的 main.org 应该尽可能简单——没有错误处理、没有 Result、没有 async、没有 logger/env 初始化,除非有具体需求:
#+title: CRATE 名称
* 文件
#+begin_src rust :noweb yes :mkdirp yes :tangle src/main.rs
<<声明>>
<<定义>>
fn main() {
<<动作>>
}
#+end_src
<<动作>> 最初可能完全为空(新 Crate 尚未做任何事情)——这是一个正常的第一步,后续的标题将逐步填充它,就像 crates/cli/main.org/crates/svc/main.org 中那样。错误处理(Result、?)、async、dotenv/env_logger 的初始化等应通过单独的标题稍后添加,只有当特定步骤实际需要时才添加——不要预先添加。
第 4 步 — 新 Crate:config.org 和在联邦 config.org 中的注册
-
crates/名称/config.org— Crate 文件(参见crates/cli/config.org作为示例):#+title: =Cargo= 文件 * 文件 #+begin_src toml :noweb yes :tangle Cargo.toml [package] name = "名称" version = "0.1.0" edition = "2021" [dependencies] <<依赖>> #+end_src每个依赖项作为一个独立的
* =crate 名称=标题,带有:noweb-ref 依赖(名称 = { workspace = true }),按照CLAUDE.md中的规则排序(第三方 →forge→ 项目内 crate,否则按字母顺序)。* 版本 =Rust=/* 本地配置(rust-toolchain.toml/.cargo/config.toml)仅当该目录中的其他 crate 已经这样做时才从相邻的config.org复制——不要“以防万一”添加它们,如果相邻的 crate 都没有这样做。如果复制,文件将成为多目标文件(多个不同的:tangle),因此所有 noweb 块(包括依赖)都应使用前缀名称(Cargo:依赖、config:定义等,参见 CLAUDE.md)——就像根目录的config.org中那样。 -
在根目录
config.org(工作区)中,仿照根目录中* =db=/* =api=部分的示例,添加一个新的* =名称=标题,包含两个代码块:#+begin_src toml :noweb-sep ",\n" :noweb-ref Cargo:工作区成员 "crates/名称" #+end_src #+begin_src toml :noweb-ref Cargo:依赖 名称 = { path = "crates/名称" } #+end_src -
env.org— 如果新 Crate 没有引入自己的环境变量,则无需修改。如果引入了,并且项目中已经存在一个公共的根目录env.org,则将一个新的*部分(带有自身的:noweb-ref,如* 数据库访问/* =JWT= 参数)添加到该文件中,而不是创建一个单独的文件。仅当新 Crate 是一个独立的部署(拥有自己的.env,而不是同一个进程)并且没有公共的env.org时,才为它单独创建env.org。无论如何,第一个env.org可以完全没有具体的变量(可以完全没有值,只有主文件)——只是一个骨架:#+title: =dotenv= 文件 * 配置文件 #+begin_src conf :noweb yes :tangle .env.example <<定义>> #+end_src稍后有实际变量出现时,再填充具体的
* =X= 参数部分。
第 5 步 — 每次更改后验证
在对 .org 源文件进行每次实质性编辑后:
- 通过
emacsclient --eval进行实际的tangle导出(切勿手动编写.rs/.toml/.env.example的内容 —— 参见相关的全局规则)。 - 运行
cargo check -p CRATE(对于ads-front,添加--target wasm32-unknown-unknown) — 零警告和错误。 - 对更改后的文件运行
org-lint技能,并立即修复已确认的问题;对于“需要检查”的问题进行审查。 - 对于新编写或已更改的散文内容,通过相同的
emacsclient运行(font-lock-ensure)+org-fill-paragraph,不要手动调整。
在针对每个新更改或已更改的 .org 文件完成上述四个要点之前,不要向用户展示该组件为就绪状态。