fix(review): manifest deny_unknown_fields + каталог сигналов в sdk::vehicle
- deny_unknown_fields: опечатка в ключе capability (граница доверия F §3) не проглатывается. - VEHICLE_SIGNALS → shturman_sdk::vehicle (single source; валидатор берёт оттуда). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Alexander <akotenev2003@gmail.com>
This commit is contained in:
@@ -5,6 +5,7 @@ pub mod connect;
|
|||||||
pub mod manifest;
|
pub mod manifest;
|
||||||
pub mod power;
|
pub mod power;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
pub mod vehicle;
|
||||||
|
|
||||||
pub use connect::connect;
|
pub use connect::connect;
|
||||||
pub use manifest::Manifest;
|
pub use manifest::Manifest;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use serde::Deserialize;
|
|||||||
|
|
||||||
/// Корень манифеста (`manifest.yaml`).
|
/// Корень манифеста (`manifest.yaml`).
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Manifest {
|
pub struct Manifest {
|
||||||
pub plugin: Plugin,
|
pub plugin: Plugin,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -15,6 +16,7 @@ pub struct Manifest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Plugin {
|
pub struct Plugin {
|
||||||
/// reverse-DNS id; `ru.shturman.*` закрыт за first-party (проверяет валидатор, F §3).
|
/// reverse-DNS id; `ru.shturman.*` закрыт за first-party (проверяет валидатор, F §3).
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@@ -29,6 +31,7 @@ pub struct Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize)]
|
#[derive(Debug, Clone, Default, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Capabilities {
|
pub struct Capabilities {
|
||||||
/// Только сигналы из каталога data-model (проверяет валидатор).
|
/// Только сигналы из каталога data-model (проверяет валидатор).
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -46,6 +49,7 @@ pub struct Capabilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Network {
|
pub struct Network {
|
||||||
/// host-allowlist (намерение; строгая гарантия — forced-proxy, plugin-sdk §3).
|
/// host-allowlist (намерение; строгая гарантия — forced-proxy, plugin-sdk §3).
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -53,6 +57,7 @@ pub struct Network {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize)]
|
#[derive(Debug, Clone, Default, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct ExtensionPoints {
|
pub struct ExtensionPoints {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub tiles: Vec<Tile>,
|
pub tiles: Vec<Tile>,
|
||||||
@@ -61,6 +66,7 @@ pub struct ExtensionPoints {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Tile {
|
pub struct Tile {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -68,6 +74,7 @@ pub struct Tile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Intents {
|
pub struct Intents {
|
||||||
/// Путь объекта `IntentHandler` (все фразы → сюда, plugin-sdk §2).
|
/// Путь объекта `IntentHandler` (все фразы → сюда, plugin-sdk §2).
|
||||||
pub handler: String,
|
pub handler: String,
|
||||||
@@ -138,4 +145,19 @@ plugin:
|
|||||||
fn rejects_malformed_yaml() {
|
fn rejects_malformed_yaml() {
|
||||||
assert!(Manifest::from_yaml(": : not yaml : :").is_err());
|
assert!(Manifest::from_yaml(": : not yaml : :").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rejects_unknown_field() {
|
||||||
|
// опечатка в ключе capability на границе доверия (F §3) не должна молча проглатываться
|
||||||
|
let bad = r#"
|
||||||
|
plugin:
|
||||||
|
id: dev.example.x
|
||||||
|
name: "x"
|
||||||
|
version: 0.1.0
|
||||||
|
shturman_api: "1"
|
||||||
|
capabilities:
|
||||||
|
vehicl_read: [speed]
|
||||||
|
"#;
|
||||||
|
assert!(Manifest::from_yaml(bad).is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
//! Каталог стандартных сигналов машины (data-model §4) — single source для валидатора манифеста
|
||||||
|
//! и будущих потребителей (домен E, v2). Алиасы; точные VSS-пути — в домене E.
|
||||||
|
|
||||||
|
/// Алиасы стандартных OBD-II сигналов (data-model §4). `vehicle_read` манифеста — только отсюда.
|
||||||
|
pub const SIGNALS: &[&str] = &[
|
||||||
|
"speed",
|
||||||
|
"rpm",
|
||||||
|
"engine_load",
|
||||||
|
"coolant_temp",
|
||||||
|
"intake_temp",
|
||||||
|
"maf",
|
||||||
|
"throttle",
|
||||||
|
"intake_pressure",
|
||||||
|
"fuel_level",
|
||||||
|
"module_voltage",
|
||||||
|
"ambient_temp",
|
||||||
|
"oil_temp",
|
||||||
|
"fuel_rate",
|
||||||
|
"run_time",
|
||||||
|
"mil_on",
|
||||||
|
"dtc_count",
|
||||||
|
"distance_mil",
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Является ли `alias` стандартным сигналом каталога.
|
||||||
|
pub fn is_known(alias: &str) -> bool {
|
||||||
|
SIGNALS.contains(&alias)
|
||||||
|
}
|
||||||
@@ -2,27 +2,6 @@
|
|||||||
|
|
||||||
use shturman_sdk::Manifest;
|
use shturman_sdk::Manifest;
|
||||||
|
|
||||||
/// Каталог стандартных сигналов (data-model §4). `vehicle_read` допускает только их.
|
|
||||||
const VEHICLE_SIGNALS: &[&str] = &[
|
|
||||||
"speed",
|
|
||||||
"rpm",
|
|
||||||
"engine_load",
|
|
||||||
"coolant_temp",
|
|
||||||
"intake_temp",
|
|
||||||
"maf",
|
|
||||||
"throttle",
|
|
||||||
"intake_pressure",
|
|
||||||
"fuel_level",
|
|
||||||
"module_voltage",
|
|
||||||
"ambient_temp",
|
|
||||||
"oil_temp",
|
|
||||||
"fuel_rate",
|
|
||||||
"run_time",
|
|
||||||
"mil_on",
|
|
||||||
"dtc_count",
|
|
||||||
"distance_mil",
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Поддерживаемые мажоры API (plugin-sdk §7).
|
/// Поддерживаемые мажоры API (plugin-sdk §7).
|
||||||
const SUPPORTED_API: &[&str] = &["1"];
|
const SUPPORTED_API: &[&str] = &["1"];
|
||||||
|
|
||||||
@@ -55,9 +34,9 @@ pub fn validate(m: &Manifest) -> Vec<String> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// vehicle_read только из каталога data-model
|
// vehicle_read только из каталога data-model (single source — shturman_sdk::vehicle)
|
||||||
for sig in &m.capabilities.vehicle_read {
|
for sig in &m.capabilities.vehicle_read {
|
||||||
if !VEHICLE_SIGNALS.contains(&sig.as_str()) {
|
if !shturman_sdk::vehicle::is_known(sig) {
|
||||||
errs.push(format!("vehicle_read '{sig}' нет в каталоге data-model"));
|
errs.push(format!("vehicle_read '{sig}' нет в каталоге data-model"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user