1 use std::path::Path;
2 use std::process::Command;
3 use std::str;
4 
5 use super::{error, Error};
6 
7 /// A version structure for making relative comparisons.
8 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
9 pub struct Version {
10     major: usize,
11     minor: usize,
12     patch: usize,
13 }
14 
15 impl Version {
16     /// Creates a `Version` instance for a specific `major.minor.patch` version.
new(major: usize, minor: usize, patch: usize) -> Self17     pub fn new(major: usize, minor: usize, patch: usize) -> Self {
18         Version {
19             major: major,
20             minor: minor,
21             patch: patch,
22         }
23     }
24 
from_rustc(rustc: &Path) -> Result<Self, Error>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