Skip to content

Commit

Permalink
Make parser more robust to input variations (fixes #11)
Browse files Browse the repository at this point in the history
  • Loading branch information
djc committed Jan 11, 2021
1 parent 92e72bc commit 9447ae3
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 68 deletions.
93 changes: 43 additions & 50 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ extern crate doc_comment;
#[cfg(test)]
doctest!("../README.md");

use std::collections::HashMap;
use std::process::Command;
use std::{env, error, fmt, io, num, str};
use std::{ffi::OsString, str::FromStr};
Expand Down Expand Up @@ -206,65 +207,43 @@ pub fn version_meta() -> Result<VersionMeta> {
/// the SemVer version and additional metadata
/// like the git short hash and build date.
pub fn version_meta_for(verbose_version_string: &str) -> Result<VersionMeta> {
let out: Vec<_> = verbose_version_string.lines().collect();

if !(out.len() >= 6 && out.len() <= 8) {
return Err(Error::UnexpectedVersionFormat);
}

let short_version_string = out[0];

#[allow(clippy::manual_strip)]
fn expect_prefix<'a>(line: &'a str, prefix: &str) -> Result<&'a str> {
if line.starts_with(prefix) {
Ok(&line[prefix.len()..])
} else {
Err(Error::UnexpectedVersionFormat)
let mut map = HashMap::new();
for (i, line) in verbose_version_string.lines().enumerate() {
if i == 0 {
map.insert("short", line);
continue;
}
}

let commit_hash = match expect_prefix(out[2], "commit-hash: ")? {
"unknown" => None,
hash => Some(hash.to_owned()),
};

let commit_date = match expect_prefix(out[3], "commit-date: ")? {
"unknown" => None,
hash => Some(hash.to_owned()),
};

// Handle that the build date may or may not be present.
let mut idx = 4;
let mut build_date = None;
if out[idx].starts_with("build-date") {
build_date = match expect_prefix(out[idx], "build-date: ")? {
"unknown" => None,
s => Some(s.to_owned()),
let mut parts = line.splitn(2, ": ");
let key = match parts.next() {
Some(key) => key,
None => continue,
};
idx += 1;

if let Some(value) = parts.next() {
map.insert(key, value);
}
}

let host = expect_prefix(out[idx], "host: ")?;
idx += 1;
let release = expect_prefix(out[idx], "release: ")?;
idx += 1;
let short_version_string = expect_key("short", &map)?;
let host = expect_key("host", &map)?;
let release = expect_key("release", &map)?;
let semver: Version = release.parse()?;

let channel = if semver.pre.is_empty() {
Channel::Stable
} else {
match semver.pre[0] {
Identifier::AlphaNumeric(ref s) if s == "dev" => Channel::Dev,
Identifier::AlphaNumeric(ref s) if s == "beta" => Channel::Beta,
Identifier::AlphaNumeric(ref s) if s == "nightly" => Channel::Nightly,
ref x => return Err(Error::UnknownPreReleaseTag(x.clone())),
}
let channel = match semver.pre.first() {
None => Channel::Stable,
Some(Identifier::AlphaNumeric(s)) if s == "dev" => Channel::Dev,
Some(Identifier::AlphaNumeric(s)) if s == "beta" => Channel::Beta,
Some(Identifier::AlphaNumeric(s)) if s == "nightly" => Channel::Nightly,
Some(x) => return Err(Error::UnknownPreReleaseTag(x.clone())),
};

let llvm_version = if let Some(&line) = out.get(idx) {
Some(expect_prefix(line, "LLVM version: ")?.parse()?)
} else {
None
let commit_hash = expect_key_or_unknown("commit-hash", &map)?;
let commit_date = expect_key_or_unknown("commit-date", &map)?;
let build_date = expect_key_or_unknown("build-date", &map).ok().flatten();
let llvm_version = match map.get("LLVM version") {
Some(&v) => Some(v.parse()?),
None => None,
};

Ok(VersionMeta {
Expand All @@ -279,6 +258,20 @@ pub fn version_meta_for(verbose_version_string: &str) -> Result<VersionMeta> {
})
}

fn expect_key_or_unknown(key: &str, map: &HashMap<&str, &str>) -> Result<Option<String>, Error> {
match map.get(key) {
Some(&v) if v == "unknown" => Ok(None),
Some(&v) => Ok(Some(String::from(v))),
None => Err(Error::UnexpectedVersionFormat),
}
}

fn expect_key(key: &str, map: &HashMap<&str, &str>) -> Result<String, Error> {
map.get(key)
.map(|&v| String::from(v))
.ok_or(Error::UnexpectedVersionFormat)
}

/// LLVM Version Parse Error
#[derive(Debug)]
pub enum LlvmVersionParseError {
Expand Down
18 changes: 0 additions & 18 deletions tests/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,6 @@ fn smoketest() {
assert!(version().unwrap() >= Version::parse("1.0.0").unwrap());
}

#[test]
fn parse_unexpected() {
let res = version_meta_for(
"rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14)
binary: rustc
commit-hash: a59de37e99060162a2674e3ff45409ac73595c0e
commit-date: 2015-05-13
rust-birthday: 2015-05-14
host: x86_64-unknown-linux-gnu
release: 1.0.0",
);

assert!(match res {
Err(Error::UnexpectedVersionFormat) => true,
_ => false,
});
}

#[test]
fn parse_1_0_0() {
let version = version_meta_for(
Expand Down

0 comments on commit 9447ae3

Please sign in to comment.