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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use regex::Regex;
use std::fs::File;
use std::io::prelude::*;
use std::io::Error;
use utils;

#[derive(Debug, PartialEq)]
pub struct OSRelease {
    pub distro: Option<String>,
    pub version: Option<String>,
}

fn read_file(filename: &str) -> Result<String, Error> {
    let mut file = File::open(filename)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

pub fn retrieve() -> Option<OSRelease> {
    if utils::file_exists("/etc/os-release") {
        if let Ok(release) = read_file("/etc/os-release") {
            Some(parse(release))
        } else {
            None
        }
    } else {
        if let Ok(release) = read_file("/usr/lib/os-release") {
            Some(parse(release))
        } else {
            None
        }
    }
}

pub fn parse(file: String) -> OSRelease {
    let distrib_regex = Regex::new(r#"NAME="(\w+)"#).unwrap();
    let version_regex = Regex::new(r#"VERSION_ID="?([\w\.]+)"#).unwrap();

    let distro = match distrib_regex.captures_iter(&file).next() {
        Some(m) => match m.get(1) {
            Some(distro) => Some(distro.as_str().to_owned()),
            None => None,
        },
        None => None,
    };

    let version = match version_regex.captures_iter(&file).next() {
        Some(m) => match m.get(1) {
            Some(version) => Some(version.as_str().to_owned()),
            None => None,
        },
        None => None,
    };

    OSRelease { distro, version }
}

mod tests {
    use super::*;

    #[test]
    fn parse_ubuntu_18_04_os_release() {
        let sample = "\
        NAME=\"Ubuntu\"\
        VERSION=\"18.04 LTS (Bionic Beaver)\"
        ID=ubuntu
        ID_LIKE=debian
        PRETTY_NAME=\"Ubuntu 18.04 LTS\"\
        VERSION_ID=\"18.04\"\
        HOME_URL=\"https://www.ubuntu.com/\"\
        SUPPORT_URL=\"https://help.ubuntu.com/\"\
        BUG_REPORT_URL=\"https://bugs.launchpad.net/ubuntu\"\
        PRIVACY_POLICY_URL=\"https://www.ubuntu.com/legal/terms-and-policies/privacy-policy\"\
        VERSION_CODENAME=bionic
        UBUNTU_CODENAME=bionic
        "
        .to_string();

        assert_eq!(
            parse(sample),
            OSRelease {
                distro: Some("Ubuntu".to_string()),
                version: Some("18.04".to_string()),
            }
        );
    }

    #[test]
    fn parse_alpine_3_9_5_os_release() {
        let sample = "\
        NAME=\"Alpine Linux\"
        ID=alpine
        VERSION_ID=3.9.5
        PRETTY_NAME=\"Alpine Linux v3.9\"
        HOME_URL=\"https://alpinelinux.org/\"
        BUG_REPORT_URL=\"https://bugs.alpinelinux.org/\"
        "
        .to_string();

        assert_eq!(
            parse(sample),
            OSRelease {
                distro: Some("Alpine".to_string()),
                version: Some("3.9.5".to_string()),
            }
        );
    }

    #[test]
    fn parse_deepin_20_3_os_release() {
        let sample = "\
        PRETTY_NAME=\"Deepin 20.3\"
        NAME=\"Deepin\"
        VERSION_ID=\"20.3\"
        VERSION=\"20.3\"
        ID=Deepin
        HOME_URL=\"https://www.deepin.org/\"
        "
        .to_string();

        assert_eq!(
            parse(sample),
            OSRelease {
                distro: Some("Deepin".to_string()),
                version: Some("20.3".to_string()),
            }
        );
    }

    #[test]
    fn parse_nixos_21_11_os_release() {
        let sample = "\
        NAME=NixOS
        ID=nixos
        VERSION=\"21.11 (Porcupine)\"
        VERSION_CODENAME=porcupine
        VERSION_ID=\"21.11\"
        BUILD_ID=\"21.11.20220325.d89f18a\"
        PRETTY_NAME=\"NixOS 21.11 (Porcupine)\"
        LOGO=\"nix-snowflake\"
        HOME_URL=\"https://nixos.org/\"
        DOCUMENTATION_URL=\"https://nixos.org/learn.html\"
        SUPPORT_URL=\"https://nixos.org/community.html\"
        BUG_REPORT_URL=\"https://github.com/NixOS/nixpkgs/issues\"
        "
        .to_string();

        assert_eq!(
            parse(sample),
            OSRelease {
                distro: Some("NixOS".to_string()),
                version: Some("21.11".to_string()),
            }
        );
    }
}