1*285d70f1SLoGin use serde::Deserialize;
2*285d70f1SLoGin
3*285d70f1SLoGin use super::hypervisor::hyp_type::HypervisorType;
4*285d70f1SLoGin
5*285d70f1SLoGin #[derive(Debug, Clone, Deserialize)]
6*285d70f1SLoGin pub struct BootMetadata {
7*285d70f1SLoGin /// The boot protocol used during startup
8*285d70f1SLoGin #[serde(rename = "boot-protocol")]
9*285d70f1SLoGin pub boot_protocol: BootProtocol,
10*285d70f1SLoGin /// The mode of booting
11*285d70f1SLoGin #[serde(rename = "boot-mode")]
12*285d70f1SLoGin pub boot_mode: BootMode,
13*285d70f1SLoGin /// The hypervisor used during startup
14*285d70f1SLoGin pub hypervisor: HypervisorType,
15*285d70f1SLoGin
16*285d70f1SLoGin /// Kernel command-line arguments
17*285d70f1SLoGin #[serde(rename = "kcmd-args", default = "default_empty_vec")]
18*285d70f1SLoGin pub kcmd_args: Vec<String>,
19*285d70f1SLoGin /// Arguments passed to the init process
20*285d70f1SLoGin #[serde(rename = "init-args", default = "default_empty_vec")]
21*285d70f1SLoGin pub init_args: Vec<String>,
22*285d70f1SLoGin }
23*285d70f1SLoGin
default_empty_vec() -> Vec<String>24*285d70f1SLoGin fn default_empty_vec() -> Vec<String> {
25*285d70f1SLoGin vec![]
26*285d70f1SLoGin }
27*285d70f1SLoGin
28*285d70f1SLoGin #[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
29*285d70f1SLoGin pub enum BootProtocol {
30*285d70f1SLoGin /// BIOS Bootloader
31*285d70f1SLoGin #[serde(rename = "grub-legacy")]
32*285d70f1SLoGin GrubLegacy,
33*285d70f1SLoGin /// UEFI Bootloader (Grub)
34*285d70f1SLoGin #[serde(rename = "grub-efi")]
35*285d70f1SLoGin GrubEFI,
36*285d70f1SLoGin /// Direct Linux Boot (with `-kernel` options)
37*285d70f1SLoGin #[serde(rename = "direct")]
38*285d70f1SLoGin Direct,
39*285d70f1SLoGin /// Dragon Stub Bootloader (riscv only)
40*285d70f1SLoGin #[serde(rename = "dragon-stub")]
41*285d70f1SLoGin DragonStub,
42*285d70f1SLoGin }
43*285d70f1SLoGin
44*285d70f1SLoGin /// The mode of booting
45*285d70f1SLoGin #[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
46*285d70f1SLoGin pub enum BootMode {
47*285d70f1SLoGin /// Graphic mode
48*285d70f1SLoGin #[serde(rename = "graphic")]
49*285d70f1SLoGin Graphic,
50*285d70f1SLoGin /// Graphic mode with VNC
51*285d70f1SLoGin #[serde(rename = "graphic-vnc")]
52*285d70f1SLoGin GraphicVnc,
53*285d70f1SLoGin /// No graphic mode
54*285d70f1SLoGin #[serde(rename = "no-graphic")]
55*285d70f1SLoGin NoGraphic,
56*285d70f1SLoGin }
57*285d70f1SLoGin
58*285d70f1SLoGin #[cfg(test)]
59*285d70f1SLoGin mod tests {
60*285d70f1SLoGin use super::*;
61*285d70f1SLoGin
62*285d70f1SLoGin // Helper function to parse TOML string into BootMetadata
parse_boot_metadata(toml_str: &str) -> Result<BootMetadata, toml::de::Error>63*285d70f1SLoGin fn parse_boot_metadata(toml_str: &str) -> Result<BootMetadata, toml::de::Error> {
64*285d70f1SLoGin toml::from_str(toml_str)
65*285d70f1SLoGin }
66*285d70f1SLoGin
assert_missing_field(err_str: &str, field: &str)67*285d70f1SLoGin fn assert_missing_field(err_str: &str, field: &str) {
68*285d70f1SLoGin assert!(err_str.contains(&format!("missing field `{field}`")));
69*285d70f1SLoGin }
70*285d70f1SLoGin
assert_unknown_variant(err_str: &str, variant: &str)71*285d70f1SLoGin fn assert_unknown_variant(err_str: &str, variant: &str) {
72*285d70f1SLoGin assert!(err_str.contains(&format!("unknown variant `{variant}`")));
73*285d70f1SLoGin }
74*285d70f1SLoGin
75*285d70f1SLoGin #[test]
test_parse_grub_legacy_graphic()76*285d70f1SLoGin fn test_parse_grub_legacy_graphic() {
77*285d70f1SLoGin let toml_str = r#"
78*285d70f1SLoGin boot-protocol = "grub-legacy"
79*285d70f1SLoGin boot-mode = "graphic"
80*285d70f1SLoGin hypervisor = "qemu"
81*285d70f1SLoGin "#;
82*285d70f1SLoGin
83*285d70f1SLoGin let result = parse_boot_metadata(toml_str).unwrap();
84*285d70f1SLoGin assert_eq!(result.boot_protocol, BootProtocol::GrubLegacy);
85*285d70f1SLoGin assert_eq!(result.boot_mode, BootMode::Graphic);
86*285d70f1SLoGin }
87*285d70f1SLoGin
88*285d70f1SLoGin #[test]
test_parse_grub_efi_graphic_vnc()89*285d70f1SLoGin fn test_parse_grub_efi_graphic_vnc() {
90*285d70f1SLoGin let toml_str = r#"
91*285d70f1SLoGin boot-protocol = "grub-efi"
92*285d70f1SLoGin boot-mode = "graphic-vnc"
93*285d70f1SLoGin hypervisor = "qemu"
94*285d70f1SLoGin "#;
95*285d70f1SLoGin
96*285d70f1SLoGin let result = parse_boot_metadata(toml_str).unwrap();
97*285d70f1SLoGin assert_eq!(result.boot_protocol, BootProtocol::GrubEFI);
98*285d70f1SLoGin assert_eq!(result.boot_mode, BootMode::GraphicVnc);
99*285d70f1SLoGin }
100*285d70f1SLoGin
101*285d70f1SLoGin #[test]
test_parse_direct_no_graphic()102*285d70f1SLoGin fn test_parse_direct_no_graphic() {
103*285d70f1SLoGin let toml_str = r#"
104*285d70f1SLoGin boot-protocol = "direct"
105*285d70f1SLoGin boot-mode = "no-graphic"
106*285d70f1SLoGin hypervisor = "qemu"
107*285d70f1SLoGin "#;
108*285d70f1SLoGin
109*285d70f1SLoGin let result = parse_boot_metadata(toml_str).unwrap();
110*285d70f1SLoGin assert_eq!(result.boot_protocol, BootProtocol::Direct);
111*285d70f1SLoGin assert_eq!(result.boot_mode, BootMode::NoGraphic);
112*285d70f1SLoGin }
113*285d70f1SLoGin
114*285d70f1SLoGin #[test]
test_parse_dragon_stub_graphic()115*285d70f1SLoGin fn test_parse_dragon_stub_graphic() {
116*285d70f1SLoGin let toml_str = r#"
117*285d70f1SLoGin boot-protocol = "dragon-stub"
118*285d70f1SLoGin boot-mode = "graphic"
119*285d70f1SLoGin hypervisor = "qemu"
120*285d70f1SLoGin "#;
121*285d70f1SLoGin
122*285d70f1SLoGin let result = parse_boot_metadata(toml_str).unwrap();
123*285d70f1SLoGin assert_eq!(result.boot_protocol, BootProtocol::DragonStub);
124*285d70f1SLoGin assert_eq!(result.boot_mode, BootMode::Graphic);
125*285d70f1SLoGin }
126*285d70f1SLoGin
127*285d70f1SLoGin #[test]
test_parse_missing_boot_protocol()128*285d70f1SLoGin fn test_parse_missing_boot_protocol() {
129*285d70f1SLoGin let toml_str = r#"
130*285d70f1SLoGin boot-mode = "graphic"
131*285d70f1SLoGin "#;
132*285d70f1SLoGin
133*285d70f1SLoGin let r = parse_boot_metadata(toml_str);
134*285d70f1SLoGin assert!(r.is_err());
135*285d70f1SLoGin let r = r.unwrap_err();
136*285d70f1SLoGin assert_missing_field(&r.to_string(), "boot-protocol");
137*285d70f1SLoGin }
138*285d70f1SLoGin
139*285d70f1SLoGin #[test]
test_parse_missing_boot_mode()140*285d70f1SLoGin fn test_parse_missing_boot_mode() {
141*285d70f1SLoGin let toml_str = r#"
142*285d70f1SLoGin boot-protocol = "grub-legacy"
143*285d70f1SLoGin hypervisor = "qemu"
144*285d70f1SLoGin "#;
145*285d70f1SLoGin
146*285d70f1SLoGin let r = parse_boot_metadata(toml_str);
147*285d70f1SLoGin assert!(r.is_err());
148*285d70f1SLoGin let r = r.unwrap_err();
149*285d70f1SLoGin assert_missing_field(&r.to_string(), "boot-mode");
150*285d70f1SLoGin }
151*285d70f1SLoGin
152*285d70f1SLoGin #[test]
test_parse_invalid_boot_protocol()153*285d70f1SLoGin fn test_parse_invalid_boot_protocol() {
154*285d70f1SLoGin let toml_str = r#"
155*285d70f1SLoGin boot-protocol = "invalid-protocol"
156*285d70f1SLoGin boot-mode = "graphic"
157*285d70f1SLoGin "#;
158*285d70f1SLoGin let r = parse_boot_metadata(toml_str);
159*285d70f1SLoGin assert!(r.is_err());
160*285d70f1SLoGin let r = r.unwrap_err();
161*285d70f1SLoGin assert_unknown_variant(&r.to_string(), "invalid-protocol");
162*285d70f1SLoGin }
163*285d70f1SLoGin
164*285d70f1SLoGin #[test]
test_parse_invalid_boot_mode()165*285d70f1SLoGin fn test_parse_invalid_boot_mode() {
166*285d70f1SLoGin let toml_str = r#"
167*285d70f1SLoGin boot-protocol = "grub-legacy"
168*285d70f1SLoGin boot-mode = "invalid-mode"
169*285d70f1SLoGin "#;
170*285d70f1SLoGin
171*285d70f1SLoGin let r = parse_boot_metadata(toml_str);
172*285d70f1SLoGin assert!(r.is_err());
173*285d70f1SLoGin let r = r.unwrap_err();
174*285d70f1SLoGin assert_unknown_variant(&r.to_string(), "invalid-mode");
175*285d70f1SLoGin }
176*285d70f1SLoGin
177*285d70f1SLoGin #[test]
test_parse_empty_fields()178*285d70f1SLoGin fn test_parse_empty_fields() {
179*285d70f1SLoGin let toml_str = r#"
180*285d70f1SLoGin boot-protocol = ""
181*285d70f1SLoGin boot-mode = ""
182*285d70f1SLoGin "#;
183*285d70f1SLoGin
184*285d70f1SLoGin assert!(parse_boot_metadata(toml_str).is_err());
185*285d70f1SLoGin }
186*285d70f1SLoGin }
187