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 51 .next() 52 .ok_or_else(|| error::from_str("missing major version"))); 53 let minor = try!(iter 54 .next() 55 .ok_or_else(|| error::from_str("missing minor version"))); 56 let patch = try!(iter 57 .next() 58 .ok_or_else(|| error::from_str("missing patch version"))); 59 60 Ok(Version::new( 61 try!(major.parse().map_err(error::from_num)), 62 try!(minor.parse().map_err(error::from_num)), 63 try!(patch.parse().map_err(error::from_num)), 64 )) 65 } 66 } 67