Merge pull request #1343 from matthiasbeyer/imag-tag/rewrite-cli

imag-tag: rewrite cli
This commit is contained in:
Matthias Beyer 2018-03-12 23:07:06 +01:00 committed by GitHub
commit ee72e61b44
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 61 additions and 97 deletions

View file

@ -67,27 +67,30 @@ fn main() {
"Direct interface to the store. Use with great care!", "Direct interface to the store. Use with great care!",
build_ui); build_ui);
let id = rt.cli().value_of("id").unwrap(); // enforced by clap let id = rt.cli().value_of("id").map(PathBuf::from).unwrap(); // enforced by clap
rt.cli() rt.cli()
.subcommand_name() .subcommand_name()
.map_or_else( .map(|name| match name {
|| { "list" => list(id, &rt),
"remove" => {
let id = PathBuf::from(id); let id = PathBuf::from(id);
let add = get_add_tags(rt.cli()); let add = None;
let rem = get_remove_tags(rt.cli()); let rem = get_remove_tags(rt.cli());
debug!("id = {:?}, add = {:?}, rem = {:?}", id, add, rem);
alter(&rt, id, add, rem); alter(&rt, id, add, rem);
}, },
|name| { "add" => {
let id = PathBuf::from(id); let id = PathBuf::from(id);
debug!("Call: {}", name); let add = get_add_tags(rt.cli());
match name { let rem = None;
"list" => list(id, &rt), debug!("id = {:?}, add = {:?}, rem = {:?}", id, add, rem);
_ => { alter(&rt, id, add, rem);
warn!("Unknown command"); },
// More error handling _ => {
}, error!("Unknown command");
}; ::std::process::exit(1)
}); },
});
} }
fn alter(rt: &Runtime, id: PathBuf, add: Option<Vec<Tag>>, rem: Option<Vec<Tag>>) { fn alter(rt: &Runtime, id: PathBuf, add: Option<Vec<Tag>>, rem: Option<Vec<Tag>>) {
@ -190,36 +193,28 @@ fn list(id: PathBuf, rt: &Runtime) {
/// ///
/// Returns none if the argument was not specified /// Returns none if the argument was not specified
fn get_add_tags(matches: &ArgMatches) -> Option<Vec<Tag>> { fn get_add_tags(matches: &ArgMatches) -> Option<Vec<Tag>> {
let a = "add-tags"; retrieve_tags(matches, "add", "add-tags")
extract_tags(matches, a, '+')
.or_else(|| matches.values_of(a).map(|values| values.map(String::from).collect()))
} }
/// Get the tags which should be removed from the commandline /// Get the tags which should be removed from the commandline
/// ///
/// Returns none if the argument was not specified /// Returns none if the argument was not specified
fn get_remove_tags(matches: &ArgMatches) -> Option<Vec<Tag>> { fn get_remove_tags(matches: &ArgMatches) -> Option<Vec<Tag>> {
let r = "remove-tags"; retrieve_tags(matches, "remove", "remove-tags")
extract_tags(matches, r, '+')
.or_else(|| matches.values_of(r).map(|values| values.map(String::from).collect()))
} }
fn extract_tags(matches: &ArgMatches, specifier: &str, specchar: char) -> Option<Vec<Tag>> { fn retrieve_tags(m: &ArgMatches, s: &'static str, v: &'static str) -> Option<Vec<Tag>> {
if let Some(submatch) = matches.subcommand_matches("tags") { Some(m
submatch.values_of(specifier) .subcommand_matches(s)
.map(|values| values.map(String::from).collect()) .unwrap_or_else(|| {
} else { error!("Expected subcommand '{}', but was not specified", s);
matches.values_of("specify-tags") ::std::process::exit(1)
.map(|argmatches| { })
argmatches .values_of(v)
.map(String::from) .unwrap() // enforced by clap
.filter(|s| s.starts_with(specchar)) .into_iter()
.map(|s| { .map(String::from)
String::from(s.split_at(1).1) .collect())
})
.collect()
})
}
} }
#[cfg(test)] #[cfg(test)]
@ -275,7 +270,7 @@ mod tests {
setup_logging(); setup_logging();
debug!("Generating runtime"); debug!("Generating runtime");
let name = "test-tag-add-adds-tags"; let name = "test-tag-add-adds-tags";
let rt = generate_test_runtime(vec![name, "--add", "foo"]).unwrap(); let rt = generate_test_runtime(vec![name, "add", "foo"]).unwrap();
debug!("Creating default entry"); debug!("Creating default entry");
create_test_default_entry(&rt, name).unwrap(); create_test_default_entry(&rt, name).unwrap();
@ -285,12 +280,8 @@ mod tests {
let add = get_add_tags(rt.cli()); let add = get_add_tags(rt.cli());
debug!("Add-tags: {:?}", add); debug!("Add-tags: {:?}", add);
debug!("Getting 'remove' tags");
let rem = get_remove_tags(rt.cli());
debug!("Rem-tags: {:?}", rem);
debug!("Altering things"); debug!("Altering things");
alter(&rt, id.clone(), add, rem); alter(&rt, id.clone(), add, None);
debug!("Altered"); debug!("Altered");
let test_entry = rt.store().get(id).unwrap().unwrap(); let test_entry = rt.store().get(id).unwrap().unwrap();
@ -306,48 +297,12 @@ mod tests {
assert_eq!(*test_tags, tags_toml_value(vec!["foo"])); assert_eq!(*test_tags, tags_toml_value(vec!["foo"]));
} }
#[test]
fn test_tag_add_more_than_remove_adds_tags() {
setup_logging();
debug!("Generating runtime");
let name = "test-tag-add-more-than-remove-adds-tags";
let rt = generate_test_runtime(vec![name,
"--add", "foo",
"--add", "bar",
"--add", "baz",
"--add", "bub",
"--remove", "foo",
"--remove", "bar",
"--remove", "baz",
]).unwrap();
debug!("Creating default entry");
create_test_default_entry(&rt, name).unwrap();
let id = PathBuf::from(String::from(name));
// Manually add tags
let add = get_add_tags(rt.cli());
debug!("Getting 'remove' tags");
let rem = get_remove_tags(rt.cli());
debug!("Rem-tags: {:?}", rem);
debug!("Altering things");
alter(&rt, id.clone(), add, rem);
debug!("Altered");
let test_entry = rt.store().get(id).unwrap().unwrap();
let test_tags = get_entry_tags(&test_entry).unwrap().unwrap();
assert_eq!(*test_tags, tags_toml_value(vec!["bub"]));
}
#[test] #[test]
fn test_tag_remove_removes_tag() { fn test_tag_remove_removes_tag() {
setup_logging(); setup_logging();
debug!("Generating runtime"); debug!("Generating runtime");
let name = "test-tag-remove-removes-tag"; let name = "test-tag-remove-removes-tag";
let rt = generate_test_runtime(vec![name, "--remove", "foo"]).unwrap(); let rt = generate_test_runtime(vec![name, "remove", "foo"]).unwrap();
debug!("Creating default entry"); debug!("Creating default entry");
create_test_default_entry(&rt, name).unwrap(); create_test_default_entry(&rt, name).unwrap();
@ -375,7 +330,7 @@ mod tests {
setup_logging(); setup_logging();
debug!("Generating runtime"); debug!("Generating runtime");
let name = "test-tag-remove-removes-only-to-remove-tag-doesnt-crash-on-nonexistent-tag"; let name = "test-tag-remove-removes-only-to-remove-tag-doesnt-crash-on-nonexistent-tag";
let rt = generate_test_runtime(vec![name, "--remove", "foo"]).unwrap(); let rt = generate_test_runtime(vec![name, "remove", "foo"]).unwrap();
debug!("Creating default entry"); debug!("Creating default entry");
create_test_default_entry(&rt, name).unwrap(); create_test_default_entry(&rt, name).unwrap();
@ -403,7 +358,7 @@ mod tests {
setup_logging(); setup_logging();
debug!("Generating runtime"); debug!("Generating runtime");
let name = "test-tag-remove-removes-but-doesnt-crash-on-nonexistent-tag"; let name = "test-tag-remove-removes-but-doesnt-crash-on-nonexistent-tag";
let rt = generate_test_runtime(vec![name, "--remove", "foo", "--remove", "bar"]).unwrap(); let rt = generate_test_runtime(vec![name, "remove", "foo", "bar"]).unwrap();
debug!("Creating default entry"); debug!("Creating default entry");
create_test_default_entry(&rt, name).unwrap(); create_test_default_entry(&rt, name).unwrap();

View file

@ -30,23 +30,31 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
.value_name("ID") .value_name("ID")
.help("Entry to use")) .help("Entry to use"))
.arg(Arg::with_name("add-tags") .subcommand(SubCommand::with_name("add")
.short("a") .about("Add tags")
.long("add") .version("0.1")
.takes_value(true) .arg(Arg::with_name("add-tags")
.value_name("tags") .index(1)
.multiple(true) .takes_value(true)
.validator(is_tag) .required(true)
.help("Add these tags")) .multiple(true)
.value_name("tags")
.validator(is_tag)
.help("Add these tags"))
)
.arg(Arg::with_name("remove-tags") .subcommand(SubCommand::with_name("remove")
.short("r") .about("Remove tags")
.long("remove") .version("0.1")
.takes_value(true) .arg(Arg::with_name("remove-tags")
.multiple(true) .index(1)
.validator(is_tag) .takes_value(true)
.value_name("tags") .required(true)
.help("Remove these tags")) .multiple(true)
.value_name("tags")
.validator(is_tag)
.help("Remove these tags"))
)
.subcommand(SubCommand::with_name("list") .subcommand(SubCommand::with_name("list")
.about("List tags (default)") .about("List tags (default)")

View file

@ -56,6 +56,7 @@ This section contains the changelog from the last release to the next release.
"debug" mode. "debug" mode.
* `imag-diary` supports "daily" diaries now. * `imag-diary` supports "daily" diaries now.
* `imag-contact` joins multiple emails with "," now * `imag-contact` joins multiple emails with "," now
* `imag-tag` commandline was rewritten for positional arguments.
* Bugfixes * Bugfixes
* imag does not panic anymore when piping and breaking that pipe, for * imag does not panic anymore when piping and breaking that pipe, for
example like with `imag store ids | head -n 1`. example like with `imag store ids | head -n 1`.