use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Boot {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kernel: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub initrd: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cmdline: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dtb: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub firmware: Option<Firmware>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub loader: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub nvram: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub firmware_format: Option<FirmwareFormat>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub order: Vec<BootDevice>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub network: Option<NetworkBoot>,
}
impl Boot {
#[must_use]
pub fn beyond_firmware_format(&self) -> Vec<&'static str> {
let set = [
("firmware", self.firmware == Some(Firmware::Bios)),
("kernel", self.kernel.is_some()),
("initrd", self.initrd.is_some()),
("cmdline", self.cmdline.is_some()),
("dtb", self.dtb.is_some()),
("loader", self.loader.is_some()),
("nvram", self.nvram.is_some()),
("order", !self.order.is_empty()),
("network", self.network.is_some()),
];
set.into_iter().filter(|(_, on)| *on).map(|(name, _)| name).collect()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum Firmware {
Bios,
Uefi,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum FirmwareFormat {
Raw,
Qcow2,
}
impl FirmwareFormat {
#[must_use]
pub fn id(self) -> &'static str {
match self {
FirmwareFormat::Raw => "raw",
FirmwareFormat::Qcow2 => "qcow2",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum BootDevice {
Hd,
Network,
Cdrom,
}
impl BootDevice {
#[must_use]
pub fn id(self) -> &'static str {
match self {
BootDevice::Hd => "hd",
BootDevice::Network => "network",
BootDevice::Cdrom => "cdrom",
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct NetworkBoot {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tftp_root: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub server: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
fn beyond(yaml: &str) -> Result<Vec<&'static str>, serde_yaml_ng::Error> {
Ok(serde_yaml_ng::from_str::<Boot>(yaml)?.beyond_firmware_format())
}
#[test]
fn uefi_and_its_format_are_all_vagrants_qemu_provider_boots() -> Result<(), serde_yaml_ng::Error> {
assert_eq!(beyond("{ firmware: uefi, firmware_format: qcow2 }")?, Vec::<&str>::new());
assert_eq!(
beyond("{ firmware: bios, kernel: /k, initrd: /i, cmdline: quiet, dtb: /d, loader: /l, nvram: /n, order: [hd] }")?,
["firmware", "kernel", "initrd", "cmdline", "dtb", "loader", "nvram", "order"]
);
Ok(())
}
#[test]
fn a_firmware_format_goes_by_the_name_a_spec_writes() -> Result<(), serde_yaml_ng::Error> {
for format in [FirmwareFormat::Raw, FirmwareFormat::Qcow2] {
assert_eq!(serde_yaml_ng::from_str::<FirmwareFormat>(format.id())?, format);
}
Ok(())
}
}