1f1555e47Sopenharmony_ciuse std::path::Path;
2f1555e47Sopenharmony_ciuse std::process::Command;
3f1555e47Sopenharmony_ciuse std::str;
4f1555e47Sopenharmony_ci
5f1555e47Sopenharmony_ciuse super::{error, Error};
6f1555e47Sopenharmony_ci
7f1555e47Sopenharmony_ci/// A version structure for making relative comparisons.
8f1555e47Sopenharmony_ci#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
9f1555e47Sopenharmony_cipub struct Version {
10f1555e47Sopenharmony_ci    major: usize,
11f1555e47Sopenharmony_ci    minor: usize,
12f1555e47Sopenharmony_ci    patch: usize,
13f1555e47Sopenharmony_ci}
14f1555e47Sopenharmony_ci
15f1555e47Sopenharmony_ciimpl Version {
16f1555e47Sopenharmony_ci    /// Creates a `Version` instance for a specific `major.minor.patch` version.
17f1555e47Sopenharmony_ci    pub fn new(major: usize, minor: usize, patch: usize) -> Self {
18f1555e47Sopenharmony_ci        Version {
19f1555e47Sopenharmony_ci            major: major,
20f1555e47Sopenharmony_ci            minor: minor,
21f1555e47Sopenharmony_ci            patch: patch,
22f1555e47Sopenharmony_ci        }
23f1555e47Sopenharmony_ci    }
24f1555e47Sopenharmony_ci
25f1555e47Sopenharmony_ci    pub fn from_rustc(rustc: &Path) -> Result<Self, Error> {
26f1555e47Sopenharmony_ci        // Get rustc's verbose version
27f1555e47Sopenharmony_ci        let output = try!(Command::new(rustc)
28f1555e47Sopenharmony_ci            .args(&["--version", "--verbose"])
29f1555e47Sopenharmony_ci            .output()
30f1555e47Sopenharmony_ci            .map_err(error::from_io));
31f1555e47Sopenharmony_ci        if !output.status.success() {
32f1555e47Sopenharmony_ci            return Err(error::from_str("could not execute rustc"));
33f1555e47Sopenharmony_ci        }
34f1555e47Sopenharmony_ci        let output = try!(str::from_utf8(&output.stdout).map_err(error::from_utf8));
35f1555e47Sopenharmony_ci
36f1555e47Sopenharmony_ci        // Find the release line in the verbose version output.
37f1555e47Sopenharmony_ci        let release = match output.lines().find(|line| line.starts_with("release: ")) {
38f1555e47Sopenharmony_ci            Some(line) => &line["release: ".len()..],
39f1555e47Sopenharmony_ci            None => return Err(error::from_str("could not find rustc release")),
40f1555e47Sopenharmony_ci        };
41f1555e47Sopenharmony_ci
42f1555e47Sopenharmony_ci        // Strip off any extra channel info, e.g. "-beta.N", "-nightly"
43f1555e47Sopenharmony_ci        let version = match release.find('-') {
44f1555e47Sopenharmony_ci            Some(i) => &release[..i],
45f1555e47Sopenharmony_ci            None => release,
46f1555e47Sopenharmony_ci        };
47f1555e47Sopenharmony_ci
48f1555e47Sopenharmony_ci        // Split the version into semver components.
49f1555e47Sopenharmony_ci        let mut iter = version.splitn(3, '.');
50f1555e47Sopenharmony_ci        let major = try!(iter.next().ok_or(error::from_str("missing major version")));
51f1555e47Sopenharmony_ci        let minor = try!(iter.next().ok_or(error::from_str("missing minor version")));
52f1555e47Sopenharmony_ci        let patch = try!(iter.next().ok_or(error::from_str("missing patch version")));
53f1555e47Sopenharmony_ci
54f1555e47Sopenharmony_ci        Ok(Version::new(
55f1555e47Sopenharmony_ci            try!(major.parse().map_err(error::from_num)),
56f1555e47Sopenharmony_ci            try!(minor.parse().map_err(error::from_num)),
57f1555e47Sopenharmony_ci            try!(patch.parse().map_err(error::from_num)),
58f1555e47Sopenharmony_ci        ))
59f1555e47Sopenharmony_ci    }
60f1555e47Sopenharmony_ci}
61