use std::sync::OnceLock;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Distro {
FedoraAtomic,
Ubuntu,
Unknown,
}
impl Distro {
pub fn as_str(self) -> &'static str {
match self {
Distro::FedoraAtomic => "Fedora Atomic",
Distro::Ubuntu => "Ubuntu",
Distro::Unknown => "Unknown",
}
}
}
static CURRENT: OnceLock<Distro> = OnceLock::new();
pub fn current() -> Distro {
*CURRENT.get_or_init(detect)
}
fn detect() -> Distro {
let content = std::fs::read_to_string("/etc/os-release").unwrap_or_default();
let id = parse_os_release_field(&content, "ID").unwrap_or_default();
let variant_id = parse_os_release_field(&content, "VARIANT_ID").unwrap_or_default();
match id.as_str() {
"fedora"
if matches!(
variant_id.as_str(),
"silverblue" | "kinoite" | "sericea" | "onyx" | "cosmic-atomic"
) =>
{
Distro::FedoraAtomic
}
"ubuntu" => Distro::Ubuntu,
_ => Distro::Unknown,
}
}
fn parse_os_release_field(content: &str, key: &str) -> Option<String> {
for line in content.lines() {
if let Some(rest) = line.strip_prefix(&format!("{key}=")) {
return Some(rest.trim_matches('"').to_string());
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_unquoted_id() {
let content = "ID=fedora\nVARIANT_ID=silverblue\n";
assert_eq!(
parse_os_release_field(content, "ID"),
Some("fedora".to_string())
);
assert_eq!(
parse_os_release_field(content, "VARIANT_ID"),
Some("silverblue".to_string())
);
}
#[test]
fn parse_quoted_id() {
let content = "ID=\"ubuntu\"\nVERSION_ID=\"24.04\"\n";
assert_eq!(
parse_os_release_field(content, "ID"),
Some("ubuntu".to_string())
);
}
#[test]
fn parse_returns_none_for_missing_key() {
let content = "ID=fedora\n";
assert_eq!(parse_os_release_field(content, "VARIANT_ID"), None);
}
#[test]
fn detect_fedora_atomic_silverblue() {
let content = "ID=fedora\nVARIANT_ID=silverblue\n";
let id = parse_os_release_field(content, "ID").unwrap_or_default();
let variant = parse_os_release_field(content, "VARIANT_ID").unwrap_or_default();
assert_eq!(id.as_str(), "fedora");
assert!(matches!(
variant.as_str(),
"silverblue" | "kinoite" | "sericea" | "onyx" | "cosmic-atomic"
));
}
#[test]
fn detect_ubuntu() {
let content = "ID=ubuntu\nVERSION_ID=\"24.04\"\n";
let id = parse_os_release_field(content, "ID").unwrap_or_default();
assert_eq!(id.as_str(), "ubuntu");
}
#[test]
fn distro_as_str() {
assert_eq!(Distro::FedoraAtomic.as_str(), "Fedora Atomic");
assert_eq!(Distro::Ubuntu.as_str(), "Ubuntu");
assert_eq!(Distro::Unknown.as_str(), "Unknown");
}
}