Skip to content

Commit 1c2fe1e

Browse files
committed
Add helper for parsing macOS version
1 parent 662a75f commit 1c2fe1e

File tree

4 files changed

+88
-19
lines changed

4 files changed

+88
-19
lines changed

Diff for: talpid-platform-metadata/src/command.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
use std::process::Command;
1+
use std::{io, process::Command};
22

33
/// Helper for getting stdout of some command as a String. Ignores the exit code of the command.
4-
pub fn command_stdout_lossy(cmd: &str, args: &[&str]) -> Option<String> {
4+
pub fn command_stdout_lossy(cmd: &str, args: &[&str]) -> io::Result<String> {
55
Command::new(cmd)
66
.args(args)
77
.output()
88
.map(|output| String::from_utf8_lossy(&output.stdout).trim().to_string())
9-
.ok()
109
}

Diff for: talpid-platform-metadata/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ mod imp;
1414
#[path = "android.rs"]
1515
mod imp;
1616

17+
#[cfg(target_os = "macos")]
18+
pub use self::imp::MacosVersion;
1719
#[cfg(windows)]
1820
pub use self::imp::WindowsVersion;
1921
pub use self::imp::{extra_metadata, short_version, version};

Diff for: talpid-platform-metadata/src/linux.rs

+11-9
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,15 @@ fn read_os_release_file() -> Result<String, Option<String>> {
6868
}
6969

7070
fn parse_lsb_release() -> Option<String> {
71-
command_stdout_lossy("lsb_release", &["-ds"]).and_then(|output| {
72-
if output.is_empty() {
73-
None
74-
} else {
75-
Some(output)
76-
}
77-
})
71+
command_stdout_lossy("lsb_release", &["-ds"])
72+
.ok()
73+
.and_then(|output| {
74+
if output.is_empty() {
75+
None
76+
} else {
77+
Some(output)
78+
}
79+
})
7880
}
7981

8082
pub fn extra_metadata() -> impl Iterator<Item = (String, String)> {
@@ -86,7 +88,7 @@ pub fn extra_metadata() -> impl Iterator<Item = (String, String)> {
8688
/// `uname -r` outputs a single line containing only the kernel version:
8789
/// > 5.9.15
8890
fn kernel_version() -> Option<(String, String)> {
89-
let kernel = command_stdout_lossy("uname", &["-r"])?;
91+
let kernel = command_stdout_lossy("uname", &["-r"]).ok()?;
9092
Some(("kernel".to_string(), kernel))
9193
}
9294

@@ -112,7 +114,7 @@ fn wg_version() -> Option<(String, String)> {
112114
/// > systemd 246 (246)
113115
/// > +PAM +AUDIT -SELINUX +IMA +APPARMOR +SMACK -SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT -GNUTLS +ACL
114116
fn systemd_version() -> Option<(String, String)> {
115-
let systemd_version_output = command_stdout_lossy("systemctl", &["--version"])?;
117+
let systemd_version_output = command_stdout_lossy("systemctl", &["--version"]).ok()?;
116118
let version = systemd_version_output.lines().next()?.to_string();
117119
Some(("systemd".to_string(), version))
118120
}

Diff for: talpid-platform-metadata/src/macos.rs

+73-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
mod command;
2+
23
use command::command_stdout_lossy;
4+
use std::io;
35

46
pub fn version() -> String {
5-
let version = run_sw_vers().unwrap_or(String::from("N/A"));
7+
let version = MacosVersion::new()
8+
.map(|version| version.version())
9+
.unwrap_or(String::from("N/A"));
610
format!("macOS {}", version)
711
}
812

913
pub fn short_version() -> String {
10-
let version = run_sw_vers()
11-
.and_then(parse_short_version_output)
12-
.map(|(major, minor)| format!("{}.{}", major, minor))
14+
let version = MacosVersion::new()
15+
.map(|version| version.short_version())
1316
.unwrap_or(String::from("N/A"));
1417
format!("macOS {}", version)
1518
}
@@ -18,14 +21,77 @@ pub fn extra_metadata() -> impl Iterator<Item = (String, String)> {
1821
std::iter::empty()
1922
}
2023

24+
#[derive(Debug, PartialEq)]
25+
pub struct MacosVersion {
26+
raw_version: String,
27+
major: u32,
28+
minor: u32,
29+
patch: u32,
30+
}
31+
32+
impl MacosVersion {
33+
pub fn new() -> Result<MacosVersion, io::Error> {
34+
Self::from_raw_version(&run_sw_vers()?)
35+
}
36+
37+
fn from_raw_version(version_string: &str) -> Result<MacosVersion, io::Error> {
38+
let (major, minor, patch) = parse_version_output(version_string).ok_or(io::Error::new(
39+
io::ErrorKind::InvalidInput,
40+
"Failed to parse raw version string",
41+
))?;
42+
Ok(MacosVersion {
43+
raw_version: version_string.to_owned(),
44+
major,
45+
minor,
46+
patch,
47+
})
48+
}
49+
50+
/// Return the current version as a string (e.g. 14.2.1)
51+
pub fn version(&self) -> String {
52+
self.raw_version.clone()
53+
}
54+
55+
/// Return the current version as a string (e.g. 14.2), not including the patch version
56+
pub fn short_version(&self) -> String {
57+
format!("{}.{}", self.major_version(), self.minor_version())
58+
}
59+
60+
pub fn major_version(&self) -> u32 {
61+
self.major
62+
}
63+
64+
pub fn minor_version(&self) -> u32 {
65+
self.minor
66+
}
67+
68+
pub fn patch_version(&self) -> u32 {
69+
self.patch
70+
}
71+
}
72+
2173
/// Outputs a string in a format `$major.$minor.$patch`, e.g. `11.0.1`
22-
fn run_sw_vers() -> Option<String> {
74+
fn run_sw_vers() -> io::Result<String> {
2375
command_stdout_lossy("sw_vers", &["-productVersion"])
2476
}
2577

26-
fn parse_short_version_output(output: String) -> Option<(u32, u32)> {
78+
fn parse_version_output(output: &str) -> Option<(u32, u32, u32)> {
2779
let mut parts = output.split('.');
2880
let major = parts.next()?.parse().ok()?;
2981
let minor = parts.next()?.parse().ok()?;
30-
Some((major, minor))
82+
let patch = parts
83+
.next()
84+
.and_then(|patch| patch.parse().ok())
85+
.unwrap_or(0);
86+
Some((major, minor, patch))
87+
}
88+
89+
#[test]
90+
fn test_version_parsing() {
91+
// % sw_vers --productVersion
92+
// 14.2.1
93+
let version = MacosVersion::from_raw_version("14.2.1").expect("failed to parse version");
94+
assert_eq!(version.major_version(), 14);
95+
assert_eq!(version.minor_version(), 2);
96+
assert_eq!(version.patch_version(), 1);
3197
}

0 commit comments

Comments
 (0)