1use std::{path::PathBuf, sync::LazyLock};
2
3use regex::Regex;
4
5use super::DownloaderError;
6
7#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub enum LinuxVersionSignature {
10 Ubuntu(UbuntuVersionSignature),
12}
13
14impl LinuxVersionSignature {
15 pub fn subdirectory(&self) -> PathBuf {
17 match self {
18 Self::Ubuntu(signature) => signature.subdirectory(),
19 }
20 }
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub struct UbuntuVersionSignature {
26 pub release: String,
28
29 pub revision: String,
31
32 pub kernel_flavour: String,
34
35 pub mainline_kernel_version: String,
37}
38
39impl UbuntuVersionSignature {
57 pub fn revision_short(&self) -> &str {
59 match self.revision.split_once('.') {
60 Some((revision_short, _)) => revision_short,
61 None => &self.revision,
62 }
63 }
64
65 pub fn kernel_release(&self) -> String {
67 format!(
68 "{}-{}-{}",
69 self.release,
70 self.revision_short(),
71 self.kernel_flavour
72 )
73 }
74
75 pub fn kernel_version(&self) -> String {
77 format!("{}-{}", self.release, self.revision)
78 }
79
80 pub fn subdirectory(&self) -> PathBuf {
82 PathBuf::from(format!("{}-{}", self.kernel_version(), self.kernel_flavour))
83 }
84}
85
86#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
88pub struct LinuxBanner {
89 pub uts_release: String,
91
92 pub linux_compile_by: String,
94
95 pub linux_compile_host: String,
97
98 pub linux_compiler: String,
100
101 pub uts_version: String,
103
104 pub version_signature: Option<LinuxVersionSignature>,
106}
107
108impl std::str::FromStr for LinuxBanner {
111 type Err = DownloaderError;
112
113 fn from_str(banner: &str) -> Result<Self, Self::Err> {
114 static LINUX_VERSION_REGEX: LazyLock<Regex> = LazyLock::new(|| {
122 Regex::new(concat!(
123 r"Linux version (?<UTS_RELEASE>[0-9]+\.[0-9]+\.[0-9]+[^ ]*) ",
124 r"\((?<LINUX_COMPILE_BY>[^@]*)@(?<LINUX_COMPILE_HOST>[^)]*)\) ",
125 r"\((?<LINUX_COMPILER>.*)\) ",
126 r"#(?<UTS_VERSION>.*)"
127 ))
128 .unwrap()
129 });
130
131 let captures = LINUX_VERSION_REGEX
132 .captures(banner)
133 .ok_or(DownloaderError::InvalidBanner)?;
134
135 let version_signature = try_parse_ubuntu_signature(&captures["UTS_VERSION"]);
136
137 Ok(Self {
138 uts_release: captures["UTS_RELEASE"].to_string(),
139 linux_compile_by: captures["LINUX_COMPILE_BY"].to_string(),
140 linux_compile_host: captures["LINUX_COMPILE_HOST"].to_string(),
141 linux_compiler: captures["LINUX_COMPILER"].to_string(),
142 uts_version: captures["UTS_VERSION"].to_string(),
143 version_signature,
144 })
145 }
146}
147
148fn try_parse_ubuntu_signature(uts_version: &str) -> Option<LinuxVersionSignature> {
149 static UBUNTU_VERSION_REGEX: LazyLock<Regex> = LazyLock::new(|| {
154 Regex::new(concat!(
155 r"\(Ubuntu ",
156 r"(?<UBUNTU_RELEASE>.*)-(?<UBUNTU_REVISION>.*)-(?<UBUNTU_KERNEL_FLAVOUR>.*) ",
157 r"(?<UBUNTU_MAINLINE_KERNEL_VERSION>.*)\)"
158 ))
159 .unwrap()
160 });
161
162 let captures = UBUNTU_VERSION_REGEX.captures(uts_version)?;
163
164 Some(LinuxVersionSignature::Ubuntu(UbuntuVersionSignature {
165 release: captures["UBUNTU_RELEASE"].into(),
166 revision: captures["UBUNTU_REVISION"].into(),
167 kernel_flavour: captures["UBUNTU_KERNEL_FLAVOUR"].into(),
168 mainline_kernel_version: captures["UBUNTU_MAINLINE_KERNEL_VERSION"].into(),
169 }))
170}