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