1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/*
 * Mac OS X related checks
 */
use std::process::Command;
use regex::Regex;

pub struct SwVers {
    pub product_name: Option<String>,
    pub product_version: Option<String>,
    pub build_version: Option<String>
}

fn extract_from_regex(stdout: &String, regex: Regex) -> Option<String> {
    match regex.captures_iter(&stdout).next() {
        Some(m) => {
            match m.get(1) {
                Some(s) => {
                    Some(s.as_str().to_owned())
                },
                None => None
            }
        },
        None => None
    }
}

pub fn is_os_x() -> bool {
    match Command::new("sw_vers").output() {
        Ok(output) => output.status.success(),
        Err(_) => false
    }
}

pub fn retrieve() -> Option<SwVers> {
    let output = match Command::new("sw_vers").output() {
        Ok(output) => output,
        Err(_) => return None
    };

    let stdout = String::from_utf8_lossy(&output.stdout);
    Some(parse(stdout.to_string()))
}

pub fn parse(version_str: String) -> SwVers {
    let product_name_regex = Regex::new(r"ProductName:\s([\w\s]+)\n").unwrap();
    let product_version_regex = Regex::new(r"ProductVersion:\s(\w+\.\w+\.\w+)").unwrap();
    let build_number_regex = Regex::new(r"BuildVersion:\s(\w+)").unwrap();

    SwVers {
        product_name: extract_from_regex(&version_str, product_name_regex),
        product_version: extract_from_regex(&version_str, product_version_regex),
        build_version: extract_from_regex(&version_str, build_number_regex),
    }
}