use std::{path::PathBuf, sync::LazyLock};
use regex::Regex;
use super::DownloaderError;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum LinuxVersionSignature {
Ubuntu(UbuntuVersionSignature),
}
impl LinuxVersionSignature {
pub fn subdirectory(&self) -> PathBuf {
match self {
Self::Ubuntu(signature) => signature.subdirectory(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UbuntuVersionSignature {
pub release: String,
pub revision: String,
pub kernel_flavour: String,
pub mainline_kernel_version: String,
}
impl UbuntuVersionSignature {
pub fn revision_short(&self) -> &str {
match self.revision.split_once('.') {
Some((revision_short, _)) => revision_short,
None => &self.revision,
}
}
pub fn kernel_release(&self) -> String {
format!(
"{}-{}-{}",
self.release,
self.revision_short(),
self.kernel_flavour
)
}
pub fn kernel_version(&self) -> String {
format!("{}-{}", self.release, self.revision)
}
pub fn subdirectory(&self) -> PathBuf {
PathBuf::from(format!("{}-{}", self.kernel_version(), self.kernel_flavour))
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LinuxBanner {
pub uts_release: String,
pub linux_compile_by: String,
pub linux_compile_host: String,
pub linux_compiler: String,
pub uts_version: String,
pub version_signature: Option<LinuxVersionSignature>,
}
impl std::str::FromStr for LinuxBanner {
type Err = DownloaderError;
fn from_str(banner: &str) -> Result<Self, Self::Err> {
static LINUX_VERSION_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(concat!(
r"Linux version (?<UTS_RELEASE>[0-9]+\.[0-9]+\.[0-9]+[^ ]*) ",
r"\((?<LINUX_COMPILE_BY>[^@]*)@(?<LINUX_COMPILE_HOST>[^)]*)\) ",
r"\((?<LINUX_COMPILER>.*)\) ",
r"#(?<UTS_VERSION>.*)"
))
.unwrap()
});
let captures = LINUX_VERSION_REGEX
.captures(banner)
.ok_or(DownloaderError::InvalidBanner)?;
let version_signature = try_parse_ubuntu_signature(&captures["UTS_VERSION"]);
Ok(Self {
uts_release: captures["UTS_RELEASE"].to_string(),
linux_compile_by: captures["LINUX_COMPILE_BY"].to_string(),
linux_compile_host: captures["LINUX_COMPILE_HOST"].to_string(),
linux_compiler: captures["LINUX_COMPILER"].to_string(),
uts_version: captures["UTS_VERSION"].to_string(),
version_signature,
})
}
}
fn try_parse_ubuntu_signature(uts_version: &str) -> Option<LinuxVersionSignature> {
static UBUNTU_VERSION_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(concat!(
r"\(Ubuntu ",
r"(?<UBUNTU_RELEASE>.*)-(?<UBUNTU_REVISION>.*)-(?<UBUNTU_KERNEL_FLAVOUR>.*) ",
r"(?<UBUNTU_MAINLINE_KERNEL_VERSION>.*)\)"
))
.unwrap()
});
let captures = UBUNTU_VERSION_REGEX.captures(uts_version)?;
Some(LinuxVersionSignature::Ubuntu(UbuntuVersionSignature {
release: captures["UBUNTU_RELEASE"].into(),
revision: captures["UBUNTU_REVISION"].into(),
kernel_flavour: captures["UBUNTU_KERNEL_FLAVOUR"].into(),
mainline_kernel_version: captures["UBUNTU_MAINLINE_KERNEL_VERSION"].into(),
}))
}