Skip to content

Commit ddc6a5f

Browse files
committed
Split options parsing into several functions
1 parent 12397e9 commit ddc6a5f

File tree

1 file changed

+179
-123
lines changed

1 file changed

+179
-123
lines changed

src/libtest/cli.rs

Lines changed: 179 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ impl TestOpts {
4040
/// Result of parsing the options.
4141
pub type OptRes = Result<TestOpts, String>;
4242
/// Result of parsing the option part.
43-
type OptPartRes<T> = Result<Option<T>, String>;
43+
type OptPartRes<T> = Result<T, String>;
4444

4545
fn optgroups() -> getopts::Options {
4646
let mut opts = getopts::Options::new();
@@ -186,36 +186,111 @@ Test Attributes:
186186
);
187187
}
188188

189-
// FIXME: Copied from libsyntax until linkage errors are resolved. Issue #47566
190-
fn is_nightly() -> bool {
191-
// Whether this is a feature-staged build, i.e., on the beta or stable channel
192-
let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
193-
// Whether we should enable unstable features for bootstrapping
194-
let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
189+
/// Parses command line arguments into test options.
190+
/// Returns `None` if help was requested (since we only show help message and don't run tests),
191+
/// returns `Some(Err(..))` if provided arguments are incorrect,
192+
/// otherwise creates a `TestOpts` object and returns it.
193+
pub fn parse_opts(args: &[String]) -> Option<OptRes> {
194+
// Parse matches.
195+
let opts = optgroups();
196+
let args = args.get(1..).unwrap_or(args);
197+
let matches = match opts.parse(args) {
198+
Ok(m) => m,
199+
Err(f) => return Some(Err(f.to_string())),
200+
};
195201

196-
bootstrap || !disable_unstable_features
202+
// Check if help was requested.
203+
if matches.opt_present("h") {
204+
// Show help and do nothing more.
205+
usage(&args[0], &opts);
206+
return None;
207+
}
208+
209+
// Actually parse the opts.
210+
let opts_result = parse_opts_impl(matches);
211+
212+
Some(opts_result)
197213
}
198214

199215
// Gets the option value and checks if unstable features are enabled.
200216
macro_rules! unstable_optflag {
201217
($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
202218
let opt = $matches.opt_present($option_name);
203219
if !$allow_unstable && opt {
204-
return Some(Err(format!(
220+
return Err(format!(
205221
"The \"{}\" flag is only accepted on the nightly compiler",
206222
$option_name
207-
)));
223+
));
208224
}
209225

210226
opt
211227
}};
212228
}
213229

230+
// Implementation of `parse_opts` that doesn't care about help message
231+
// and returns a `Result`.
232+
fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
233+
let allow_unstable = get_allow_unstable(&matches)?;
234+
235+
// Unstable flags
236+
let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
237+
let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored");
238+
let time_options = get_time_options(&matches, allow_unstable)?;
239+
240+
let quiet = matches.opt_present("quiet");
241+
let exact = matches.opt_present("exact");
242+
let list = matches.opt_present("list");
243+
let skip = matches.opt_strs("skip");
244+
245+
let bench_benchmarks = matches.opt_present("bench");
246+
let run_tests = !bench_benchmarks || matches.opt_present("test");
247+
248+
let logfile = get_log_file(&matches)?;
249+
let run_ignored = get_run_ignored(&matches, include_ignored)?;
250+
let filter = get_filter(&matches)?;
251+
let nocapture = get_nocapture(&matches)?;
252+
let test_threads = get_test_threads(&matches)?;
253+
let color = get_color_config(&matches)?;
254+
let format = get_format(&matches, quiet, allow_unstable)?;
255+
256+
let options = Options::new().display_output(matches.opt_present("show-output"));
257+
258+
let test_opts = TestOpts {
259+
list,
260+
filter,
261+
filter_exact: exact,
262+
exclude_should_panic,
263+
run_ignored,
264+
run_tests,
265+
bench_benchmarks,
266+
logfile,
267+
nocapture,
268+
color,
269+
format,
270+
test_threads,
271+
skip,
272+
time_options,
273+
options,
274+
};
275+
276+
Ok(test_opts)
277+
}
278+
279+
// FIXME: Copied from libsyntax until linkage errors are resolved. Issue #47566
280+
fn is_nightly() -> bool {
281+
// Whether this is a feature-staged build, i.e., on the beta or stable channel
282+
let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
283+
// Whether we should enable unstable features for bootstrapping
284+
let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
285+
286+
bootstrap || !disable_unstable_features
287+
}
288+
214289
// Gets the CLI options assotiated with `report-time` feature.
215290
fn get_time_options(
216291
matches: &getopts::Matches,
217292
allow_unstable: bool)
218-
-> Option<OptPartRes<TestTimeOptions>> {
293+
-> OptPartRes<Option<TestTimeOptions>> {
219294
let report_time = unstable_optflag!(matches, allow_unstable, "report-time");
220295
let colored_opt_str = matches.opt_str("report-time");
221296
let mut report_time_colored = report_time && colored_opt_str == Some("colored".into());
@@ -232,71 +307,73 @@ fn get_time_options(
232307
None
233308
};
234309

235-
Some(Ok(options))
310+
Ok(options)
236311
}
237312

238-
// Parses command line arguments into test options
239-
pub fn parse_opts(args: &[String]) -> Option<OptRes> {
240-
let mut allow_unstable = false;
241-
let opts = optgroups();
242-
let args = args.get(1..).unwrap_or(args);
243-
let matches = match opts.parse(args) {
244-
Ok(m) => m,
245-
Err(f) => return Some(Err(f.to_string())),
313+
fn get_test_threads(matches: &getopts::Matches) -> OptPartRes<Option<usize>> {
314+
let test_threads = match matches.opt_str("test-threads") {
315+
Some(n_str) => match n_str.parse::<usize>() {
316+
Ok(0) => return Err("argument for --test-threads must not be 0".to_string()),
317+
Ok(n) => Some(n),
318+
Err(e) => {
319+
return Err(format!(
320+
"argument for --test-threads must be a number > 0 \
321+
(error: {})",
322+
e
323+
));
324+
}
325+
},
326+
None => None,
246327
};
247328

248-
if let Some(opt) = matches.opt_str("Z") {
249-
if !is_nightly() {
250-
return Some(Err(
251-
"the option `Z` is only accepted on the nightly compiler".into(),
252-
));
253-
}
329+
Ok(test_threads)
330+
}
254331

255-
match &*opt {
256-
"unstable-options" => {
257-
allow_unstable = true;
258-
}
259-
_ => {
260-
return Some(Err("Unrecognized option to `Z`".into()));
332+
fn get_format(matches: &getopts::Matches, quiet: bool, allow_unstable: bool) -> OptPartRes<OutputFormat> {
333+
let format = match matches.opt_str("format").as_ref().map(|s| &**s) {
334+
None if quiet => OutputFormat::Terse,
335+
Some("pretty") | None => OutputFormat::Pretty,
336+
Some("terse") => OutputFormat::Terse,
337+
Some("json") => {
338+
if !allow_unstable {
339+
return Err(
340+
"The \"json\" format is only accepted on the nightly compiler".into(),
341+
);
261342
}
343+
OutputFormat::Json
262344
}
263-
};
264345

265-
if matches.opt_present("h") {
266-
usage(&args[0], &opts);
267-
return None;
268-
}
269-
270-
let filter = if !matches.free.is_empty() {
271-
Some(matches.free[0].clone())
272-
} else {
273-
None
346+
Some(v) => {
347+
return Err(format!(
348+
"argument for --format must be pretty, terse, or json (was \
349+
{})",
350+
v
351+
));
352+
}
274353
};
275354

276-
let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
355+
Ok(format)
356+
}
277357

278-
let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored");
358+
fn get_color_config(matches: &getopts::Matches) -> OptPartRes<ColorConfig> {
359+
let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
360+
Some("auto") | None => ColorConfig::AutoColor,
361+
Some("always") => ColorConfig::AlwaysColor,
362+
Some("never") => ColorConfig::NeverColor,
279363

280-
let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
281-
(true, true) => {
282-
return Some(Err(
283-
"the options --include-ignored and --ignored are mutually exclusive".into(),
364+
Some(v) => {
365+
return Err(format!(
366+
"argument for --color must be auto, always, or never (was \
367+
{})",
368+
v
284369
));
285370
}
286-
(true, false) => RunIgnored::Yes,
287-
(false, true) => RunIgnored::Only,
288-
(false, false) => RunIgnored::No,
289371
};
290-
let quiet = matches.opt_present("quiet");
291-
let exact = matches.opt_present("exact");
292-
let list = matches.opt_present("list");
293372

294-
let logfile = matches.opt_str("logfile");
295-
let logfile = logfile.map(|s| PathBuf::from(&s));
296-
297-
let bench_benchmarks = matches.opt_present("bench");
298-
let run_tests = !bench_benchmarks || matches.opt_present("test");
373+
Ok(color)
374+
}
299375

376+
fn get_nocapture(matches: &getopts::Matches) -> OptPartRes<bool> {
300377
let mut nocapture = matches.opt_present("nocapture");
301378
if !nocapture {
302379
nocapture = match env::var("RUST_TEST_NOCAPTURE") {
@@ -305,80 +382,59 @@ pub fn parse_opts(args: &[String]) -> Option<OptRes> {
305382
};
306383
}
307384

308-
let time_options = match get_time_options(&matches, allow_unstable) {
309-
Some(Ok(val)) => val,
310-
Some(Err(e)) => return Some(Err(e)),
311-
None => panic!("Unexpected output from `get_time_options`"),
312-
};
385+
Ok(nocapture)
386+
}
313387

314-
let test_threads = match matches.opt_str("test-threads") {
315-
Some(n_str) => match n_str.parse::<usize>() {
316-
Ok(0) => return Some(Err("argument for --test-threads must not be 0".to_string())),
317-
Ok(n) => Some(n),
318-
Err(e) => {
319-
return Some(Err(format!(
320-
"argument for --test-threads must be a number > 0 \
321-
(error: {})",
322-
e
323-
)));
324-
}
325-
},
326-
None => None,
388+
fn get_run_ignored(matches: &getopts::Matches, include_ignored: bool) -> OptPartRes<RunIgnored> {
389+
let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
390+
(true, true) => {
391+
return Err(
392+
"the options --include-ignored and --ignored are mutually exclusive".into(),
393+
);
394+
}
395+
(true, false) => RunIgnored::Yes,
396+
(false, true) => RunIgnored::Only,
397+
(false, false) => RunIgnored::No,
327398
};
328399

329-
let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
330-
Some("auto") | None => ColorConfig::AutoColor,
331-
Some("always") => ColorConfig::AlwaysColor,
332-
Some("never") => ColorConfig::NeverColor,
400+
Ok(run_ignored)
401+
}
333402

334-
Some(v) => {
335-
return Some(Err(format!(
336-
"argument for --color must be auto, always, or never (was \
337-
{})",
338-
v
339-
)));
340-
}
403+
fn get_filter(matches: &getopts::Matches) -> OptPartRes<Option<String>> {
404+
let filter = if !matches.free.is_empty() {
405+
Some(matches.free[0].clone())
406+
} else {
407+
None
341408
};
342409

343-
let format = match matches.opt_str("format").as_ref().map(|s| &**s) {
344-
None if quiet => OutputFormat::Terse,
345-
Some("pretty") | None => OutputFormat::Pretty,
346-
Some("terse") => OutputFormat::Terse,
347-
Some("json") => {
348-
if !allow_unstable {
349-
return Some(Err(
350-
"The \"json\" format is only accepted on the nightly compiler".into(),
351-
));
352-
}
353-
OutputFormat::Json
410+
Ok(filter)
411+
}
412+
413+
fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes<bool> {
414+
let mut allow_unstable = false;
415+
416+
if let Some(opt) = matches.opt_str("Z") {
417+
if !is_nightly() {
418+
return Err(
419+
"the option `Z` is only accepted on the nightly compiler".into(),
420+
);
354421
}
355422

356-
Some(v) => {
357-
return Some(Err(format!(
358-
"argument for --format must be pretty, terse, or json (was \
359-
{})",
360-
v
361-
)));
423+
match &*opt {
424+
"unstable-options" => {
425+
allow_unstable = true;
426+
}
427+
_ => {
428+
return Err("Unrecognized option to `Z`".into());
429+
}
362430
}
363431
};
364432

365-
let test_opts = TestOpts {
366-
list,
367-
filter,
368-
filter_exact: exact,
369-
exclude_should_panic,
370-
run_ignored,
371-
run_tests,
372-
bench_benchmarks,
373-
logfile,
374-
nocapture,
375-
color,
376-
format,
377-
test_threads,
378-
skip: matches.opt_strs("skip"),
379-
time_options,
380-
options: Options::new().display_output(matches.opt_present("show-output")),
381-
};
433+
Ok(allow_unstable)
434+
}
435+
436+
fn get_log_file(matches: &getopts::Matches) -> OptPartRes<Option<PathBuf>> {
437+
let logfile = matches.opt_str("logfile").map(|s| PathBuf::from(&s));
382438

383-
Some(Ok(test_opts))
439+
Ok(logfile)
384440
}

0 commit comments

Comments
 (0)