1use anyhow::{Result, bail};
2
3pub async fn detect_os_id() -> Result<String> {
6 let os_release = tokio::fs::read_to_string("/etc/os-release").await.ok();
7 detect_os_id_from(std::env::consts::OS, os_release.as_deref())
8}
9
10fn detect_os_id_from(os: &str, os_release: Option<&str>) -> Result<String> {
11 match os {
12 "macos" => Ok("macos".to_string()),
13 "windows" => Ok("windows".to_string()),
14 "linux" => {
15 if let Some(content) = os_release {
16 for line in content.lines() {
17 if let Some(id) = line.strip_prefix("ID=") {
18 return Ok(id.trim_matches('"').to_lowercase());
19 }
20 }
21 }
22 bail!(
23 "Could not detect Linux distribution. \
24 Expected ID= in /etc/os-release. Supported: ubuntu"
25 )
26 }
27 other => bail!(
28 "Unsupported platform '{}'. Supported targets: ubuntu (Linux), macos, windows",
29 other
30 ),
31 }
32}
33
34#[cfg(test)]
35mod tests {
36 use super::*;
37
38 #[test]
39 fn detects_macos() {
40 let result = detect_os_id_from("macos", None).unwrap();
41 assert_eq!(result, "macos");
42 }
43
44 #[test]
45 fn detects_windows() {
46 let result = detect_os_id_from("windows", None).unwrap();
47 assert_eq!(result, "windows");
48 }
49
50 #[test]
51 fn detects_ubuntu_from_os_release() {
52 let os_release = "PRETTY_NAME=\"Ubuntu 22.04\"\nID=ubuntu\nVERSION_ID=\"22.04\"\n";
53 let result = detect_os_id_from("linux", Some(os_release)).unwrap();
54 assert_eq!(result, "ubuntu");
55 }
56
57 #[test]
58 fn detects_quoted_id() {
59 let os_release = "ID=\"ubuntu\"\n";
60 let result = detect_os_id_from("linux", Some(os_release)).unwrap();
61 assert_eq!(result, "ubuntu");
62 }
63
64 #[test]
65 fn lowercases_id() {
66 let os_release = "ID=Debian\n";
67 let result = detect_os_id_from("linux", Some(os_release)).unwrap();
68 assert_eq!(result, "debian");
69 }
70
71 #[test]
72 fn errors_on_linux_without_os_release() {
73 let err = detect_os_id_from("linux", None).unwrap_err();
74 assert!(err.to_string().contains("os-release"));
75 }
76
77 #[test]
78 fn errors_on_unsupported_platform() {
79 let err = detect_os_id_from("freebsd", None).unwrap_err();
80 assert!(err.to_string().contains("freebsd"));
81 }
82}