#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Hmi {
pub engine: HmiEngine,
pub backend: HmiBackend,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub debug: Option<String>,
pub source: String,
pub build: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub app: Option<String>,
#[serde(default)]
pub logical: HmiLogical,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct HmiLogical {
pub width: u32,
pub height: u32,
}
impl Default for HmiLogical {
fn default() -> Self {
Self {
width: 480,
height: 272,
}
}
}
impl Hmi {
pub fn validate(&self) -> Result<(), String> {
if self.logical.width == 0 || self.logical.height == 0 {
return Err("[hmi.logical] width and height must be non-zero".to_string());
}
match self.engine {
HmiEngine::Cog => Ok(()),
HmiEngine::Pocket => {
if self.app.as_deref().unwrap_or_default().trim().is_empty() {
return Err(
"[hmi] engine = \"pocket\" requires `app` (the PocketJS entry name, \
e.g. \"hero-main\")"
.to_string(),
);
}
if self.backend == HmiBackend::Drm {
return Err(
"[hmi] engine = \"pocket\" does not yet support backend = \"drm\"; \
use \"wayland\" or \"headless\""
.to_string(),
);
}
Ok(())
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum HmiEngine {
Cog,
Pocket,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum HmiBackend {
Wayland,
Drm,
Headless,
}
#[cfg(test)]
mod tests {
use super::*;
fn pocket(app: Option<&str>, backend: HmiBackend) -> Hmi {
Hmi {
engine: HmiEngine::Pocket,
backend,
debug: None,
source: "submodules/pocketjs".to_string(),
build: "dist".to_string(),
app: app.map(str::to_string),
logical: HmiLogical::default(),
}
}
#[test]
fn pocket_requires_app_entry() {
assert!(
pocket(Some("hero-main"), HmiBackend::Wayland)
.validate()
.is_ok()
);
assert!(pocket(None, HmiBackend::Wayland).validate().is_err());
assert!(pocket(Some(" "), HmiBackend::Wayland).validate().is_err());
}
#[test]
fn pocket_rejects_drm_backend_for_now() {
assert!(
pocket(Some("hero-main"), HmiBackend::Drm)
.validate()
.is_err()
);
assert!(
pocket(Some("hero-main"), HmiBackend::Headless)
.validate()
.is_ok()
);
}
#[test]
fn cog_ignores_pocket_only_fields() {
let hmi = Hmi {
engine: HmiEngine::Cog,
backend: HmiBackend::Wayland,
debug: None,
source: "website".to_string(),
build: "website/dist".to_string(),
app: None,
logical: HmiLogical::default(),
};
assert!(hmi.validate().is_ok());
}
#[test]
fn rejects_zero_logical_dimensions() {
let mut hmi = pocket(Some("hero-main"), HmiBackend::Headless);
hmi.logical.width = 0;
assert!(hmi.validate().is_err());
}
}