Skip to content

Commit 2ba693d

Browse files
committed
Make parser more robust to input variations (fixes #11)
1 parent eaf3035 commit 2ba693d

File tree

2 files changed

+48
-70
lines changed

2 files changed

+48
-70
lines changed

src/lib.rs

+48-52
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ extern crate doc_comment;
5656
#[cfg(test)]
5757
doctest!("../README.md");
5858

59+
use std::collections::HashMap;
5960
use std::process::Command;
6061
use std::{env, error, fmt, io, num, str};
6162
use std::{ffi::OsString, str::FromStr};
@@ -206,65 +207,46 @@ pub fn version_meta() -> Result<VersionMeta> {
206207
/// the SemVer version and additional metadata
207208
/// like the git short hash and build date.
208209
pub fn version_meta_for(verbose_version_string: &str) -> Result<VersionMeta> {
209-
let out: Vec<_> = verbose_version_string.lines().collect();
210-
211-
if !(out.len() >= 6 && out.len() <= 8) {
212-
return Err(Error::UnexpectedVersionFormat);
213-
}
214-
215-
let short_version_string = out[0];
216-
217-
#[allow(clippy::manual_strip)]
218-
fn expect_prefix<'a>(line: &'a str, prefix: &str) -> Result<&'a str> {
219-
if line.starts_with(prefix) {
220-
Ok(&line[prefix.len()..])
221-
} else {
222-
Err(Error::UnexpectedVersionFormat)
210+
let mut map = HashMap::new();
211+
for (i, line) in verbose_version_string.lines().enumerate() {
212+
if i == 0 {
213+
map.insert("short", line);
214+
continue;
223215
}
224-
}
225-
226-
let commit_hash = match expect_prefix(out[2], "commit-hash: ")? {
227-
"unknown" => None,
228-
hash => Some(hash.to_owned()),
229-
};
230-
231-
let commit_date = match expect_prefix(out[3], "commit-date: ")? {
232-
"unknown" => None,
233-
hash => Some(hash.to_owned()),
234-
};
235216

236-
// Handle that the build date may or may not be present.
237-
let mut idx = 4;
238-
let mut build_date = None;
239-
if out[idx].starts_with("build-date") {
240-
build_date = match expect_prefix(out[idx], "build-date: ")? {
241-
"unknown" => None,
242-
s => Some(s.to_owned()),
217+
let mut parts = line.splitn(2, ": ");
218+
let key = match parts.next() {
219+
Some(key) => key,
220+
None => continue,
243221
};
244-
idx += 1;
222+
223+
if let Some(value) = parts.next() {
224+
map.insert(key, value);
225+
}
245226
}
246227

247-
let host = expect_prefix(out[idx], "host: ")?;
248-
idx += 1;
249-
let release = expect_prefix(out[idx], "release: ")?;
250-
idx += 1;
228+
let short_version_string = expect_key("short", &map)?;
229+
let host = expect_key("host", &map)?;
230+
let release = expect_key("release", &map)?;
251231
let semver: Version = release.parse()?;
252232

253-
let channel = if semver.pre.is_empty() {
254-
Channel::Stable
255-
} else {
256-
match semver.pre[0] {
257-
Identifier::AlphaNumeric(ref s) if s == "dev" => Channel::Dev,
258-
Identifier::AlphaNumeric(ref s) if s == "beta" => Channel::Beta,
259-
Identifier::AlphaNumeric(ref s) if s == "nightly" => Channel::Nightly,
260-
ref x => return Err(Error::UnknownPreReleaseTag(x.clone())),
261-
}
233+
let channel = match semver.pre.first() {
234+
None => Channel::Stable,
235+
Some(Identifier::AlphaNumeric(s)) if s == "dev" => Channel::Dev,
236+
Some(Identifier::AlphaNumeric(s)) if s == "beta" => Channel::Beta,
237+
Some(Identifier::AlphaNumeric(s)) if s == "nightly" => Channel::Nightly,
238+
Some(x) => return Err(Error::UnknownPreReleaseTag(x.clone())),
262239
};
263240

264-
let llvm_version = if let Some(&line) = out.get(idx) {
265-
Some(expect_prefix(line, "LLVM version: ")?.parse()?)
266-
} else {
267-
None
241+
let commit_hash = expect_key_or_unknown("commit-hash", &map)?;
242+
let commit_date = expect_key_or_unknown("commit-date", &map)?;
243+
let build_date = map
244+
.get("build-date")
245+
.filter(|&v| *v != "unknown")
246+
.map(|&v| String::from(v));
247+
let llvm_version = match map.get("LLVM version") {
248+
Some(&v) => Some(v.parse()?),
249+
None => None,
268250
};
269251

270252
Ok(VersionMeta {
@@ -273,12 +255,26 @@ pub fn version_meta_for(verbose_version_string: &str) -> Result<VersionMeta> {
273255
commit_date,
274256
build_date,
275257
channel,
276-
host: host.into(),
277-
short_version_string: short_version_string.into(),
258+
host,
259+
short_version_string,
278260
llvm_version,
279261
})
280262
}
281263

264+
fn expect_key_or_unknown(key: &str, map: &HashMap<&str, &str>) -> Result<Option<String>, Error> {
265+
match map.get(key) {
266+
Some(&v) if v == "unknown" => Ok(None),
267+
Some(&v) => Ok(Some(String::from(v))),
268+
None => Err(Error::UnexpectedVersionFormat),
269+
}
270+
}
271+
272+
fn expect_key(key: &str, map: &HashMap<&str, &str>) -> Result<String, Error> {
273+
map.get(key)
274+
.map(|&v| String::from(v))
275+
.ok_or(Error::UnexpectedVersionFormat)
276+
}
277+
282278
/// LLVM Version Parse Error
283279
#[derive(Debug)]
284280
pub enum LlvmVersionParseError {

tests/all.rs

-18
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,6 @@ fn smoketest() {
2929
assert!(version().unwrap() >= Version::parse("1.0.0").unwrap());
3030
}
3131

32-
#[test]
33-
fn parse_unexpected() {
34-
let res = version_meta_for(
35-
"rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14)
36-
binary: rustc
37-
commit-hash: a59de37e99060162a2674e3ff45409ac73595c0e
38-
commit-date: 2015-05-13
39-
rust-birthday: 2015-05-14
40-
host: x86_64-unknown-linux-gnu
41-
release: 1.0.0",
42-
);
43-
44-
assert!(match res {
45-
Err(Error::UnexpectedVersionFormat) => true,
46-
_ => false,
47-
});
48-
}
49-
5032
#[test]
5133
fn parse_1_0_0() {
5234
let version = version_meta_for(

0 commit comments

Comments
 (0)