summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2020-02-29 22:23:55 +0100
committerMatthias Beyer <mail@beyermatthias.de>2020-06-01 13:58:54 +0200
commit779378bcf5a476c2f5fd4b86619c5d22c2e03b28 (patch)
tree173e31802db3033c2f05cb343ef78eff1fe95c52
parent003826cbdf2f2fd8d2695cce73dbf6aba0a45b17 (diff)
Reimplement imag-mail to take queries from stdin
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
-rw-r--r--bin/domain/imag-mail/src/lib.rs129
-rw-r--r--bin/domain/imag-mail/src/ui.rs27
2 files changed, 40 insertions, 116 deletions
diff --git a/bin/domain/imag-mail/src/lib.rs b/bin/domain/imag-mail/src/lib.rs
index 720f7a93..383a7ca8 100644
--- a/bin/domain/imag-mail/src/lib.rs
+++ b/bin/domain/imag-mail/src/lib.rs
@@ -51,21 +51,21 @@ extern crate libimagentryref;
extern crate libimaginteraction;
use std::io::Write;
+use std::io::BufRead;
use failure::Fallible as Result;
use failure::err_msg;
+use failure::Error;
use clap::App;
use resiter::AndThen;
use resiter::IterInnerOkOrElse;
use resiter::Filter;
use resiter::Map;
use handlebars::Handlebars;
-use itertools::Itertools;
use libimagmail::store::MailStore;
use libimagmail::store::Sorting;
use libimagmail::mail::Mail;
-use libimagmail::store::MailStoreWithConnection;
use libimagmail::notmuch::connection::NotmuchConnection;
use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
@@ -153,16 +153,13 @@ fn list(rt: &Runtime) -> Result<()> {
let notmuch_connection = NotmuchConnection::open(notmuch_path)?;
- let find_root = scmd.is_present("find-root");
- debug!("find_root: {}", find_root);
-
let mut out = rt.stdout();
let mut i = 0;
/// Helper function for processing a tree
///
/// This function implements the normal-style printing functionality (without "--tree").
- fn process_nontree<'a, I>(iter: I,
+ fn process<'a, I>(iter: I,
i: &mut usize,
conn: &NotmuchConnection,
list_format: &Handlebars,
@@ -171,7 +168,6 @@ fn list(rt: &Runtime) -> Result<()> {
-> Result<()>
where I: Iterator<Item = Result<FileLockEntry<'a>>>
{
- debug!("Processing non-tree");
iter.and_then_ok(|fle| {
trace!("Loading: {}", fle.get_location());
let loaded = fle.load(&conn)?
@@ -191,78 +187,6 @@ fn list(rt: &Runtime) -> Result<()> {
.map(|_| ())
};
- /// Helper function for processing a tree
- ///
- /// This function implements the tree-style printing functionality ("--tree").
- fn process_tree<'a, I>(iter: I,
- mailstore: &'a MailStoreWithConnection<'a>,
- conn: &NotmuchConnection,
- list_format: &Handlebars,
- rt: &Runtime,
- out: &mut dyn Write,
- find_root: bool)
- -> Result<()>
- where I: Iterator<Item = Result<FileLockEntry<'a>>>
- {
- debug!("Processing tree");
- let iter = if find_root {
- let iter = iter.and_then_ok(|fle| {
- trace!("Loading: {}", fle.get_location());
- let id = fle
- .load(conn)?
- .ok_or_else(|| format_err!("Cannot load mail: {}", fle.get_location()))?
- .parsed()?
- .root_parent(&mailstore)?
- .ok_or_else(|| format_err!("Failed to find root parent: {}", fle.get_location()))
- .map(|p| p.get_id().clone());
-
- drop(fle);
- trace!("Loaded and parsed {:?} successfully", id);
- id
- });
-
- Box::new(iter) as Box<dyn Iterator<Item = Result<String>>>
- } else {
- let iter = iter.and_then_ok(|fle| fle.get_cached_id());
- Box::new(iter) as Box<dyn Iterator<Item = Result<String>>>
- };
-
- trace!("Printing mailtrees now!");
-
- // we have to collect here, so that all FLEs are drop()ed
- let ids = iter.collect::<Result<Vec<String>>>()?;
- trace!("ids = {:?}", ids);
-
- let mss = ids.into_iter()
- .map(|id: String| mailstore.get_mailtree(&id))
- .collect::<Result<Vec<_>>>()?;
- trace!("mss = {:?}", mss);
-
- mss.iter()
- .map(|mailtree| mailtree.traverse())
- .flatten()
- .unique_by(|tpl| tpl.1.clone())
- .sorted_by(|a, b| a.0.cmp(&b.0))
- .enumerate()
- .map(|(i, (indent, mid))| {
- let fle = mailstore.get_mail_by_id(&mid)?.ok_or_else(|| format_err!("Cannot find mail with id = {}", mid))?;
- trace!("Printing: {}", fle.get_location());
-
- let loaded = fle.load(&conn)?
- .ok_or_else(|| format_err!("Mail not found: {}", fle.get_location()))?;
- trace!("Loaded: {}", fle.get_location());
-
- let parsed = loaded.parsed()?;
- trace!("Parsed: {}", fle.get_location());
-
- crate::util::list_mail(&parsed, i, indent, list_format, out)?;
- trace!("Listed: {}", fle.get_location());
- rt.report_touched(fle.get_location())
- })
- .collect::<Result<Vec<()>>>()
- .map(|_| ())
- }
-
if let Some(query) = scmd.value_of("query") {
// Use notmuch to find mails
@@ -276,28 +200,37 @@ fn list(rt: &Runtime) -> Result<()> {
.into_iter()
.map(Ok);
- if tree {
- process_tree(iter, &mailstore, &notmuch_connection, &list_format, rt, &mut out, find_root)
- } else {
- process_nontree(iter, &mut i, &notmuch_connection, &list_format, rt, &mut out)
- }
+ process(iter, &mut i, &notmuch_connection, &list_format, rt, &mut out)
} else {
- // use StoreIds to find mails
- let iter = rt.ids::<crate::ui::PathProvider>()?
- .ok_or_else(|| err_msg("No ids supplied"))?
- .into_iter()
- .map(Ok)
- .into_get_iter(rt.store())
- .map_inner_ok_or_else(|| err_msg("Did not find one entry"))
- .and_then_ok(|m| m.is_mail().map(|b| (b, m)))
- .filter_ok(|tpl| tpl.0)
- .map_ok(|tpl| tpl.1);
-
- if tree {
+ if scmd.is_present("query-from-stdin") {
+ let stdin = ::std::io::stdin();
+
let mailstore = store.with_connection(&notmuch_connection);
- process_tree(iter, &mailstore, &notmuch_connection, &list_format, rt, &mut out, find_root)
+ stdin.lock()
+ .lines()
+ .map(|r| r.map_err(Error::from).and_then(|query| {
+ debug!("Querying: {}", query);
+ let it = mailstore.query(&query)?;
+
+ debug!("Found: {:?}", it);
+ let it = it.into_iter().map(Ok);
+
+ process(it, &mut i, &notmuch_connection, &list_format, rt, &mut out)
+ }))
+ .collect::<Result<_>>()
} else {
- process_nontree(iter, &mut i, &notmuch_connection, &list_format, rt, &mut out)
+ // use StoreIds to find mails
+ let iter = rt.ids::<crate::ui::PathProvider>()?
+ .ok_or_else(|| err_msg("No ids supplied"))?
+ .into_iter()
+ .map(Ok)
+ .into_get_iter(rt.store())
+ .map_inner_ok_or_else(|| err_msg("Did not find one entry"))
+ .and_then_ok(|m| m.is_mail().map(|b| (b, m)))
+ .filter_ok(|tpl| tpl.0)
+ .map_ok(|tpl| tpl.1);
+
+ process(iter, &mut i, &notmuch_connection, &list_format, rt, &mut out)
}
}
}
diff --git a/bin/domain/imag-mail/src/ui.rs b/bin/domain/imag-mail/src/ui.rs
index 22562275..3a928d87 100644
--- a/bin/domain/imag-mail/src/ui.rs
+++ b/bin/domain/imag-mail/src/ui.rs
@@ -24,7 +24,7 @@ use libimagstore::storeid::StoreId;
use libimagrt::runtime::IdPathProvider;
use libimagstore::storeid::IntoStoreId;
-use clap::{Arg, ArgMatches, ArgGroup, App, SubCommand};
+use clap::{Arg, ArgMatches, App, SubCommand};
pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
app
@@ -66,6 +66,14 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
.value_name("ID")
.help("List mails by Store ID (non-mails will be ignored)"))
+ .arg(Arg::with_name("query-from-stdin")
+ .long("stdin")
+ .short("S")
+ .takes_value(false)
+ .required(false)
+ .multiple(false)
+ .help("Expect notmuch queries linewise on STDIN, for example from 'notmuch search --output=messages <query>'"))
+
.arg(Arg::with_name("format")
.long("format")
.short("f")
@@ -74,23 +82,6 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
.value_name("FORMAT")
.help("Format to list entries with (default in config)"))
- .arg(Arg::with_name("tree")
- .long("tree")
- .short("t")
- .takes_value(false)
- .required(false)
- .help("Build a tree and print that. When this argument is passed, the {{indent}} variable is available in the format, otherwise it is set always to zero"))
-
- .arg(Arg::with_name("find-root")
- .long("find-root")
- .short("R")
- .takes_value(false)
- .required(false)
- .help("If --tree is passed, this can be used to tell imag to find the root message and starting the tree there instead of the passed mail"))
-
- .group(ArgGroup::with_name("tree-root")
- .arg("find-root")
- .requires("tree"))
)
.subcommand(SubCommand::with_name("print-id")