summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2019-11-16 12:48:48 +0100
committerMatthias Beyer <mail@beyermatthias.de>2019-11-16 12:48:48 +0100
commit1a25bd2da4dafc0768a41b8fe4539dc7e3edb194 (patch)
tree4e6561e69a07c40a6c0ba3520e4c15f6cc6d85e7
parent5a89a29b6d3cc9d020006c264e321bcb47553711 (diff)
parent734f889cdf382b1a6dadc81b255a6deba64cbffd (diff)
Merge branch 'imag-tag/filtering' into master
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
-rw-r--r--bin/core/imag-tag/src/lib.rs151
-rw-r--r--bin/core/imag-tag/src/ui.rs20
-rw-r--r--tests/ui/src/imag_tag.rs48
3 files changed, 178 insertions, 41 deletions
diff --git a/bin/core/imag-tag/src/lib.rs b/bin/core/imag-tag/src/lib.rs
index 83f58722..874ebf5a 100644
--- a/bin/core/imag-tag/src/lib.rs
+++ b/bin/core/imag-tag/src/lib.rs
@@ -66,10 +66,12 @@ use failure::Error;
use failure::err_msg;
use resiter::AndThen;
use resiter::Map;
+use resiter::FilterMap;
use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagentrytag::tagable::Tagable;
+use libimagentrytag::tag::is_tag_str;
use libimagentrytag::tag::Tag;
use libimagstore::storeid::StoreId;
@@ -86,51 +88,118 @@ pub enum ImagTag {}
impl ImagApplication for ImagTag {
fn run(rt: Runtime) -> Result<()> {
let process = |iter: &mut dyn Iterator<Item = Result<StoreId>>| -> Result<()> {
- if let Some(name) = rt.cli().subcommand_name() {
- match name {
- "list" => iter
- .map_ok(|id| list(id, &rt))
+ match rt.cli().subcommand() {
+ ("list", _) => iter
+ .map_ok(|id| list(id, &rt))
+ .collect::<Result<Vec<_>>>()
+ .map(|_| ()),
+
+ ("remove", _) => iter.and_then_ok(|id| {
+ let add = None;
+ let rem = get_remove_tags(rt.cli())?;
+ debug!("id = {:?}, add = {:?}, rem = {:?}", id, add, rem);
+ alter(&rt, id, add, rem)
+ }).collect(),
+
+ ("add", _) => iter.and_then_ok(|id| {
+ let add = get_add_tags(rt.cli())?;
+ let rem = None;
+ debug!("id = {:?}, add = {:?}, rem = {:?}", id, add, rem);
+ alter(&rt, id, add, rem)
+ }).collect(),
+
+ ("present", Some(scmd)) => {
+ let must_be_present = scmd
+ .values_of("present-tag")
+ .unwrap()
+ .map(String::from)
+ .collect::<Vec<String>>();
+
+ must_be_present.iter().map(|t| is_tag_str(t)).collect::<Result<Vec<_>>>()?;
+
+ iter.filter_map_ok(|id| {
+ match rt.store().get(id.clone()) {
+ Err(e) => Some(Err(e)),
+ Ok(None) => Some(Err(format_err!("No entry for id {}", id))),
+ Ok(Some(entry)) => {
+ let entry_tags = match entry.get_tags() {
+ Err(e) => return Some(Err(e)),
+ Ok(e) => e,
+ };
+
+ if must_be_present.iter().all(|pres| entry_tags.contains(pres)) {
+ Some(Ok(entry))
+ } else {
+ None
+ }
+ }
+ }
+ })
+ .flatten()
+ .and_then_ok(|e| {
+ if !rt.output_is_pipe() {
+ writeln!(rt.stdout(), "{}", e.get_location())?;
+ }
+ Ok(e)
+ })
+ .and_then_ok(|e| rt.report_touched(e.get_location()).map_err(Error::from))
.collect::<Result<Vec<_>>>()
- .map(|_| ()),
-
- "remove" => iter.and_then_ok(|id| {
- let add = None;
- let rem = get_remove_tags(rt.cli())?;
- debug!("id = {:?}, add = {:?}, rem = {:?}", id, add, rem);
- alter(&rt, id, add, rem)
- }).collect(),
-
- "add" => iter.and_then_ok(|id| {
- let add = get_add_tags(rt.cli())?;
- let rem = None;
- debug!("id = {:?}, add = {:?}, rem = {:?}", id, add, rem);
- alter(&rt, id, add, rem)
- }).collect(),
-
- other => {
- debug!("Unknown command");
- if rt.handle_unknown_subcommand("imag-tag", other, rt.cli())?.success() {
- Ok(())
- } else {
- Err(format_err!("Subcommand failed"))
- }
- },
- }
- } else {
- Ok(())
+ .map(|_| ())
+ },
+
+ ("missing", Some(scmd)) => {
+ let must_be_missing = scmd
+ .values_of("missing-tag")
+ .unwrap()
+ .map(String::from)
+ .collect::<Vec<String>>();
+
+ must_be_missing.iter().map(|t| is_tag_str(t)).collect::<Result<Vec<_>>>()?;
+
+ iter.filter_map_ok(|id| {
+ match rt.store().get(id.clone()) {
+ Err(e) => Some(Err(e)),
+ Ok(None) => Some(Err(format_err!("No entry for id {}", id))),
+ Ok(Some(entry)) => {
+ let entry_tags = match entry.get_tags() {
+ Err(e) => return Some(Err(e)),
+ Ok(e) => e,
+ };
+
+ if must_be_missing.iter().all(|miss| !entry_tags.contains(miss)) {
+ Some(Ok(entry))
+ } else {
+ None
+ }
+ }
+ }
+ })
+ .flatten()
+ .and_then_ok(|e| {
+ if !rt.output_is_pipe() {
+ writeln!(rt.stdout(), "{}", e.get_location())?;
+ }
+ Ok(e)
+ })
+ .and_then_ok(|e| rt.report_touched(e.get_location()).map_err(Error::from))
+ .collect::<Result<Vec<_>>>()
+ .map(|_| ())
+ },
+
+ (other, _) => {
+ debug!("Unknown command");
+ if rt.handle_unknown_subcommand("imag-tag", other, rt.cli())?.success() {
+ Ok(())
+ } else {
+ Err(format_err!("Subcommand failed"))
+ }
+ },
}
};
- if rt.ids_from_stdin() {
- debug!("Fetching IDs from stdin...");
- let mut iter = rt.ids::<crate::ui::PathProvider>()?
- .ok_or_else(|| err_msg("No ids supplied"))?
- .into_iter()
- .map(Ok);
-
- process(&mut iter)
- } else {
- process(&mut rt.store().entries()?)
+ match rt.ids::<crate::ui::PathProvider>()? {
+ Some(ids) => process(&mut ids.into_iter().map(Ok)),
+ None => process(&mut rt.store().entries()?),
}
}
diff --git a/bin/core/imag-tag/src/ui.rs b/bin/core/imag-tag/src/ui.rs
index ba3a7859..a4a00f21 100644
--- a/bin/core/imag-tag/src/ui.rs
+++ b/bin/core/imag-tag/src/ui.rs
@@ -101,6 +101,26 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
.required(true))
)
+ .subcommand(SubCommand::with_name("present")
+ .about("List entries that have a certain tag")
+ .version("0.1")
+ .arg(Arg::with_name("present-tag")
+ .index(1)
+ .required(true)
+ .multiple(true)
+ .help("Tag to list entries for"))
+ )
+
+ .subcommand(SubCommand::with_name("missing")
+ .about("List entries miss a certain tag")
+ .version("0.1")
+ .arg(Arg::with_name("missing-tag")
+ .index(1)
+ .required(true)
+ .multiple(true)
+ .help("Tag which should be missing in the entries"))
+ )
+
}
pub struct PathProvider;
diff --git a/tests/ui/src/imag_tag.rs b/tests/ui/src/imag_tag.rs
index 352dcf25..77784a1e 100644
--- a/tests/ui/src/imag_tag.rs
+++ b/tests/ui/src/imag_tag.rs
@@ -34,6 +34,14 @@ pub fn call(tmpdir: &TempDir, args: &[&str]) -> Vec<String> {
crate::imag::stdout_of_command(binary)
}
+pub fn call_give_ids(tmpdir: &TempDir, args: &[&str]) -> Vec<String> {
+ let mut binary = binary(tmpdir);
+ binary.stdin(std::process::Stdio::inherit());
+ binary.args(args);
+ debug!("Command = {:?}", binary);
+ crate::imag::stdout_of_command(binary)
+}
+
#[test]
fn test_new_entry_has_no_tags() {
crate::setup_logging();
@@ -94,3 +102,43 @@ fn test_adding_twice_does_not_add_twice() {
assert_eq!(output[0], "tag");
}
+#[test]
+fn test_listing_entries_with_certain_tag() {
+ crate::setup_logging();
+ let imag_home = crate::imag::make_temphome();
+ crate::imag_init::call(&imag_home);
+ crate::imag_create::call(&imag_home, &["test1", "test2", "test3"]);
+ let _ = call(&imag_home, &["test1", "add", "tag1"]);
+ let _ = call(&imag_home, &["test2", "add", "tag2"]);
+ let _ = call(&imag_home, &["test3", "add", "tag3"]);
+
+ let output = call_give_ids(&imag_home, &["present", "tag1"]);
+ debug!("output = {:?}", output);
+
+ assert!(!output.is_empty());
+ assert_eq!(output.len(), 1);
+ assert!(output[0].contains("test1"));
+ assert!(output.iter().all(|s| !s.contains("test2")));
+ assert!(output.iter().all(|s| !s.contains("test3")));
+}
+
+#[test]
+fn test_listing_entries_without_certain_tag() {
+ crate::setup_logging();
+ let imag_home = crate::imag::make_temphome();
+ crate::imag_init::call(&imag_home);
+ crate::imag_create::call(&imag_home, &["test1", "test2", "test3"]);
+ let _ = call(&imag_home, &["test1", "add", "tag1"]);
+ let _ = call(&imag_home, &["test2", "add", "tag2"]);
+ let _ = call(&imag_home, &["test3", "add", "tag3"]);
+
+ let output = call_give_ids(&imag_home, &["missing", "tag1"]);
+ debug!("output = {:?}", output);
+
+ assert!(!output.is_empty());
+ assert_eq!(output.len(), 2);
+ assert!(output.iter().any(|s| s.contains("test2")));
+ assert!(output.iter().any(|s| s.contains("test3")));
+ assert!(output.iter().all(|s| !s.contains("test1")));
+}
+