Rewrite imag-mail

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
This commit is contained in:
Matthias Beyer 2019-02-20 14:42:16 +01:00
parent a819aeb6ed
commit a959167b15
4 changed files with 184 additions and 64 deletions

View file

@ -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"]

View file

@ -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 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 path = scmd.value_of("path").unwrap(); // enforced by clap
let store = rt.store();
let mail = Mail::import_from_path(rt.store(), path)
debug!(r#"Importing mail with
collection_name = {}
refconfig = {:?}
"#, collection_name, refconfig);
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();
let _ = rt.report_touched(mail.fle().get_location()).unwrap_or_exit();
.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");
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
fn list_mail(rt: &Runtime, m: Mail) {
let id = match m.get_message_id() {
//
// 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) => "<no id>".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) => "<no from>".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) => "<no to>".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) => "<no subject>".to_owned(),
Err(e) => {
@ -139,32 +185,53 @@ fn list(rt: &Runtime) {
},
};
if print_content {
use libimagmail::hasher::MailHasher;
let content = m.as_ref_with_hasher::<MailHasher>()
.get_path(&refconfig)
.and_then(util::get_mail_text_content)
.map_err_trace_exit_unwrap();
writeln!(rt.stdout(),
"Mail: {id}\n\tFrom: {from}\n\tTo: {to}\n\t{subj}\n",
"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.fle().get_location()).unwrap_or_exit();
}
let _ = rt.store()
.entries()
let _ = rt.report_touched(m.get_location()).unwrap_or_exit();
}
if rt.ids_from_stdin() {
let iter = rt.ids::<::ui::PathProvider>()
.map_err_trace_exit_unwrap()
.trace_unwrap_exit()
.filter(|id| id.is_in_collection(&["mail"]))
.filter_map(|id| {
.into_iter()
.map(Ok);
StoreIdIterator::new(Box::new(iter))
} else {
rt.store()
.get(id)
.context(err_msg("Ref handling error"))
.map_err(Error::from)
.all_mails()
.map_err_trace_exit_unwrap()
.map(|fle| Mail::from_fle(fle).map_err_trace().ok())
})
.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, m));
.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<String> {
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))
}

View file

@ -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<StoreId> {
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![]
}
}
}

View file

@ -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"