aboutsummaryrefslogtreecommitdiff
path: root/rustversion/src/rustc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rustversion/src/rustc.rs')
-rw-r--r--rustversion/src/rustc.rs195
1 files changed, 195 insertions, 0 deletions
diff --git a/rustversion/src/rustc.rs b/rustversion/src/rustc.rs
new file mode 100644
index 0000000..4e7699d
--- /dev/null
+++ b/rustversion/src/rustc.rs
@@ -0,0 +1,195 @@
+use std::env;
+use std::ffi::OsString;
+use std::fmt::{self, Display};
+use std::io;
+use std::process::Command;
+use std::str::FromStr;
+use std::string::FromUtf8Error;
+
+use crate::date::Date;
+use crate::version::{Channel::*, Version};
+use proc_macro2::Span;
+
+#[derive(Debug)]
+pub enum Error {
+ Exec(io::Error),
+ Utf8(FromUtf8Error),
+ Parse(String),
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Error::*;
+
+ match self {
+ Exec(e) => write!(f, "failed to run `rustc --version`: {}", e),
+ Utf8(e) => write!(f, "failed to parse output of `rustc --version`: {}", e),
+ Parse(string) => write!(
+ f,
+ "unexpected output from `rustc --version`, please file an issue: {:?}",
+ string,
+ ),
+ }
+ }
+}
+
+impl From<FromUtf8Error> for Error {
+ fn from(err: FromUtf8Error) -> Self {
+ Error::Utf8(err)
+ }
+}
+
+impl From<Error> for syn::Error {
+ fn from(err: Error) -> Self {
+ syn::Error::new(Span::call_site(), err)
+ }
+}
+
+pub fn version() -> Result<Version> {
+ let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc"));
+ let output = Command::new(rustc)
+ .arg("--version")
+ .output()
+ .map_err(Error::Exec)?;
+ let string = String::from_utf8(output.stdout)?;
+
+ match parse(&string) {
+ Some(version) => Ok(version),
+ None => Err(Error::Parse(string)),
+ }
+}
+
+fn parse(string: &str) -> Option<Version> {
+ let last_line = string.lines().last().unwrap_or(&string);
+ let mut words = last_line.trim().split(' ');
+
+ if words.next()? != "rustc" {
+ return None;
+ }
+
+ let mut version_channel = words.next()?.split('-');
+ let version = version_channel.next()?;
+ let channel = version_channel.next();
+
+ let mut digits = version.split('.');
+ let major = digits.next()?;
+ if major != "1" {
+ return None;
+ }
+ let minor = digits.next()?.parse().ok()?;
+ let patch = digits.next().unwrap_or("0").parse().ok()?;
+
+ let channel = match channel {
+ None => Stable,
+ Some(channel) if channel == "dev" => Dev,
+ Some(channel) if channel.starts_with("beta") => Beta,
+ Some(channel) if channel == "nightly" => {
+ match words.next() {
+ Some(hash) => {
+ if !hash.starts_with('(') {
+ return None;
+ }
+ let date = words.next()?;
+ if !date.ends_with(')') {
+ return None;
+ }
+ let date = Date::from_str(&date[..date.len() - 1]).ok()?;
+ Nightly(date)
+ }
+ None => Dev,
+ }
+ }
+ Some(_) => return None,
+ };
+
+ Some(Version {
+ minor,
+ patch,
+ channel,
+ })
+}
+
+#[test]
+fn test_parse() {
+ let cases = &[
+ (
+ "rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14)",
+ Version {
+ minor: 0,
+ patch: 0,
+ channel: Stable,
+ },
+ ),
+ (
+ "rustc 1.18.0",
+ Version {
+ minor: 18,
+ patch: 0,
+ channel: Stable,
+ },
+ ),
+ (
+ "rustc 1.24.1 (d3ae9a9e0 2018-02-27)",
+ Version {
+ minor: 24,
+ patch: 1,
+ channel: Stable,
+ },
+ ),
+ (
+ "rustc 1.35.0-beta.3 (c13114dc8 2019-04-27)",
+ Version {
+ minor: 35,
+ patch: 0,
+ channel: Beta,
+ },
+ ),
+ (
+ "rustc 1.36.0-nightly (938d4ffe1 2019-04-27)",
+ Version {
+ minor: 36,
+ patch: 0,
+ channel: Nightly(Date {
+ year: 2019,
+ month: 4,
+ day: 27,
+ }),
+ },
+ ),
+ (
+ "rustc 1.36.0-dev",
+ Version {
+ minor: 36,
+ patch: 0,
+ channel: Dev,
+ },
+ ),
+ (
+ "rustc 1.36.0-nightly",
+ Version {
+ minor: 36,
+ patch: 0,
+ channel: Dev,
+ },
+ ),
+ (
+ "warning: invalid logging spec 'warning', ignoring it
+ rustc 1.30.0-nightly (3bc2ca7e4 2018-09-20)",
+ Version {
+ minor: 30,
+ patch: 0,
+ channel: Nightly(Date {
+ year: 2018,
+ month: 9,
+ day: 20,
+ }),
+ },
+ ),
+ ];
+
+ for (string, expected) in cases {
+ assert_eq!(parse(string).unwrap(), *expected);
+ }
+}