From 0d630dde84abc7487acdd8581852f988bab36737 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 24 Jun 2026 12:10:07 +0300 Subject: [PATCH] =?UTF-8?q?feat(ipc):=20enum-=D1=82=D0=B8=D0=BF=D1=8B=20Po?= =?UTF-8?q?wer/Ignition/Source/Reason=20(string=20round-trip)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 Signed-off-by: Alexander --- crates/shturman-ipc/src/lib.rs | 2 + crates/shturman-ipc/src/types.rs | 126 +++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 crates/shturman-ipc/src/types.rs diff --git a/crates/shturman-ipc/src/lib.rs b/crates/shturman-ipc/src/lib.rs index 37092b5..f1053d6 100644 --- a/crates/shturman-ipc/src/lib.rs +++ b/crates/shturman-ipc/src/lib.rs @@ -4,5 +4,7 @@ pub mod error; pub mod names; +pub mod types; pub use error::Error; +pub use types::{IgnitionState, PowerSource, PowerState, ShutdownReason}; diff --git a/crates/shturman-ipc/src/types.rs b/crates/shturman-ipc/src/types.rs new file mode 100644 index 0000000..022ca83 --- /dev/null +++ b/crates/shturman-ipc/src/types.rs @@ -0,0 +1,126 @@ +//! Enum-типы шины (data-model / spec §5.2): на проводе — строки (snake_case). +//! D-Bus-сигнатуры используют `String` (см. `proxy`); Rust-сторона конвертит через `as_str`/`FromStr`. + +/// Генерирует enum с `as_str`/`FromStr`/`Display` (строка на проводе). +macro_rules! str_enum { + ($(#[$m:meta])* $name:ident { $($variant:ident => $s:literal),+ $(,)? }) => { + $(#[$m])* + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum $name { $($variant),+ } + + impl $name { + pub fn as_str(&self) -> &'static str { + match self { $(Self::$variant => $s),+ } + } + } + + impl core::str::FromStr for $name { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + $($s => Ok(Self::$variant),)+ + _ => Err(()), + } + } + } + + impl core::fmt::Display for $name { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(self.as_str()) + } + } + }; +} + +str_enum!( + /// Состояние питания (`Power.GetPowerState`, B §2). + PowerState { + Off => "off", + Accessory => "accessory", + Running => "running", + ShuttingDown => "shutting_down", + Sleep => "sleep", + BatteryCutoff => "battery_cutoff", + } +); + +str_enum!( + /// Ось зажигания — канон (`Power.IgnitionState`, B §1; E зеркалит, не дублирует). + IgnitionState { + Off => "off", + Accessory => "accessory", + Running => "running", + } +); + +str_enum!( + /// Источник питания (`Power.PowerSource`). + PowerSource { + Vehicle12v => "vehicle_12v", + HoldupCap => "holdup_cap", + SleepRail => "sleep_rail", + LowBattery => "low_battery", + } +); + +str_enum!( + /// Причина выключения (`Power.ShutdownImminent`). + ShutdownReason { + AccOff => "acc_off", + UnderVoltage => "under_voltage", + Thermal => "thermal", + BatteryCutoff => "battery_cutoff", + } +); + +#[cfg(test)] +mod tests { + use super::*; + use core::str::FromStr; + + #[test] + fn power_state_round_trip() { + for v in [ + PowerState::Off, + PowerState::Accessory, + PowerState::Running, + PowerState::ShuttingDown, + PowerState::Sleep, + PowerState::BatteryCutoff, + ] { + assert_eq!(PowerState::from_str(v.as_str()), Ok(v)); + } + assert!(PowerState::from_str("nonsense").is_err()); + } + + #[test] + fn ignition_round_trip() { + for v in [ + IgnitionState::Off, + IgnitionState::Accessory, + IgnitionState::Running, + ] { + assert_eq!(IgnitionState::from_str(v.as_str()), Ok(v)); + } + } + + #[test] + fn source_and_reason_round_trip() { + for v in [ + PowerSource::Vehicle12v, + PowerSource::HoldupCap, + PowerSource::SleepRail, + PowerSource::LowBattery, + ] { + assert_eq!(PowerSource::from_str(v.as_str()), Ok(v)); + } + for v in [ + ShutdownReason::AccOff, + ShutdownReason::UnderVoltage, + ShutdownReason::Thermal, + ShutdownReason::BatteryCutoff, + ] { + assert_eq!(ShutdownReason::from_str(v.as_str()), Ok(v)); + } + } +}