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