Skip to content

Commit 4fc3033

Browse files
committed
Make parser more robust to input variations (fixes #11)
1 parent 92e72bc commit 4fc3033

File tree

2 files changed

+45
-70
lines changed

2 files changed

+45
-70
lines changed

src/lib.rs

+45-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,43 @@ 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 = expect_key_or_unknown("build-date", &map).ok().flatten();
244+
let llvm_version = match map.get("LLVM version") {
245+
Some(&v) => Some(v.parse()?),
246+
None => None,
268247
};
269248

270249
Ok(VersionMeta {
@@ -273,12 +252,26 @@ pub fn version_meta_for(verbose_version_string: &str) -> Result<VersionMeta> {
273252
commit_date,
274253
build_date,
275254
channel,
276-
host: host.into(),
277-
short_version_string: short_version_string.into(),
255+
host,
256+
short_version_string,
278257
llvm_version,
279258
})
280259
}
281260

261+
fn expect_key_or_unknown(key: &str, map: &HashMap<&str, &str>) -> Result<Option<String>, Error> {
262+
match map.get(key) {
263+
Some(&v) if v == "unknown" => Ok(None),
264+
Some(&v) => Ok(Some(String::from(v))),
265+
None => Err(Error::UnexpectedVersionFormat),
266+
}
267+
}
268+
269+
fn expect_key(key: &str, map: &HashMap<&str, &str>) -> Result<String, Error> {
270+
map.get(key)
271+
.map(|&v| String::from(v))
272+
.ok_or(Error::UnexpectedVersionFormat)
273+
}
274+
282275
/// LLVM Version Parse Error
283276
#[derive(Debug)]
284277
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)