libimagmailfrontend/
lib.rs1#![forbid(unsafe_code)]
21
22#![deny(
23 non_camel_case_types,
24 non_snake_case,
25 path_statements,
26 trivial_numeric_casts,
27 unstable_features,
28 unused_allocation,
29 unused_import_braces,
30 unused_imports,
31 unused_must_use,
32 unused_mut,
33 unused_qualifications,
34 while_true,
35)]
36
37extern crate clap;
38#[macro_use] extern crate log;
39#[macro_use] extern crate failure;
40extern crate toml_query;
41#[macro_use] extern crate indoc;
42extern crate resiter;
43
44extern crate libimagrt;
45extern crate libimagmail;
46extern crate libimagerror;
47extern crate libimagstore;
48extern crate libimagutil;
49extern crate libimagentryref;
50
51use std::io::Write;
52use std::path::PathBuf;
53
54use failure::Fallible as Result;
55use failure::err_msg;
56use failure::Error;
57use toml_query::read::TomlValueReadTypeExt;
58use clap::App;
59use resiter::AndThen;
60use resiter::IterInnerOkOrElse;
61
62use libimagmail::mail::Mail;
63use libimagmail::store::MailStore;
64use libimagmail::util;
65use libimagentryref::reference::{Ref, RefFassade};
66use libimagentryref::util::get_ref_config;
67use libimagrt::runtime::Runtime;
68use libimagrt::application::ImagApplication;
69use libimagutil::info_result::*;
70use libimagstore::store::FileLockEntry;
71use libimagstore::storeid::StoreIdIterator;
72use libimagstore::iter::get::StoreIdGetIteratorExtension;
73
74mod ui;
75
76pub enum ImagMail {}
81impl ImagApplication for ImagMail {
82 fn run(rt: Runtime) -> Result<()> {
83 match rt.cli().subcommand_name().ok_or_else(|| err_msg("No subcommand called"))? {
84 "import-mail" => import_mail(&rt),
85 "list" => list(&rt),
86 "mail-store" => mail_store(&rt),
87 other => {
88 debug!("Unknown command");
89 if rt.handle_unknown_subcommand("imag-mail", other, rt.cli())?.success() {
90 Ok(())
91 } else {
92 Err(err_msg("Failed to handle unknown subcommand"))
93 }
94 },
95 }
96 }
97
98 fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
99 ui::build_ui(app)
100 }
101
102 fn name() -> &'static str {
103 env!("CARGO_PKG_NAME")
104 }
105
106 fn description() -> &'static str {
107 "Mail collection tool"
108 }
109
110 fn version() -> &'static str {
111 env!("CARGO_PKG_VERSION")
112 }
113}
114
115
116fn import_mail(rt: &Runtime) -> Result<()> {
117 let collection_name = get_ref_collection_name(rt)?;
118 let refconfig = get_ref_config(rt, "imag-mail")?;
119 let scmd = rt.cli().subcommand_matches("import-mail").unwrap();
120 let store = rt.store();
121
122 debug!(r#"Importing mail with
123 collection_name = {}
124 refconfig = {:?}
125 "#, collection_name, refconfig);
126
127 scmd.values_of("path")
128 .unwrap() .map(PathBuf::from)
130 .map(|path| {
131 if scmd.is_present("ignore-existing-ids") {
132 store.retrieve_mail_from_path(path, &collection_name, &refconfig)
133 } else {
134 store.create_mail_from_path(path, &collection_name, &refconfig)
135 }
136 .map_info_str("Ok")
137 })
138 .and_then_ok(|e| rt.report_touched(e.get_location()).map_err(Error::from))
139 .collect::<Result<Vec<()>>>()
140 .map(|_| ())
141}
142
143fn list(rt: &Runtime) -> Result<()> {
144 let refconfig = get_ref_config(rt, "imag-mail")?;
145 let scmd = rt.cli().subcommand_matches("list").unwrap(); let print_content = scmd.is_present("list-read");
147
148 if print_content {
149 warn!("{}", indoc!(r#"You requested to print the content of the mail as well.
151 We use the 'mailparse' crate underneath, but its implementation is nonoptimal.
152 Thus, the content might be printed as empty (no text in the email)
153 This is not reliable and might be wrong."#));
154
155 }
157
158 fn list_mail<'a>(rt: &Runtime,
163 refconfig: &::libimagentryref::reference::Config,
164 m: &FileLockEntry<'a>,
165 print_content: bool) -> Result<()> {
166
167 let id = match m.get_message_id(&refconfig)? {
168 Some(f) => f,
169 None => "<no id>".to_owned(),
170 };
171
172 let from = match m.get_from(&refconfig)? {
173 Some(f) => f,
174 None => "<no from>".to_owned(),
175 };
176
177 let to = match m.get_to(&refconfig)? {
178 Some(f) => f,
179 None => "<no to>".to_owned(),
180 };
181
182 let subject = match m.get_subject(&refconfig)? {
183 Some(f) => f,
184 None => "<no subject>".to_owned(),
185 };
186
187 if print_content {
188 use libimagmail::hasher::MailHasher;
189
190 let content = m.as_ref_with_hasher::<MailHasher>()
191 .get_path(&refconfig)
192 .and_then(util::get_mail_text_content)?;
193
194 writeln!(rt.stdout(),
195 "Mail: {id}\nFrom: {from}\nTo: {to}\n{subj}\n---\n{content}\n---\n",
196 from = from,
197 id = id,
198 subj = subject,
199 to = to,
200 content = content
201 )?;
202 } else {
203 writeln!(rt.stdout(),
204 "Mail: {id}\nFrom: {from}\nTo: {to}\n{subj}\n",
205 from = from,
206 id = id,
207 subj = subject,
208 to = to
209 )?;
210 }
211
212 rt.report_touched(m.get_location())?;
213 Ok(())
214 }
215
216 if rt.ids_from_stdin() {
217 let iter = rt
218 .ids::<crate::ui::PathProvider>()?
219 .ok_or_else(|| err_msg("No ids supplied"))?
220 .into_iter()
221 .map(Ok);
222
223 StoreIdIterator::new(Box::new(iter))
224 } else {
225 rt.store()
226 .all_mails()?
227 .into_storeid_iter()
228 }
229 .inspect(|id| debug!("Found: {:?}", id))
230 .into_get_iter(rt.store())
231 .map_inner_ok_or_else(|| err_msg("Did not find one entry"))
232 .and_then_ok(|m| list_mail(&rt, &refconfig, &m, print_content))
233 .collect::<Result<Vec<_>>>()
234 .map(|_| ())
235}
236
237fn mail_store(rt: &Runtime) -> Result<()> {
238 let _ = rt.cli().subcommand_matches("mail-store").unwrap();
239 Err(format_err!("This feature is currently not implemented."))
240}
241
242fn get_ref_collection_name(rt: &Runtime) -> Result<String> {
243 let setting_name = "mail.ref_collection_name";
244
245 debug!("Getting configuration: {}", setting_name);
246
247 rt.config()
248 .ok_or_else(|| format_err!("No configuration, cannot find collection name for mail collection"))?
249 .read_string(setting_name)?
250 .ok_or_else(|| format_err!("Setting missing: {}", setting_name))
251}
252