create-cmpnnt
Навичка для створення нового компонента (cmpnnt) — команди, процедури, підкоманди, Yew-компонента чи цілого крейта — в літературному org-babel стилі, задокументованому в ~/.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. Процедура/команда в наявному крейті — типовий випадок (напр. новий
rocket-хендлер вcrates/api, нова бібліотечна функція вcrates/db). Один новий org-файл, без змін уconfig.org. - B. Підкоманда наявного
CLI— новий файл<підкоманда>.org(мод) + вписування в наявнийmain.orgцього крейта (mod,Command:Елементи,Варіанти). - C.
Yew-компонент (ads-front) — структура ближча доcrates/app/marker.org: розділ "Властивості" зі властивостями компонента,Html-блоки. Той самий каркас (Декларації перед Визначенням, один заголовок на концепцію), інша термінологія блоків. - D. Новий крейт ("федеративний
cargo") — коли компонент логічно не влазить у жоден наявний крейт. Тоді, крім самого компонента, потрібніconfig.org,env.org,main.orgнового крейта і реєстрація в кореневомуconfig.org(див. Крок 4).
Не признач варіант D "про запас" — лише коли користувач явно просить новий крейт, або коли жоден наявний крейт логічно не підходить (і це підтверджено користувачем).
Крок 1 — мова org-файлу
Мова залежить від ситуації, не фіксована наперед. Перед створенням файлу подивись мову сусідніх org-файлів у цільовому каталозі (code/ads-back/env.org — англійською, більшість crates/api/*.org — українською): якщо каталог має чітку більшість — пиши тією ж мовою; якщо це новий каталог або мова сусідів неоднозначна — запитай користувача одним питанням, не вгадуй.
Крок 2 — спільний каркас (усі варіанти)
Незалежно від варіанта, кожен новий org-файл із власним :tangle-таргетом:
-
#+title: ...— назва компонента (мовою з кроку 1). -
За потреби — абзац із посиланням на специфікацію (
[[https://...][специфікацію]]), як уcreate-card.org. -
Перший заголовок (
* Модуль/* Файл/* Крейт— назва за прикладом сусідніх файлів того ж каталогу, це не строго фіксовано) зі скелетним блоком:#+begin_src rust :noweb yes :mkdirp yes :tangle src/ІМ'Я_ФАЙЛУ.rs <<Декларації>> <<Визначення>> #+end_src -
Одразу після скелета — розділ
* Виконавець(для процедури/команди) з двома блоками::noweb-ref Декларації(імпорти, включно з типамиRequest/Responseзads_api) і:noweb-ref Визначення(сама функціяpub fn run(...), тіло якої переважно<<Дії>>всерединіdb::cnnctn().transaction(...), якщо потрібна транзакція). -
Кожна наступна дрібниця логіки (перевірка, отримання поля, вставка в таблицю тощо) — окремий заголовок
*/**із власними:noweb-ref Декларації(якщо потрібні новіuse) і:noweb-ref Дії— у такому порядку (декларація перед вживанням, як і скрізь). Не збирай усю логіку в один блок — кожен смисловий крок повинен мати свій заголовок, як уcreate-card.org(там 15+ окремих заголовків на одну процедуру). -
Якщо один крок породжує допоміжну функцію (напр.
upsert_phoneуcreate-card.org) — власний:noweb-ref Визначеннядля сигнатури функції, а тіло — через власний іменованийnoweb-ref(upsert-phone:Дії), і підзаголовки**/***для кожної дії всередині неї.
Варіант B (підкоманда CLI) відрізняється тим, що нового скелетного блока не буде — замість цього допиши в наявний main.org три носвеб-блоки без нового заголовка-скелета: :noweb-ref Декларації (mod нова_підкоманда;), :noweb-sep ",\n" :noweb-ref Command:Елементи (варіант enum Command із полями-аргументами), і :noweb yes :noweb-sep ",\n" :noweb-ref Варіанти (Command::Варіант { ... } => crate::модуль::run(...)) — кожен у своєму *-заголовку, названому дією (напр. * Створення організації), і сама реалізація — в окремому новому org-файлі підкоманди за спільним каркасом (крок 2, варіант A).
Варіант C (Yew-компонент) відрізняється назвами розділів: замість "Виконавець" — розділ "Властивості" (struct ...Props, одна властивість = один **-підрозділ, :noweb-sep ",\n" :noweb-ref ІмʼяProps:Властивості), і розділ "Компонент" (#[function_component(...)], тіло — <<Ім'я:Стан>>, <<Ім'я:Ефекти>>, <<Ім'я:Обробники>>, html! { <<Ім'я:Html>> }). Дивись code/ads-front/crates/app/marker.org як живий приклад цього варіанта.
Крок 3 — типова функція main (варіант D, спочатку)
Перша версія main.org нового крейта має бути найтупішою — без обробки помилок, без Result, без async, без ініціалізації логера/env, поки в цьому немає конкретної потреби:
#+title: НАЗВА КРЕЙТА
* Файл
#+begin_src rust :noweb yes :mkdirp yes :tangle src/main.rs
<<Декларації>>
<<Визначення>>
fn main() {
<<Дії>>
}
#+end_src
<<Дії>> спершу може бути зовсім порожнім (нова крейта ще нічого не робить) — це нормальний перший крок, наступні заголовки поступово наповнюють його, як у crates/cli/main.org/crates/svc/main.org. Обробку помилок (Result, ?), async, ініціалізацію dotenv/env_logger тощо додавай окремими заголовками пізніше, коли конкретний крок цього реально вимагає — не наперед.
Крок 4 — новий крейт: config.org і реєстрація у федеративному config.org
-
crates/НАЗВА/config.org— файл крейта (див.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Кожна залежність — окремий
* =назва_крейта=заголовок із:noweb-ref Залежності(назва = { workspace = true }), впорядкований за правилом зCLAUDE.md(сторонні →forge→ проєктні крейти, інакше — алфавіт). Блоки* Версія =Rust=/* Локальна конфігурація(rust-toolchain.toml/.cargo/config.toml) копіюй з сусідньогоconfig.orgлише якщо в цьому каталозі так уже заведено — не додавай їх "про всяк випадок", якщо жоден сусідній крейт цього не робить. Якщо копіюєш — файл стає multi-target (кілька різних:tangle), тож усі носвеб-блоки (включно зЗалежності) переходять на префіксовані імена (Cargo:Залежності,config:Визначеннятощо, див. CLAUDE.md) — як у кореневомуconfig.orgнижче. -
У КОРЕНЕВОМУ
config.org(workspace) додай новий* =НАЗВА=заголовок із двома блоками — за зразком секцій* =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— якщо новий крейт не вводить власних змінних середовища, нічого не чіпай. Якщо вводить — і в проєкті вже є спільний кореневийenv.org— додай туди новий*-розділ із власним:noweb-ref(як* Database access/* =JWT= parameters), а не створюй окремий файл. Створюй ОКРЕМИЙenv.orgдля нового крейта лише тоді, коли він — самостійний деплой (власний.env, не той самий процес) і жодного спільногоenv.orgнемає. У будь-якому разі першийenv.orgможна лишити зовсім без конкретних змінних (можна взагалі без значень, тільки головний файл) — просто скелет:#+title: =dotenv= file * Configuration file #+begin_src conf :noweb yes :tangle .env.example <<Визначення>> #+end_srcі наповнювати конкретними
* =Х= parameters-розділами пізніше, коли з'являться реальні змінні.
Крок 5 — перевірка після кожної зміни
Після кожного суттєвого редагування org-джерела:
- Реальний
tangleчерезemacsclient --eval(ніколи не пиши вміст.rs/.toml/.env.exampleвручну — див. відповідне глобальне правило). cargo check -p КРЕЙТ(додай--target wasm32-unknown-unknownдляads-front) — нуль попереджень і помилок.- Прожени навичку
org-lintна змінений файл і виправ "ПЕВНІ" знахідки одразу, "потребує перевірки" — переглянь. - Для щойно написаної/зміненої прози —
(font-lock-ensure)+org-fill-paragraphчерез той самийemacsclient, не вручну.
Не показуй компонент користувачу як готовий, поки ці чотири пункти не пройдені для кожного нового чи зміненого org-файлу.