From a959167b15d7ecfeb552253033f33cf0ece13e30 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 20 Feb 2019 14:42:16 +0100 Subject: [PATCH] Rewrite imag-mail Signed-off-by: Matthias Beyer --- bin/domain/imag-mail/Cargo.toml | 8 ++ bin/domain/imag-mail/src/main.rs | 165 +++++++++++++++++++++++-------- bin/domain/imag-mail/src/ui.rs | 70 ++++++++----- imagrc.toml | 5 + 4 files changed, 184 insertions(+), 64 deletions(-) diff --git a/bin/domain/imag-mail/Cargo.toml b/bin/domain/imag-mail/Cargo.toml index 68836c55..5faa653d 100644 --- a/bin/domain/imag-mail/Cargo.toml +++ b/bin/domain/imag-mail/Cargo.toml @@ -24,14 +24,22 @@ maintenance = { status = "actively-developed" } [dependencies] log = "0.4.0" failure = "0.1" +indoc = "0.3" libimagrt = { version = "0.10.0", path = "../../../lib/core/libimagrt" } +libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" } libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" } libimagmail = { version = "0.10.0", path = "../../../lib/domain/libimagmail" } libimagutil = { version = "0.10.0", path = "../../../lib/etc/libimagutil" } +libimagentryref = { version = "0.10.0", path = "../../../lib/entry/libimagentryref" } [dependencies.clap] version = "^2.29" default-features = false features = ["color", "suggestions", "wrap_help"] +[dependencies.toml-query] +version = "0.8" +default-features = false +features = ["typed"] + diff --git a/bin/domain/imag-mail/src/main.rs b/bin/domain/imag-mail/src/main.rs index d9b9dfbd..59d7f649 100644 --- a/bin/domain/imag-mail/src/main.rs +++ b/bin/domain/imag-mail/src/main.rs @@ -36,26 +36,38 @@ extern crate clap; #[macro_use] extern crate log; -extern crate failure; +#[macro_use] extern crate failure; +extern crate toml_query; +#[macro_use] extern crate indoc; #[macro_use] extern crate libimagrt; extern crate libimagmail; extern crate libimagerror; +extern crate libimagstore; extern crate libimagutil; +extern crate libimagentryref; use std::io::Write; +use std::path::PathBuf; -use failure::Error; -use failure::err_msg; +use failure::Fallible as Result; +use toml_query::read::TomlValueReadExt; +use toml_query::read::TomlValueReadTypeExt; use libimagerror::trace::{MapErrTrace, trace_error}; use libimagerror::iter::TraceIterator; use libimagerror::exit::ExitUnwrap; use libimagerror::io::ToExitCode; use libimagmail::mail::Mail; +use libimagmail::store::MailStore; +use libimagmail::util; +use libimagentryref::reference::{Ref, RefFassade}; use libimagrt::runtime::Runtime; use libimagrt::setup::generate_runtime_setup; use libimagutil::info_result::*; +use libimagstore::store::FileLockEntry; +use libimagstore::storeid::StoreIdIterator; +use libimagstore::iter::get::StoreIdGetIteratorExtension; mod ui; @@ -88,22 +100,56 @@ fn main() { } fn import_mail(rt: &Runtime) { - let scmd = rt.cli().subcommand_matches("import-mail").unwrap(); - let path = scmd.value_of("path").unwrap(); // enforced by clap + let collection_name = get_ref_collection_name(rt).map_err_trace_exit_unwrap(); + let refconfig = get_ref_config(rt).map_err_trace_exit_unwrap(); + let scmd = rt.cli().subcommand_matches("import-mail").unwrap(); + let store = rt.store(); - let mail = Mail::import_from_path(rt.store(), path) - .map_info_str("Ok") - .map_err_trace_exit_unwrap(); + debug!(r#"Importing mail with + collection_name = {} + refconfig = {:?} + "#, collection_name, refconfig); - let _ = rt.report_touched(mail.fle().get_location()).unwrap_or_exit(); + scmd.values_of("path") + .unwrap() // enforced by clap + .map(PathBuf::from) + .map(|path| { + if scmd.is_present("ignore_existing_ids") { + store.retrieve_mail_from_path(path, &collection_name, &refconfig) + } else { + store.create_mail_from_path(path, &collection_name, &refconfig) + } + .map_info_str("Ok") + .map_err_trace_exit_unwrap() + }) + .for_each(|entry| rt.report_touched(entry.get_location()).unwrap_or_exit()); } fn list(rt: &Runtime) { - use failure::ResultExt; + let refconfig = get_ref_config(rt).map_err_trace_exit_unwrap(); + let scmd = rt.cli().subcommand_matches("list").unwrap(); // safe via clap + let print_content = scmd.is_present("list-read"); - // TODO: Implement lister type in libimagmail for this - fn list_mail(rt: &Runtime, m: Mail) { - let id = match m.get_message_id() { + if print_content { + /// TODO: Check whether workaround with "{}" is still necessary when updating "indoc" + warn!("{}", indoc!(r#"You requested to print the content of the mail as well. + We use the 'mailparse' crate underneath, but its implementation is nonoptimal. + Thus, the content might be printed as empty (no text in the email) + This is not reliable and might be wrong."#)); + + // TODO: Fix above. + } + + // TODO: Implement lister type in libimagmail for this + // + // Optimization: Pass refconfig here instead of call get_ref_config() in lister function. This + // way we do not call get_ref_config() multiple times. + fn list_mail<'a>(rt: &Runtime, + refconfig: &::libimagentryref::reference::Config, + m: &FileLockEntry<'a>, + print_content: bool) { + + let id = match m.get_message_id(&refconfig) { Ok(Some(f)) => f, Ok(None) => "".to_owned(), Err(e) => { @@ -112,7 +158,7 @@ fn list(rt: &Runtime) { }, }; - let from = match m.get_from() { + let from = match m.get_from(&refconfig) { Ok(Some(f)) => f, Ok(None) => "".to_owned(), Err(e) => { @@ -121,7 +167,7 @@ fn list(rt: &Runtime) { }, }; - let to = match m.get_to() { + let to = match m.get_to(&refconfig) { Ok(Some(f)) => f, Ok(None) => "".to_owned(), Err(e) => { @@ -130,7 +176,7 @@ fn list(rt: &Runtime) { }, }; - let subject = match m.get_subject() { + let subject = match m.get_subject(&refconfig) { Ok(Some(f)) => f, Ok(None) => "".to_owned(), Err(e) => { @@ -139,32 +185,53 @@ fn list(rt: &Runtime) { }, }; - writeln!(rt.stdout(), - "Mail: {id}\n\tFrom: {from}\n\tTo: {to}\n\t{subj}\n", - from = from, - id = id, - subj = subject, - to = to - ).to_exit_code().unwrap_or_exit(); + if print_content { + use libimagmail::hasher::MailHasher; - let _ = rt.report_touched(m.fle().get_location()).unwrap_or_exit(); + let content = m.as_ref_with_hasher::() + .get_path(&refconfig) + .and_then(util::get_mail_text_content) + .map_err_trace_exit_unwrap(); + + writeln!(rt.stdout(), + "Mail: {id}\nFrom: {from}\nTo: {to}\n{subj}\n---\n{content}\n---\n", + from = from, + id = id, + subj = subject, + to = to, + content = content + ).to_exit_code().unwrap_or_exit(); + } else { + writeln!(rt.stdout(), + "Mail: {id}\nFrom: {from}\nTo: {to}\n{subj}\n", + from = from, + id = id, + subj = subject, + to = to + ).to_exit_code().unwrap_or_exit(); + } + + let _ = rt.report_touched(m.get_location()).unwrap_or_exit(); } - let _ = rt.store() - .entries() - .map_err_trace_exit_unwrap() - .trace_unwrap_exit() - .filter(|id| id.is_in_collection(&["mail"])) - .filter_map(|id| { - rt.store() - .get(id) - .context(err_msg("Ref handling error")) - .map_err(Error::from) - .map_err_trace_exit_unwrap() - .map(|fle| Mail::from_fle(fle).map_err_trace().ok()) - }) - .filter_map(|e| e) - .for_each(|m| list_mail(&rt, m)); + if rt.ids_from_stdin() { + let iter = rt.ids::<::ui::PathProvider>() + .map_err_trace_exit_unwrap() + .into_iter() + .map(Ok); + + StoreIdIterator::new(Box::new(iter)) + } else { + rt.store() + .all_mails() + .map_err_trace_exit_unwrap() + .into_storeid_iter() + } + .map(|id| { debug!("Found: {:?}", id); id }) + .into_get_iter(rt.store()) + .trace_unwrap_exit() + .filter_map(|e| e) + .for_each(|m| list_mail(&rt, &refconfig, &m, print_content)); } fn mail_store(rt: &Runtime) { @@ -173,3 +240,23 @@ fn mail_store(rt: &Runtime) { unimplemented!() } +fn get_ref_collection_name(rt: &Runtime) -> Result { + let setting_name = "mail.ref_collection_name"; + + debug!("Getting configuration: {}", setting_name); + + rt.config() + .ok_or_else(|| format_err!("No configuration, cannot find collection name for mail collection"))? + .read_string(setting_name)? + .ok_or_else(|| format_err!("Setting missing: {}", setting_name)) +} + +fn get_ref_config(rt: &Runtime) -> Result<::libimagentryref::reference::Config> { + let setting_name = "ref.basepathes"; + + rt.config() + .ok_or_else(|| format_err!("No configuration, cannot find collection name for mail collection"))? + .read_deserialized::<::libimagentryref::reference::Config>(setting_name)? + .ok_or_else(|| format_err!("Setting missing: {}", setting_name)) +} + diff --git a/bin/domain/imag-mail/src/ui.rs b/bin/domain/imag-mail/src/ui.rs index 938764eb..54a89b63 100644 --- a/bin/domain/imag-mail/src/ui.rs +++ b/bin/domain/imag-mail/src/ui.rs @@ -17,19 +17,33 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use clap::{Arg, App, SubCommand}; +use std::path::PathBuf; + +use libimagstore::storeid::StoreId; +use libimagrt::runtime::IdPathProvider; +use libimagstore::storeid::IntoStoreId; +use libimagerror::trace::MapErrTrace; + +use clap::{Arg, ArgMatches, App, SubCommand}; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { app .subcommand(SubCommand::with_name("import-mail") .about("Import a mail (create a reference to it) (Maildir)") .version("0.1") + .arg(Arg::with_name("ignore-existing-ids") + .long("ignore-existing") + .short("I") + .takes_value(false) + .required(false) + .help("Ignore errors that might occur when store entries exist already")) + .arg(Arg::with_name("path") - .long("path") - .short("p") + .index(1) .takes_value(true) + .multiple(true) .required(true) - .help("Path to the mail file or a directory which is then searched recursively") + .help("Path to the mail file(s) to import") .value_name("PATH")) ) @@ -37,28 +51,20 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .about("List all stored references to mails") .version("0.1") - // TODO: Thee following four arguments are the same as in imag-ref. - // We should make these importable from libimagentryref. + .arg(Arg::with_name("list-read") + .long("read") + .short("r") + .takes_value(false) + .required(false) + .multiple(false) + .help("Print the textual content of the mail itself as well")) - .arg(Arg::with_name("check-dead") - .long("check-dead") - .short("d") - .help("Check each reference whether it is dead")) - - .arg(Arg::with_name("check-changed") - .long("check-changed") - .short("c") - .help("Check whether a reference had changed (content or permissions)")) - - .arg(Arg::with_name("check-changed-content") - .long("check-changed-content") - .short("C") - .help("Check whether the content of the referenced file changed")) - - .arg(Arg::with_name("check-changed-permissions") - .long("check-changed-perms") - .short("P") - .help("Check whether the permissions of the referenced file changed")) + .arg(Arg::with_name("list-id") + .index(1) + .takes_value(true) + .required(false) + .multiple(true) + .help("The ids of the mails to list information for")) ) @@ -72,3 +78,17 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { ) } +pub struct PathProvider; +impl IdPathProvider for PathProvider { + fn get_ids(matches: &ArgMatches) -> Vec { + if matches.is_present("list-id") { + matches.values_of("list-id") + .unwrap() + .map(|s| PathBuf::from(s).into_storeid().map_err_trace_exit_unwrap()) + .collect() + } else { + vec![] + } + } +} + diff --git a/imagrc.toml b/imagrc.toml index b1c5478d..f1d3be91 100644 --- a/imagrc.toml +++ b/imagrc.toml @@ -355,4 +355,9 @@ execute_in_store = false # The base pathes define the search pathes for libimagentryref [ref.basepathes] music = "/home/user/music" +mail = "/home/user/mail" + +[mail] +# The name of the mail reference collection +ref_collection_name = "mail"