use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Persona {
pub schema_version: u32,
pub id: String,
pub vendor: String,
pub display_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub year: Option<u16>,
pub source: Source,
pub dmi: Dmi,
pub secure_boot: SecureBoot,
pub tpm: Tpm,
#[serde(default)]
pub kernel: Kernel,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub quirks: Vec<Quirk>,
#[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
pub scenarios: std::collections::BTreeMap<String, ScenarioDecision>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Source {
pub kind: SourceKind,
pub ref_: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub captured_at: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SourceKind {
CommunityReport,
LvfsCatalog,
VendorDocs,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Dmi {
pub sys_vendor: String,
pub product_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub product_version: Option<String>,
pub bios_vendor: String,
pub bios_version: String,
pub bios_date: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub board_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chassis_type: Option<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SecureBoot {
pub ovmf_variant: OvmfVariant,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub custom_keyring: Option<PathBuf>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum OvmfVariant {
MsEnrolled,
CustomPk,
SetupMode,
Disabled,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Tpm {
pub version: TpmVersion,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub manufacturer: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub firmware_version: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum TpmVersion {
None,
#[serde(rename = "1.2")]
Tpm12,
#[serde(rename = "2.0")]
Tpm20,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Kernel {
#[serde(default)]
pub lockdown: LockdownMode,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum LockdownMode {
#[default]
Inherit,
None,
Integrity,
Confidentiality,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Quirk {
pub tag: String,
pub description: String,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ScenarioDecision {
Run,
Skip,
}
impl Source {}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn minimal_persona_roundtrips() {
let yaml = r#"
schema_version: 1
id: qemu-generic-minimal
vendor: QEMU
display_name: "QEMU generic (OVMF + swtpm reference)"
source:
kind: vendor_docs
ref_: "QEMU -smbios documentation"
dmi:
sys_vendor: QEMU
product_name: "Standard PC (Q35 + ICH9, 2009)"
bios_vendor: EDK II
bios_version: "edk2-stable202402"
bios_date: 02/29/2024
secure_boot:
ovmf_variant: ms_enrolled
tpm:
version: "2.0"
"#;
let p: Persona = serde_yaml_ng::from_str(yaml).unwrap();
assert_eq!(p.id, "qemu-generic-minimal");
assert_eq!(p.dmi.sys_vendor, "QEMU");
assert!(matches!(
p.secure_boot.ovmf_variant,
OvmfVariant::MsEnrolled
));
assert!(matches!(p.tpm.version, TpmVersion::Tpm20));
}
}