use std::env;
use crate::error::*;
#[derive(Clone, Copy, Debug, PartialEq)]
enum FlagType {
Pass(bool),
Drop(bool),
Error(&'static str),
}
static KNOWN_FLAGS: &[(&str, FlagType)] = &[
("--bench", FlagType::Pass(false)),
("--color", FlagType::Pass(true)),
("--ensure-time", FlagType::Drop(false)),
("--exact", FlagType::Drop(false)),
("--exclude-should-panic", FlagType::Pass(false)),
("--force-run-in-process", FlagType::Pass(false)),
("--format", FlagType::Drop(true)),
("--help", FlagType::Error("Tests run but --help passed to process?")),
("--ignored", FlagType::Pass(false)),
("--include-ignored", FlagType::Pass(false)),
("--list", FlagType::Error("Tests run but --list passed to process?")),
("--logfile", FlagType::Drop(true)),
("--nocapture", FlagType::Drop(true)),
("--quiet", FlagType::Drop(false)),
("--report-time", FlagType::Drop(true)),
("--show-output", FlagType::Pass(false)),
("--skip", FlagType::Drop(true)),
("--test", FlagType::Pass(false)),
("--test-threads", FlagType::Drop(true)),
("-Z", FlagType::Pass(true)),
("-h", FlagType::Error("Tests run but -h passed to process?")),
("-q", FlagType::Drop(false)),
];
fn look_up_flag_from_table(flag: &str) -> Option<FlagType> {
KNOWN_FLAGS.iter().cloned().filter(|&(name, _)| name == flag)
.map(|(_, typ)| typ).next()
}
pub(crate) fn env_var_for_flag(flag: &str) -> String {
let mut var = "RUSTY_FORK_FLAG_".to_owned();
var.push_str(
&flag.trim_start_matches('-').to_uppercase().replace('-', "_"));
var
}
fn look_up_flag_from_env(flag: &str) -> Option<FlagType> {
env::var(&env_var_for_flag(flag)).ok().map(
|value| match &*value {
"pass" => FlagType::Pass(false),
"pass-arg" => FlagType::Pass(true),
"drop" => FlagType::Drop(false),
"drop-arg" => FlagType::Drop(true),
_ => FlagType::Error("incorrect flag type in environment; \
must be one of `pass`, `pass-arg`, \
`drop`, `drop-arg`"),
})
}
fn look_up_flag(flag: &str) -> Option<FlagType> {
look_up_flag_from_table(flag).or_else(|| look_up_flag_from_env(flag))
}
fn look_up_flag_or_err(flag: &str) -> Result<(bool, bool)> {
match look_up_flag(flag) {
None =>
Err(Error::UnknownFlag(flag.to_owned())),
Some(FlagType::Error(message)) =>
Err(Error::DisallowedFlag(flag.to_owned(), message.to_owned())),
Some(FlagType::Pass(has_arg)) => Ok((true, has_arg)),
Some(FlagType::Drop(has_arg)) => Ok((false, has_arg)),
}
}
pub(crate) fn strip_cmdline<A : Iterator<Item = String>>
(args: A) -> Result<Vec<String>>
{
#[derive(Clone, Copy)]
enum State {
Ground, PassingArg, DroppingArg,
}
let mut state = State::DroppingArg;
let mut ret = Vec::new();
for arg in args {
match state {
State::DroppingArg => {
state = State::Ground;
},
State::PassingArg => {
ret.push(arg);
state = State::Ground;
},
State::Ground => {
if &arg == "--" {
break;
} else if &arg == "-" {
continue;
} else if arg.starts_with("--") {
let (pass, has_arg) = look_up_flag_or_err(
arg.split('=').next().expect("split returned empty"))?;
let has_arg = has_arg && !arg.contains('=');
if pass {
ret.push(arg);
if has_arg {
state = State::PassingArg;
}
} else if has_arg {
state = State::DroppingArg;
}
} else if arg.starts_with("-") {
let mut chars = arg.chars();
let mut to_pass = "-".to_owned();
chars.next(); while let Some(flag_ch) = chars.next() {
let flag = format!("-{}", flag_ch);
let (pass, has_arg) = look_up_flag_or_err(&flag)?;
if pass {
to_pass.push(flag_ch);
if has_arg {
if chars.clone().next().is_some() {
to_pass.extend(chars);
} else {
state = State::PassingArg;
}
break;
}
} else if has_arg {
if chars.clone().next().is_none() {
state = State::DroppingArg;
}
break;
}
}
if "-" != &to_pass {
ret.push(to_pass);
}
} else {
}
},
}
}
Ok(ret)
}
pub(crate) static RUN_TEST_ARGS: &[&str] = &[
"--quiet",
"--test-threads", "1",
"--nocapture",
"--exact",
"--",
];
#[cfg(test)]
mod test {
use super::*;
fn strip(cmdline: &str) -> Result<String> {
strip_cmdline(cmdline.split_whitespace().map(|s| s.to_owned()))
.map(|strs| strs.join(" "))
}
#[test]
fn test_strip() {
assert_eq!("", &strip("test").unwrap());
assert_eq!("--ignored", &strip("test --ignored").unwrap());
assert_eq!("", &strip("test --quiet").unwrap());
assert_eq!("", &strip("test -q").unwrap());
assert_eq!("", &strip("test -qq").unwrap());
assert_eq!("", &strip("test --test-threads 42").unwrap());
assert_eq!("-Z unstable-options",
&strip("test -Z unstable-options").unwrap());
assert_eq!("-Zunstable-options",
&strip("test -Zunstable-options").unwrap());
assert_eq!("-Zunstable-options",
&strip("test -qZunstable-options").unwrap());
assert_eq!("--color auto", &strip("test --color auto").unwrap());
assert_eq!("--color=auto", &strip("test --color=auto").unwrap());
assert_eq!("", &strip("test filter filter2").unwrap());
assert_eq!("", &strip("test -- --color=auto").unwrap());
match strip("test --plugh").unwrap_err() {
Error::UnknownFlag(ref flag) => assert_eq!("--plugh", flag),
e => panic!("Unexpected error: {}", e),
}
match strip("test --help").unwrap_err() {
Error::DisallowedFlag(ref flag, _) => assert_eq!("--help", flag),
e => panic!("Unexpected error: {}", e),
}
}
rusty_fork_test! {
#[test]
fn define_args_via_env() {
env::set_var("RUSTY_FORK_FLAG_X", "pass");
env::set_var("RUSTY_FORK_FLAG_FOO", "pass-arg");
env::set_var("RUSTY_FORK_FLAG_BAR", "drop");
env::set_var("RUSTY_FORK_FLAG_BAZ", "drop-arg");
assert_eq!("-X", &strip("test -X foo").unwrap());
assert_eq!("--foo bar", &strip("test --foo bar").unwrap());
assert_eq!("", &strip("test --bar").unwrap());
assert_eq!("", &strip("test --baz --notaflag").unwrap());
}
}
}