Split options parsing into several functions
This commit is contained in:
parent
12397e9dd5
commit
ddc6a5fd0e
1 changed files with 199 additions and 143 deletions
|
|
@ -40,7 +40,7 @@ impl TestOpts {
|
|||
/// Result of parsing the options.
|
||||
pub type OptRes = Result<TestOpts, String>;
|
||||
/// Result of parsing the option part.
|
||||
type OptPartRes<T> = Result<Option<T>, String>;
|
||||
type OptPartRes<T> = Result<T, String>;
|
||||
|
||||
fn optgroups() -> getopts::Options {
|
||||
let mut opts = getopts::Options::new();
|
||||
|
|
@ -186,6 +186,96 @@ Test Attributes:
|
|||
);
|
||||
}
|
||||
|
||||
/// Parses command line arguments into test options.
|
||||
/// Returns `None` if help was requested (since we only show help message and don't run tests),
|
||||
/// returns `Some(Err(..))` if provided arguments are incorrect,
|
||||
/// otherwise creates a `TestOpts` object and returns it.
|
||||
pub fn parse_opts(args: &[String]) -> Option<OptRes> {
|
||||
// Parse matches.
|
||||
let opts = optgroups();
|
||||
let args = args.get(1..).unwrap_or(args);
|
||||
let matches = match opts.parse(args) {
|
||||
Ok(m) => m,
|
||||
Err(f) => return Some(Err(f.to_string())),
|
||||
};
|
||||
|
||||
// Check if help was requested.
|
||||
if matches.opt_present("h") {
|
||||
// Show help and do nothing more.
|
||||
usage(&args[0], &opts);
|
||||
return None;
|
||||
}
|
||||
|
||||
// Actually parse the opts.
|
||||
let opts_result = parse_opts_impl(matches);
|
||||
|
||||
Some(opts_result)
|
||||
}
|
||||
|
||||
// Gets the option value and checks if unstable features are enabled.
|
||||
macro_rules! unstable_optflag {
|
||||
($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
|
||||
let opt = $matches.opt_present($option_name);
|
||||
if !$allow_unstable && opt {
|
||||
return Err(format!(
|
||||
"The \"{}\" flag is only accepted on the nightly compiler",
|
||||
$option_name
|
||||
));
|
||||
}
|
||||
|
||||
opt
|
||||
}};
|
||||
}
|
||||
|
||||
// Implementation of `parse_opts` that doesn't care about help message
|
||||
// and returns a `Result`.
|
||||
fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
|
||||
let allow_unstable = get_allow_unstable(&matches)?;
|
||||
|
||||
// Unstable flags
|
||||
let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
|
||||
let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored");
|
||||
let time_options = get_time_options(&matches, allow_unstable)?;
|
||||
|
||||
let quiet = matches.opt_present("quiet");
|
||||
let exact = matches.opt_present("exact");
|
||||
let list = matches.opt_present("list");
|
||||
let skip = matches.opt_strs("skip");
|
||||
|
||||
let bench_benchmarks = matches.opt_present("bench");
|
||||
let run_tests = !bench_benchmarks || matches.opt_present("test");
|
||||
|
||||
let logfile = get_log_file(&matches)?;
|
||||
let run_ignored = get_run_ignored(&matches, include_ignored)?;
|
||||
let filter = get_filter(&matches)?;
|
||||
let nocapture = get_nocapture(&matches)?;
|
||||
let test_threads = get_test_threads(&matches)?;
|
||||
let color = get_color_config(&matches)?;
|
||||
let format = get_format(&matches, quiet, allow_unstable)?;
|
||||
|
||||
let options = Options::new().display_output(matches.opt_present("show-output"));
|
||||
|
||||
let test_opts = TestOpts {
|
||||
list,
|
||||
filter,
|
||||
filter_exact: exact,
|
||||
exclude_should_panic,
|
||||
run_ignored,
|
||||
run_tests,
|
||||
bench_benchmarks,
|
||||
logfile,
|
||||
nocapture,
|
||||
color,
|
||||
format,
|
||||
test_threads,
|
||||
skip,
|
||||
time_options,
|
||||
options,
|
||||
};
|
||||
|
||||
Ok(test_opts)
|
||||
}
|
||||
|
||||
// FIXME: Copied from libsyntax until linkage errors are resolved. Issue #47566
|
||||
fn is_nightly() -> bool {
|
||||
// Whether this is a feature-staged build, i.e., on the beta or stable channel
|
||||
|
|
@ -196,26 +286,11 @@ fn is_nightly() -> bool {
|
|||
bootstrap || !disable_unstable_features
|
||||
}
|
||||
|
||||
// Gets the option value and checks if unstable features are enabled.
|
||||
macro_rules! unstable_optflag {
|
||||
($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
|
||||
let opt = $matches.opt_present($option_name);
|
||||
if !$allow_unstable && opt {
|
||||
return Some(Err(format!(
|
||||
"The \"{}\" flag is only accepted on the nightly compiler",
|
||||
$option_name
|
||||
)));
|
||||
}
|
||||
|
||||
opt
|
||||
}};
|
||||
}
|
||||
|
||||
// Gets the CLI options assotiated with `report-time` feature.
|
||||
fn get_time_options(
|
||||
matches: &getopts::Matches,
|
||||
allow_unstable: bool)
|
||||
-> Option<OptPartRes<TestTimeOptions>> {
|
||||
-> OptPartRes<Option<TestTimeOptions>> {
|
||||
let report_time = unstable_optflag!(matches, allow_unstable, "report-time");
|
||||
let colored_opt_str = matches.opt_str("report-time");
|
||||
let mut report_time_colored = report_time && colored_opt_str == Some("colored".into());
|
||||
|
|
@ -232,71 +307,73 @@ fn get_time_options(
|
|||
None
|
||||
};
|
||||
|
||||
Some(Ok(options))
|
||||
Ok(options)
|
||||
}
|
||||
|
||||
// Parses command line arguments into test options
|
||||
pub fn parse_opts(args: &[String]) -> Option<OptRes> {
|
||||
let mut allow_unstable = false;
|
||||
let opts = optgroups();
|
||||
let args = args.get(1..).unwrap_or(args);
|
||||
let matches = match opts.parse(args) {
|
||||
Ok(m) => m,
|
||||
Err(f) => return Some(Err(f.to_string())),
|
||||
fn get_test_threads(matches: &getopts::Matches) -> OptPartRes<Option<usize>> {
|
||||
let test_threads = match matches.opt_str("test-threads") {
|
||||
Some(n_str) => match n_str.parse::<usize>() {
|
||||
Ok(0) => return Err("argument for --test-threads must not be 0".to_string()),
|
||||
Ok(n) => Some(n),
|
||||
Err(e) => {
|
||||
return Err(format!(
|
||||
"argument for --test-threads must be a number > 0 \
|
||||
(error: {})",
|
||||
e
|
||||
));
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
if let Some(opt) = matches.opt_str("Z") {
|
||||
if !is_nightly() {
|
||||
return Some(Err(
|
||||
"the option `Z` is only accepted on the nightly compiler".into(),
|
||||
Ok(test_threads)
|
||||
}
|
||||
|
||||
fn get_format(matches: &getopts::Matches, quiet: bool, allow_unstable: bool) -> OptPartRes<OutputFormat> {
|
||||
let format = match matches.opt_str("format").as_ref().map(|s| &**s) {
|
||||
None if quiet => OutputFormat::Terse,
|
||||
Some("pretty") | None => OutputFormat::Pretty,
|
||||
Some("terse") => OutputFormat::Terse,
|
||||
Some("json") => {
|
||||
if !allow_unstable {
|
||||
return Err(
|
||||
"The \"json\" format is only accepted on the nightly compiler".into(),
|
||||
);
|
||||
}
|
||||
OutputFormat::Json
|
||||
}
|
||||
|
||||
Some(v) => {
|
||||
return Err(format!(
|
||||
"argument for --format must be pretty, terse, or json (was \
|
||||
{})",
|
||||
v
|
||||
));
|
||||
}
|
||||
|
||||
match &*opt {
|
||||
"unstable-options" => {
|
||||
allow_unstable = true;
|
||||
}
|
||||
_ => {
|
||||
return Some(Err("Unrecognized option to `Z`".into()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if matches.opt_present("h") {
|
||||
usage(&args[0], &opts);
|
||||
return None;
|
||||
}
|
||||
Ok(format)
|
||||
}
|
||||
|
||||
let filter = if !matches.free.is_empty() {
|
||||
Some(matches.free[0].clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
fn get_color_config(matches: &getopts::Matches) -> OptPartRes<ColorConfig> {
|
||||
let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
|
||||
Some("auto") | None => ColorConfig::AutoColor,
|
||||
Some("always") => ColorConfig::AlwaysColor,
|
||||
Some("never") => ColorConfig::NeverColor,
|
||||
|
||||
let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
|
||||
|
||||
let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored");
|
||||
|
||||
let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
|
||||
(true, true) => {
|
||||
return Some(Err(
|
||||
"the options --include-ignored and --ignored are mutually exclusive".into(),
|
||||
Some(v) => {
|
||||
return Err(format!(
|
||||
"argument for --color must be auto, always, or never (was \
|
||||
{})",
|
||||
v
|
||||
));
|
||||
}
|
||||
(true, false) => RunIgnored::Yes,
|
||||
(false, true) => RunIgnored::Only,
|
||||
(false, false) => RunIgnored::No,
|
||||
};
|
||||
let quiet = matches.opt_present("quiet");
|
||||
let exact = matches.opt_present("exact");
|
||||
let list = matches.opt_present("list");
|
||||
|
||||
let logfile = matches.opt_str("logfile");
|
||||
let logfile = logfile.map(|s| PathBuf::from(&s));
|
||||
|
||||
let bench_benchmarks = matches.opt_present("bench");
|
||||
let run_tests = !bench_benchmarks || matches.opt_present("test");
|
||||
Ok(color)
|
||||
}
|
||||
|
||||
fn get_nocapture(matches: &getopts::Matches) -> OptPartRes<bool> {
|
||||
let mut nocapture = matches.opt_present("nocapture");
|
||||
if !nocapture {
|
||||
nocapture = match env::var("RUST_TEST_NOCAPTURE") {
|
||||
|
|
@ -305,80 +382,59 @@ pub fn parse_opts(args: &[String]) -> Option<OptRes> {
|
|||
};
|
||||
}
|
||||
|
||||
let time_options = match get_time_options(&matches, allow_unstable) {
|
||||
Some(Ok(val)) => val,
|
||||
Some(Err(e)) => return Some(Err(e)),
|
||||
None => panic!("Unexpected output from `get_time_options`"),
|
||||
};
|
||||
|
||||
let test_threads = match matches.opt_str("test-threads") {
|
||||
Some(n_str) => match n_str.parse::<usize>() {
|
||||
Ok(0) => return Some(Err("argument for --test-threads must not be 0".to_string())),
|
||||
Ok(n) => Some(n),
|
||||
Err(e) => {
|
||||
return Some(Err(format!(
|
||||
"argument for --test-threads must be a number > 0 \
|
||||
(error: {})",
|
||||
e
|
||||
)));
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
|
||||
Some("auto") | None => ColorConfig::AutoColor,
|
||||
Some("always") => ColorConfig::AlwaysColor,
|
||||
Some("never") => ColorConfig::NeverColor,
|
||||
|
||||
Some(v) => {
|
||||
return Some(Err(format!(
|
||||
"argument for --color must be auto, always, or never (was \
|
||||
{})",
|
||||
v
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
let format = match matches.opt_str("format").as_ref().map(|s| &**s) {
|
||||
None if quiet => OutputFormat::Terse,
|
||||
Some("pretty") | None => OutputFormat::Pretty,
|
||||
Some("terse") => OutputFormat::Terse,
|
||||
Some("json") => {
|
||||
if !allow_unstable {
|
||||
return Some(Err(
|
||||
"The \"json\" format is only accepted on the nightly compiler".into(),
|
||||
));
|
||||
}
|
||||
OutputFormat::Json
|
||||
}
|
||||
|
||||
Some(v) => {
|
||||
return Some(Err(format!(
|
||||
"argument for --format must be pretty, terse, or json (was \
|
||||
{})",
|
||||
v
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
let test_opts = TestOpts {
|
||||
list,
|
||||
filter,
|
||||
filter_exact: exact,
|
||||
exclude_should_panic,
|
||||
run_ignored,
|
||||
run_tests,
|
||||
bench_benchmarks,
|
||||
logfile,
|
||||
nocapture,
|
||||
color,
|
||||
format,
|
||||
test_threads,
|
||||
skip: matches.opt_strs("skip"),
|
||||
time_options,
|
||||
options: Options::new().display_output(matches.opt_present("show-output")),
|
||||
};
|
||||
|
||||
Some(Ok(test_opts))
|
||||
Ok(nocapture)
|
||||
}
|
||||
|
||||
fn get_run_ignored(matches: &getopts::Matches, include_ignored: bool) -> OptPartRes<RunIgnored> {
|
||||
let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
|
||||
(true, true) => {
|
||||
return Err(
|
||||
"the options --include-ignored and --ignored are mutually exclusive".into(),
|
||||
);
|
||||
}
|
||||
(true, false) => RunIgnored::Yes,
|
||||
(false, true) => RunIgnored::Only,
|
||||
(false, false) => RunIgnored::No,
|
||||
};
|
||||
|
||||
Ok(run_ignored)
|
||||
}
|
||||
|
||||
fn get_filter(matches: &getopts::Matches) -> OptPartRes<Option<String>> {
|
||||
let filter = if !matches.free.is_empty() {
|
||||
Some(matches.free[0].clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(filter)
|
||||
}
|
||||
|
||||
fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes<bool> {
|
||||
let mut allow_unstable = false;
|
||||
|
||||
if let Some(opt) = matches.opt_str("Z") {
|
||||
if !is_nightly() {
|
||||
return Err(
|
||||
"the option `Z` is only accepted on the nightly compiler".into(),
|
||||
);
|
||||
}
|
||||
|
||||
match &*opt {
|
||||
"unstable-options" => {
|
||||
allow_unstable = true;
|
||||
}
|
||||
_ => {
|
||||
return Err("Unrecognized option to `Z`".into());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(allow_unstable)
|
||||
}
|
||||
|
||||
fn get_log_file(matches: &getopts::Matches) -> OptPartRes<Option<PathBuf>> {
|
||||
let logfile = matches.opt_str("logfile").map(|s| PathBuf::from(&s));
|
||||
|
||||
Ok(logfile)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue