os-version 0.2.0

Get the operating system version
Documentation
use anyhow::Result;

#[derive(Debug, PartialEq)]
pub struct MacOS {
    pub version: String,
}

impl MacOS {
    #[cfg(target_os = "macos")]
    pub fn detect() -> Result<MacOS> {
        let file = std::fs::read_to_string("/System/Library/CoreServices/SystemVersion.plist")?;
        parse(&file)
    }

    #[cfg(not(target_os = "macos"))]
    pub fn detect() -> Result<MacOS> {
        unreachable!()
    }
}

impl ToString for MacOS {
    fn to_string(&self) -> String {
        format!("macOS {}", self.version)
    }
}

#[cfg(target_os = "macos")]
fn parse(file: &str) -> Result<MacOS> {
    use anyhow::Error;

    let cur = std::io::Cursor::new(file.as_bytes());
    let v = plist::Value::from_reader(cur)?;

    let version = v
        .as_dictionary()
        .ok_or_else(|| Error::msg("SystemVersion.plist is not a dictionary"))?
        .get("ProductVersion")
        .ok_or_else(|| Error::msg("ProductVersion is missing"))?;

    let version = version
        .as_string()
        .ok_or_else(|| Error::msg("Version is not a string"))?
        .to_string();

    Ok(MacOS { version })
}

#[cfg(test)]
mod tests {
    #[cfg(target_os = "macos")]
    use super::*;

    #[test]
    #[cfg(target_os = "macos")]
    fn detect_macos() {
        let version = parse(r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>ProductBuildVersion</key>
    <string>17G11023</string>
    <key>ProductCopyright</key>
    <string>1983-2020 Apple Inc.</string>
    <key>ProductName</key>
    <string>Mac OS X</string>
    <key>ProductUserVisibleVersion</key>
    <string>10.13.6</string>
    <key>ProductVersion</key>
    <string>10.13.6</string>
</dict>
</plist>
"#).unwrap();
        assert_eq!(
            MacOS {
                version: "10.13.6".to_string(),
            },
            version
        );
    }
}