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