Skip to main content

cmdhub_cli/
os_detector.rs

1use std::path::Path;
2
3pub fn detect_os() -> Option<String> {
4    if std::env::consts::OS == "macos" {
5        return Some("macos".to_string());
6    }
7    if std::env::consts::OS == "windows" {
8        return Some("windows".to_string());
9    }
10    parse_os_release(Path::new("/etc/os-release"))
11}
12
13fn parse_os_release(path: &Path) -> Option<String> {
14    let content = std::fs::read_to_string(path).ok()?;
15    let mut id = None;
16    let mut id_like = None;
17
18    for line in content.lines() {
19        let trimmed = line.trim();
20        if let Some(val) = trimmed.strip_prefix("ID=") {
21            id = Some(strip_quotes(val));
22        } else if let Some(val) = trimmed.strip_prefix("ID_LIKE=") {
23            id_like = Some(strip_quotes(val));
24        }
25    }
26
27    if let Some(ref val) = id {
28        if is_recognized_os(val) {
29            return Some(val.clone());
30        }
31    }
32
33    if let Some(ref val) = id_like {
34        for word in val.split_whitespace() {
35            if is_recognized_os(word) {
36                return Some(word.to_string());
37            }
38        }
39    }
40    id
41}
42
43fn strip_quotes(s: &str) -> String {
44    s.trim_matches(|c| c == '"' || c == '\'').to_string()
45}
46
47fn is_recognized_os(s: &str) -> bool {
48    matches!(
49        s,
50        "macos"
51            | "windows"
52            | "arch"
53            | "ubuntu"
54            | "debian"
55            | "fedora"
56            | "centos"
57            | "rhel"
58            | "gentoo"
59            | "alpine"
60            | "opensuse"
61            | "nixos"
62    )
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use std::io::Write;
69
70    #[test]
71    fn test_os_release_parsing() {
72        let tmp = tempfile::NamedTempFile::new().unwrap();
73        write!(
74            tmp.as_file(),
75            "NAME=\"Ubuntu\"\nID=\"ubuntu\"\nID_LIKE=\"debian\"\n"
76        )
77        .unwrap();
78        assert_eq!(parse_os_release(tmp.path()), Some("ubuntu".to_string()));
79
80        let tmp_mint = tempfile::NamedTempFile::new().unwrap();
81        write!(
82            tmp_mint.as_file(),
83            "NAME=\"Linux Mint\"\nID=linuxmint\nID_LIKE=\"ubuntu debian\"\n"
84        )
85        .unwrap();
86        assert_eq!(
87            parse_os_release(tmp_mint.path()),
88            Some("ubuntu".to_string())
89        );
90    }
91}