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