lazaret/crates/lazaret-linux/src/platform.rs

123 lines
3.8 KiB
Rust
Raw Normal View History

use std::fmt;
pub const MINIMUM_KERNEL_MAJOR: u64 = 6;
pub const MINIMUM_KERNEL_MINOR: u64 = 9;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PlatformObservation<'a> {
pub operating_system: &'a str,
pub architecture: &'a str,
pub kernel_release: &'a str,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum PlatformRefusal {
OperatingSystem(String),
Architecture(String),
KernelRelease(String),
KernelFloor { major: u64, minor: u64 },
}
impl fmt::Display for PlatformRefusal {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::OperatingSystem(value) => {
write!(formatter, "unsupported operating system {value}")
}
Self::Architecture(value) => write!(formatter, "unsupported architecture {value}"),
Self::KernelRelease(value) => write!(formatter, "invalid Linux kernel release {value}"),
Self::KernelFloor { major, minor } => write!(
formatter,
"Linux kernel {major}.{minor} is below the required 6.9 floor"
),
}
}
}
impl std::error::Error for PlatformRefusal {}
/// # Errors
/// Refuses unsupported operating systems, architectures, and kernel releases.
pub fn check_platform(observation: &PlatformObservation<'_>) -> Result<(), PlatformRefusal> {
if observation.operating_system != "linux" {
return Err(PlatformRefusal::OperatingSystem(
observation.operating_system.into(),
));
}
if observation.architecture != "x86_64" {
return Err(PlatformRefusal::Architecture(
observation.architecture.into(),
));
}
let (major, minor) = parse_kernel_release(observation.kernel_release)?;
if (major, minor) < (MINIMUM_KERNEL_MAJOR, MINIMUM_KERNEL_MINOR) {
return Err(PlatformRefusal::KernelFloor { major, minor });
}
Ok(())
}
/// # Errors
/// Refuses releases without numeric major and minor components.
pub fn parse_kernel_release(release: &str) -> Result<(u64, u64), PlatformRefusal> {
let mut components = release.split('.');
let major = components
.next()
.and_then(|value| value.parse().ok())
.ok_or_else(|| PlatformRefusal::KernelRelease(release.into()))?;
let minor = components
.next()
.and_then(|value| {
value
.split_once('-')
.map_or(value, |(number, _)| number)
.parse()
.ok()
})
.ok_or_else(|| PlatformRefusal::KernelRelease(release.into()))?;
Ok((major, minor))
}
#[cfg(test)]
mod tests {
use super::{PlatformObservation, PlatformRefusal, check_platform};
#[test]
fn accepts_floor_and_newer_linux_x86_64() {
for release in ["6.9.0", "6.9.12-custom", "6.10.0", "7.0"] {
check_platform(&PlatformObservation {
operating_system: "linux",
architecture: "x86_64",
kernel_release: release,
})
.unwrap();
}
}
#[test]
fn rejects_older_kernel_and_other_platforms() {
let old = check_platform(&PlatformObservation {
operating_system: "linux",
architecture: "x86_64",
kernel_release: "6.8.14",
});
assert_eq!(
old,
Err(PlatformRefusal::KernelFloor { major: 6, minor: 8 })
);
for observation in [
PlatformObservation {
operating_system: "freebsd",
architecture: "x86_64",
kernel_release: "14.0",
},
PlatformObservation {
operating_system: "linux",
architecture: "aarch64",
kernel_release: "6.9.0",
},
] {
assert!(check_platform(&observation).is_err());
}
}
}