Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Alexander <akotenev2003@gmail.com>
8.2 KiB
План 2 — shturman-ipc (контракт шины) + shturman-sdk (клиент)
REQUIRED SUB-SKILL:
superpowers:executing-plans+ TDD. Полный код пишется вsrcпо ходу (inline-исполнение); здесь — дизайн, задачи, ключевые сигнатуры, тест-кейсы, acceptance.
Goal: зафиксировать контракт D-Bus в коде (shturman-ipc: ошибки, имена/пути/версии, enum-типы, #[proxy]
Power1/Settings1) и публичный клиентский слой (shturman-sdk: connect, SettingsClient/PowerClient,
схема манифеста). Граница: ядро-сервисы будут зависеть от ipc; апы/плагины — от sdk (architecture §3, #1).
Architecture: ipc ← common; sdk ← ipc + common. Enum-типы сериализуются на провод как строки
(spec §5.2): D-Bus-сигнатуры используют String/Variant, Rust-сторона конвертит через as_str/FromStr.
Bus-зависимые обёртки (clients) компилируются здесь, проверяются на живой шине в Плане 3.
Tech Stack: zbus 4 (#[proxy], zbus::DBusError, zvariant), serde/serde_yaml (манифест), thiserror.
Контекст: спека §4–§5, ipc.md, data-model §2,
plugin-sdk §2. Ветка feat/v0-foundation, коммиты -s + Co-Authored-By.
Task 1: shturman-ipc — крейт + Error + names
Files: crates/shturman-ipc/Cargo.toml, src/lib.rs, src/error.rs, src/names.rs; Cargo.toml (members).
- Добавить
crates/shturman-ipcвmembers. Deps:zbus,serde,thiserror,shturman-common. error.rs—ru.shturman.Error.*(ipc §2):
#[derive(Debug, thiserror::Error, zbus::DBusError)]
#[zbus(prefix = "ru.shturman.Error")]
pub enum Error {
#[zbus(error)]
ZBus(zbus::Error), // catch-all для транспортных ошибок
#[error("permission denied: {0}")]
PermissionDenied(String),
#[error("not available: {0}")]
NotAvailable(String),
#[error("stale: {0}")]
Stale(String),
#[error("timeout: {0}")]
Timeout(String),
#[error("read only: {0}")]
ReadOnly(String),
#[error("invalid argument: {0}")]
InvalidArgument(String),
#[error("unsupported: {0}")]
Unsupported(String),
}
names.rs— well-known имена/пути (ipc §2):
pub mod power {
pub const NAME: &str = "ru.shturman.Power";
pub const PATH: &str = "/ru/shturman/Power";
pub const IFACE: &str = "ru.shturman.Power1";
}
pub mod settings {
pub const NAME: &str = "ru.shturman.Settings";
pub const PATH: &str = "/ru/shturman/Settings";
pub const IFACE: &str = "ru.shturman.Settings1";
}
- TDD-тест (error):
Error::InvalidArgument("x").to_string()содержит"invalid argument"; компиляцияzbus::DBusErrorподтверждает маппинг имён. Тест (names): консты соответствуют конвенцииru.shturman.*//ru/shturman/*(sanity). - Build +
cargo test -p shturman-ipcзелёный → commitfeat(ipc): Error + well-known имена.
Task 2: ipc::types — enum-типы (TDD round-trip строк)
Files: crates/shturman-ipc/src/types.rs.
- Enums (data-model/spec §5.2):
PowerState {off, accessory, running, shutting_down, sleep, battery_cutoff},IgnitionState {off, accessory, running},PowerSource {vehicle_12v, holdup_cap, sleep_rail, low_battery},ShutdownReason {acc_off, under_voltage, thermal, battery_cutoff}. Каждому —as_str(&self) -> &'static strиFromStr(snake_case на проводе). - TDD-тесты: для каждого enum —
parse(as_str(v)) == vпо всем вариантам; неизвестная строка →Err. - Падающий тест → реализация → зелёно → commit
feat(ipc): enum-типы Power/Ignition/Source/Reason (string round-trip).
Task 3: ipc::proxy — Power1/Settings1 (#[proxy])
Files: crates/shturman-ipc/src/proxy.rs, обновить lib.rs (re-export).
#[proxy(interface="ru.shturman.Power1", default_service="ru.shturman.Power", default_path="/ru/shturman/Power")]трейтPower1: методget_power_state() -> zbus::Result<String>,request_sleep();#[zbus(property)]ignition_state()->String,uptime()->u64,power_source()->String;#[zbus(signal)]acc_changed(on: bool),shutdown_imminent(seconds: u32, reason: String),shutdown_aborted(),sleep(),wake().Settings1трейт:get(key)->OwnedValue,set(key, value: &Value),list(prefix)->Vec<String>,reset(key);#[zbus(signal)]changed(key: String, value: OwnedValue).- Проверка — компиляция (zbus-макро генерит клиент). Build зелёный → commit
feat(ipc): zbus-proxy Power1/Settings1.
Task 4: shturman-sdk — крейт + manifest (TDD)
Files: crates/shturman-sdk/Cargo.toml, src/lib.rs, src/manifest.rs; Cargo.toml (members).
- Добавить
crates/shturman-sdkвmembers. Deps:zbus,serde,serde_yaml,tokio,shturman-ipc,shturman-common. manifest.rs— serde-схема (plugin-sdk §2):Manifest{ plugin: Plugin, capabilities: Capabilities, extension_points: Option<ExtensionPoints> };Plugin{id,name,version,description,author,shturman_api};Capabilities{ vehicle_read: Vec<String>, assistant_intents: …, ui_tiles: u32, ui_screens: u32, storage: bool, network: Option<…> }(поля#[serde(default)]где опц.).- TDD-тесты: распарсить валидный YAML (пример
fuel-tracker, plugin-sdk §8) → поля верны; битый YAML →Err; отсутствие обязательногоplugin.id→Err. - Падающий тест → реализация → зелёно → commit
feat(sdk): схема манифеста (plugin-sdk §2).
Task 5: sdk — connect + PowerClient + SettingsClient (build; bus — План 3)
Files: crates/shturman-sdk/src/{connect.rs,power.rs,settings.rs}, lib.rs (re-export).
connect()—zbus::Connectionк системной шине (Connection::system()); envSHTURMAN_BUS=sessionдля тестов/dev →Connection::session(). (Вызов на живой шине — План 3.)PowerClient{ proxy: Power1Proxy }:power_state()->Result<PowerState>(парс строки в enum черезipc::types),ignition_state()->Result<IgnitionState>,uptime()->Result<u64>; подписки на сигналы (stream).SettingsClient{ proxy: Settings1Proxy }:get_string(key),set(key, value),list(prefix),reset(key), потокchanged.- Проверка — компиляция +
cargo test --workspace(unit, без шины) +just ci. Re-exports вlib.rs. - commit
feat(sdk): connect + Power/Settings клиенты (bus-тест — План 3).
Acceptance (Плана 2)
cargo build --workspaceзелёный (ipc + sdk компилируются, proxy-макро ок).cargo test --workspace— round-trip enum (ipc) + парс манифеста (sdk) зелёные; common-тесты не сломаны.just ciзелёный (fmt + clippy -D + test + deny). Новые транзитивные лицензии (zbus и пр.) — в allow или добавить.- Границы:
ipcне зависит отsdk; сервисы (План 3) будут импортироватьipc;sdk— для апов.
Дальше
План 3 — стаб-сервисы firstboot/settings/power + интеграция на живой шине (здесь всплывёт вопрос
D-Bus на macOS-хосте: brew install dbus + session-bus, либо перенести Lima-настройку раньше).