Skip to content

Commit 1a6e914

Browse files
Johan-Liebert1cgwalters
authored andcommitted
parser/bls: New file
Signed-off-by: Johan-Liebert1 <[email protected]> Signed-off-by: Colin Walters <[email protected]>
1 parent 376dda2 commit 1a6e914

File tree

2 files changed

+206
-0
lines changed

2 files changed

+206
-0
lines changed

crates/lib/src/parsers/bls_config.rs

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
//! See <https://uapi-group.org/specifications/specs/boot_loader_specification/>
2+
//!
3+
//! This module parses the config files for the spec.
4+
5+
use anyhow::Result;
6+
use serde::de::Error;
7+
use serde::{Deserialize, Deserializer};
8+
use std::collections::HashMap;
9+
use std::fmt::Display;
10+
11+
#[derive(Debug, Deserialize, Eq)]
12+
pub(crate) struct BLSConfig {
13+
pub(crate) title: Option<String>,
14+
#[serde(deserialize_with = "deserialize_version")]
15+
pub(crate) version: u32,
16+
pub(crate) linux: String,
17+
pub(crate) initrd: String,
18+
pub(crate) options: String,
19+
20+
#[serde(flatten)]
21+
pub(crate) extra: HashMap<String, String>,
22+
}
23+
24+
impl PartialEq for BLSConfig {
25+
fn eq(&self, other: &Self) -> bool {
26+
self.version == other.version
27+
}
28+
}
29+
30+
impl PartialOrd for BLSConfig {
31+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
32+
self.version.partial_cmp(&other.version)
33+
}
34+
}
35+
36+
impl Ord for BLSConfig {
37+
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
38+
self.version.cmp(&other.version)
39+
}
40+
}
41+
42+
impl Display for BLSConfig {
43+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44+
if let Some(title) = &self.title {
45+
writeln!(f, "title {}", title)?;
46+
}
47+
48+
writeln!(f, "version {}", self.version)?;
49+
writeln!(f, "linux {}", self.linux)?;
50+
writeln!(f, "initrd {}", self.initrd)?;
51+
writeln!(f, "options {}", self.options)?;
52+
53+
for (key, value) in &self.extra {
54+
writeln!(f, "{} {}", key, value)?;
55+
}
56+
57+
Ok(())
58+
}
59+
}
60+
61+
fn deserialize_version<'de, D>(deserializer: D) -> Result<u32, D::Error>
62+
where
63+
D: Deserializer<'de>,
64+
{
65+
let s: Option<String> = Option::deserialize(deserializer)?;
66+
67+
match s {
68+
Some(s) => Ok(s.parse::<u32>().map_err(D::Error::custom)?),
69+
None => Err(D::Error::custom("Version not found")),
70+
}
71+
}
72+
73+
pub(crate) fn parse_bls_config(input: &str) -> Result<BLSConfig> {
74+
let mut map = HashMap::new();
75+
76+
for line in input.lines() {
77+
let line = line.trim();
78+
if line.is_empty() || line.starts_with('#') {
79+
continue;
80+
}
81+
82+
if let Some((key, value)) = line.split_once(' ') {
83+
map.insert(key.to_string(), value.trim().to_string());
84+
}
85+
}
86+
87+
let value = serde_json::to_value(map)?;
88+
let parsed: BLSConfig = serde_json::from_value(value)?;
89+
90+
Ok(parsed)
91+
}
92+
93+
#[cfg(test)]
94+
mod tests {
95+
use super::*;
96+
97+
#[test]
98+
fn test_parse_valid_bls_config() -> Result<()> {
99+
let input = r#"
100+
title Fedora 42.20250623.3.1 (CoreOS)
101+
version 2
102+
linux /boot/7e11ac46e3e022053e7226a20104ac656bf72d1a84e3a398b7cce70e9df188b6/vmlinuz-5.14.10
103+
initrd /boot/7e11ac46e3e022053e7226a20104ac656bf72d1a84e3a398b7cce70e9df188b6/initramfs-5.14.10.img
104+
options root=UUID=abc123 rw composefs=7e11ac46e3e022053e7226a20104ac656bf72d1a84e3a398b7cce70e9df188b6
105+
custom1 value1
106+
custom2 value2
107+
"#;
108+
109+
let config = parse_bls_config(input)?;
110+
111+
assert_eq!(
112+
config.title,
113+
Some("Fedora 42.20250623.3.1 (CoreOS)".to_string())
114+
);
115+
assert_eq!(config.version, 2);
116+
assert_eq!(config.linux, "/boot/7e11ac46e3e022053e7226a20104ac656bf72d1a84e3a398b7cce70e9df188b6/vmlinuz-5.14.10");
117+
assert_eq!(config.initrd, "/boot/7e11ac46e3e022053e7226a20104ac656bf72d1a84e3a398b7cce70e9df188b6/initramfs-5.14.10.img");
118+
assert_eq!(config.options, "root=UUID=abc123 rw composefs=7e11ac46e3e022053e7226a20104ac656bf72d1a84e3a398b7cce70e9df188b6");
119+
assert_eq!(config.extra.get("custom1"), Some(&"value1".to_string()));
120+
assert_eq!(config.extra.get("custom2"), Some(&"value2".to_string()));
121+
122+
Ok(())
123+
}
124+
125+
#[test]
126+
fn test_parse_missing_version() {
127+
let input = r#"
128+
title Fedora
129+
linux /vmlinuz
130+
initrd /initramfs.img
131+
options root=UUID=xyz ro quiet
132+
"#;
133+
134+
let parsed = parse_bls_config(input);
135+
assert!(parsed.is_err());
136+
}
137+
138+
#[test]
139+
fn test_parse_invalid_version_format() {
140+
let input = r#"
141+
title Fedora
142+
version not_an_int
143+
linux /vmlinuz
144+
initrd /initramfs.img
145+
options root=UUID=abc composefs=some-uuid
146+
"#;
147+
148+
let parsed = parse_bls_config(input);
149+
assert!(parsed.is_err());
150+
}
151+
152+
#[test]
153+
fn test_display_output() -> Result<()> {
154+
let input = r#"
155+
title Test OS
156+
version 10
157+
linux /boot/vmlinuz
158+
initrd /boot/initrd.img
159+
options root=UUID=abc composefs=some-uuid
160+
foo bar
161+
"#;
162+
163+
let config = parse_bls_config(input)?;
164+
let output = format!("{}", config);
165+
let mut output_lines = output.lines();
166+
167+
assert_eq!(output_lines.next().unwrap(), "title Test OS");
168+
assert_eq!(output_lines.next().unwrap(), "version 10");
169+
assert_eq!(output_lines.next().unwrap(), "linux /boot/vmlinuz");
170+
assert_eq!(output_lines.next().unwrap(), "initrd /boot/initrd.img");
171+
assert_eq!(
172+
output_lines.next().unwrap(),
173+
"options root=UUID=abc composefs=some-uuid"
174+
);
175+
assert_eq!(output_lines.next().unwrap(), "foo bar");
176+
177+
Ok(())
178+
}
179+
180+
#[test]
181+
fn test_ordering() -> Result<()> {
182+
let config1 = parse_bls_config(
183+
r#"
184+
title Entry 1
185+
version 3
186+
linux /vmlinuz-3
187+
initrd /initrd-3
188+
options opt1
189+
"#,
190+
)?;
191+
192+
let config2 = parse_bls_config(
193+
r#"
194+
title Entry 2
195+
version 5
196+
linux /vmlinuz-5
197+
initrd /initrd-5
198+
options opt2
199+
"#,
200+
)?;
201+
202+
assert!(config1 < config2);
203+
Ok(())
204+
}
205+
}

crates/lib/src/parsers/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
pub(crate) mod bls_config;
12
pub(crate) mod grub_menuconfig;

0 commit comments

Comments
 (0)