Refactor command-line handling into modules per subcommands

This commit is contained in:
Dustin J. Mitchell
2020-11-23 19:33:04 -05:00
parent e0b69a62b1
commit fe4183c3ca
12 changed files with 560 additions and 59 deletions

57
cli/src/lib.rs Normal file
View File

@@ -0,0 +1,57 @@
use clap::{App, AppSettings};
use failure::Fallible;
use std::ffi::OsString;
mod cmd;
use cmd::ArgMatchResult;
pub use cmd::CommandInvocation;
/// Parse the given command line and return an as-yet un-executed CommandInvocation.
pub fn parse_command_line<I, T>(iter: I) -> Fallible<CommandInvocation>
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
{
let subcommands = cmd::subcommands();
let mut app = App::new("TaskChampion")
.version("0.1")
.about("Personal task-tracking")
.setting(AppSettings::ColoredHelp);
for subcommand in subcommands.iter() {
app = subcommand.decorate_app(app);
}
let matches = app.get_matches_from_safe(iter)?;
for subcommand in subcommands.iter() {
match subcommand.arg_match(&matches) {
ArgMatchResult::Ok(invocation) => return Ok(CommandInvocation::new(invocation)),
ArgMatchResult::Err(err) => return Err(err),
ArgMatchResult::None => {}
}
}
// one of the subcommands also matches the lack of subcommands, so this never
// occurrs.
unreachable!()
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_command_line_success() -> Fallible<()> {
// This just verifies that one of the subcommands works; the subcommands themselves
// are tested in their own unit tests.
parse_command_line(vec!["task", "pending"].iter())?;
Ok(())
}
#[test]
fn test_parse_command_line_failure() {
assert!(parse_command_line(vec!["task", "--no-such-arg"].iter()).is_err());
}
}