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