()
| 84 | } |
| 85 | |
| 86 | pub fn os_version() -> Option<&'static OSVersion> { |
| 87 | static OS_VERSION: OnceLock<Option<OSVersion>> = OnceLock::new(); |
| 88 | OS_VERSION |
| 89 | .get_or_init(|| { |
| 90 | cfg_if! { |
| 91 | if #[cfg(target_os = "macos")] { |
| 92 | use std::process::Command; |
| 93 | use regex::Regex; |
| 94 | |
| 95 | let version_info = Command::new("sw_vers") |
| 96 | .output() |
| 97 | .ok()?; |
| 98 | |
| 99 | let version_info: String = String::from_utf8_lossy(&version_info.stdout).trim().into(); |
| 100 | |
| 101 | let version_regex = Regex::new(r"ProductVersion:\s*(\S+)").unwrap(); |
| 102 | let build_regex = Regex::new(r"BuildVersion:\s*(\S+)").unwrap(); |
| 103 | |
| 104 | let version: String = version_regex |
| 105 | .captures(&version_info) |
| 106 | .and_then(|c| c.get(1)) |
| 107 | .map(|v| v.as_str().into())?; |
| 108 | |
| 109 | let major = version |
| 110 | .split('.') |
| 111 | .next()? |
| 112 | .parse().ok()?; |
| 113 | |
| 114 | let minor = version |
| 115 | .split('.') |
| 116 | .nth(1)? |
| 117 | .parse().ok()?; |
| 118 | |
| 119 | let patch = version.split('.').nth(2).and_then(|p| p.parse().ok()); |
| 120 | |
| 121 | let build = build_regex |
| 122 | .captures(&version_info) |
| 123 | .and_then(|c| c.get(1))? |
| 124 | .as_str() |
| 125 | .into(); |
| 126 | |
| 127 | Some(OSVersion::MacOS { |
| 128 | major, |
| 129 | minor, |
| 130 | patch, |
| 131 | build, |
| 132 | }) |
| 133 | } else if #[cfg(target_os = "linux")] { |
| 134 | linux::get_os_version() |
| 135 | } else if #[cfg(target_os = "windows")] { |
| 136 | windows::get_os_version() |
| 137 | } else if #[cfg(target_os = "freebsd")] { |
| 138 | use nix::sys::utsname::uname; |
| 139 | |
| 140 | let version = uname().ok()?.release().to_string_lossy().into(); |
| 141 | |
| 142 | Some(OSVersion::FreeBsd { |
| 143 | version, |
no test coverage detected