mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-25 12:30:22 +00:00
use env_logger for plain output fmt (#126)
This commit is contained in:
parent
477b7748de
commit
15c635eb1d
16 changed files with 318 additions and 228 deletions
|
@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Custom config path [#86]
|
- Custom config path [#86]
|
||||||
- Setting idle-hook-cmds
|
- Setting idle-hook-cmds
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Plain logger with `env_logger` [#126]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Improve config compatibility on Windows [#111](https://github.com/soywod/himalaya/pull/111)
|
- Improve config compatibility on Windows [#111](https://github.com/soywod/himalaya/pull/111)
|
||||||
|
@ -193,3 +197,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
[#89]: https://github.com/soywod/himalaya/issues/89
|
[#89]: https://github.com/soywod/himalaya/issues/89
|
||||||
[#96]: https://github.com/soywod/himalaya/issues/96
|
[#96]: https://github.com/soywod/himalaya/issues/96
|
||||||
[#100]: https://github.com/soywod/himalaya/issues/100
|
[#100]: https://github.com/soywod/himalaya/issues/100
|
||||||
|
[#126]: https://github.com/soywod/himalaya/issues/126
|
||||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -308,6 +308,7 @@ dependencies = [
|
||||||
name = "himalaya"
|
name = "himalaya"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"error-chain",
|
"error-chain",
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
[package]
|
[package]
|
||||||
name = "himalaya"
|
name = "himalaya"
|
||||||
description = "📫 Minimalist CLI email client"
|
description = "📫 The CLI email client."
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
authors = ["soywod <clement.douin@posteo.net>"]
|
authors = ["soywod <clement.douin@posteo.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
chrono = "0.4.19"
|
||||||
clap = {version = "2.33.3", default-features = false, features = ["suggestions"]}
|
clap = {version = "2.33.3", default-features = false, features = ["suggestions"]}
|
||||||
env_logger = "0.8.3"
|
env_logger = "0.8.3"
|
||||||
error-chain = "0.12.4"
|
error-chain = "0.12.4"
|
||||||
|
|
|
@ -15,17 +15,17 @@ pub fn comp_subcmds<'s>() -> Vec<App<'s, 's>> {
|
||||||
|
|
||||||
pub fn comp_matches(mut app: App, matches: &ArgMatches) -> Result<bool> {
|
pub fn comp_matches(mut app: App, matches: &ArgMatches) -> Result<bool> {
|
||||||
if let Some(matches) = matches.subcommand_matches("completion") {
|
if let Some(matches) = matches.subcommand_matches("completion") {
|
||||||
debug!("[comp::cli::matches] completion command matched");
|
debug!("completion command matched");
|
||||||
let shell = match matches.value_of("shell").unwrap() {
|
let shell = match matches.value_of("shell").unwrap() {
|
||||||
"fish" => Shell::Fish,
|
"fish" => Shell::Fish,
|
||||||
"zsh" => Shell::Zsh,
|
"zsh" => Shell::Zsh,
|
||||||
"bash" | _ => Shell::Bash,
|
"bash" | _ => Shell::Bash,
|
||||||
};
|
};
|
||||||
debug!("[comp::cli::matches] shell: {}", shell);
|
debug!("shell: {}", shell);
|
||||||
app.gen_completions_to("himalaya", shell, &mut io::stdout());
|
app.gen_completions_to("himalaya", shell, &mut io::stdout());
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("[comp::cli::matches] nothing matched");
|
debug!("nothing matched");
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,8 @@ pub struct Account {
|
||||||
|
|
||||||
impl Account {
|
impl Account {
|
||||||
pub fn imap_addr(&self) -> (&str, u16) {
|
pub fn imap_addr(&self) -> (&str, u16) {
|
||||||
|
debug!("host: {}", self.imap_host);
|
||||||
|
debug!("port: {}", self.imap_port);
|
||||||
(&self.imap_host, self.imap_port)
|
(&self.imap_host, self.imap_port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,17 +58,23 @@ impl Account {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn imap_starttls(&self) -> bool {
|
pub fn imap_starttls(&self) -> bool {
|
||||||
match self.imap_starttls {
|
let starttls = match self.imap_starttls {
|
||||||
Some(true) => true,
|
Some(true) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
debug!("STARTTLS: {}", starttls);
|
||||||
|
starttls
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn imap_insecure(&self) -> bool {
|
pub fn imap_insecure(&self) -> bool {
|
||||||
match self.imap_insecure {
|
let insecure = match self.imap_insecure {
|
||||||
Some(true) => true,
|
Some(true) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
debug!("insecure: {}", insecure);
|
||||||
|
insecure
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn smtp_creds(&self) -> Result<SmtpCredentials> {
|
pub fn smtp_creds(&self) -> Result<SmtpCredentials> {
|
||||||
|
|
|
@ -48,13 +48,13 @@ pub fn flag_subcmds<'s>() -> Vec<App<'s, 's>> {
|
||||||
|
|
||||||
pub fn flag_matches(account: &Account, mbox: &str, matches: &ArgMatches) -> Result<bool> {
|
pub fn flag_matches(account: &Account, mbox: &str, matches: &ArgMatches) -> Result<bool> {
|
||||||
if let Some(matches) = matches.subcommand_matches("set") {
|
if let Some(matches) = matches.subcommand_matches("set") {
|
||||||
debug!("[flag::cli::matches] set command matched");
|
debug!("set command matched");
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
debug!("[flag::cli::matches] uid: {}", uid);
|
debug!("uid: {}", uid);
|
||||||
|
|
||||||
let flags = matches.value_of("flags").unwrap();
|
let flags = matches.value_of("flags").unwrap();
|
||||||
debug!("[flag::cli::matches] flags: {}", flags);
|
debug!("flags: {}", flags);
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
imap_conn.set_flags(mbox, uid, flags)?;
|
imap_conn.set_flags(mbox, uid, flags)?;
|
||||||
|
@ -64,13 +64,13 @@ pub fn flag_matches(account: &Account, mbox: &str, matches: &ArgMatches) -> Resu
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("add") {
|
if let Some(matches) = matches.subcommand_matches("add") {
|
||||||
debug!("[flag::cli::matches] add command matched");
|
debug!("add command matched");
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
debug!("[flag::cli::matches] uid: {}", uid);
|
debug!("uid: {}", uid);
|
||||||
|
|
||||||
let flags = matches.value_of("flags").unwrap();
|
let flags = matches.value_of("flags").unwrap();
|
||||||
debug!("[flag::cli::matches] flags: {}", flags);
|
debug!("flags: {}", flags);
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
imap_conn.add_flags(mbox, uid, flags)?;
|
imap_conn.add_flags(mbox, uid, flags)?;
|
||||||
|
@ -80,13 +80,13 @@ pub fn flag_matches(account: &Account, mbox: &str, matches: &ArgMatches) -> Resu
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("remove") {
|
if let Some(matches) = matches.subcommand_matches("remove") {
|
||||||
debug!("[flag::cli::matches] remove command matched");
|
debug!("remove command matched");
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
debug!("[flag::cli::matches] uid: {}", uid);
|
debug!("uid: {}", uid);
|
||||||
|
|
||||||
let flags = matches.value_of("flags").unwrap();
|
let flags = matches.value_of("flags").unwrap();
|
||||||
debug!("[flag::cli::matches] flags: {}", flags);
|
debug!("flags: {}", flags);
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
imap_conn.remove_flags(mbox, uid, flags)?;
|
imap_conn.remove_flags(mbox, uid, flags)?;
|
||||||
|
@ -95,6 +95,6 @@ pub fn flag_matches(account: &Account, mbox: &str, matches: &ArgMatches) -> Resu
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("[flag::cli::matches] nothing matched");
|
debug!("nothing matched");
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,13 +25,13 @@ pub fn imap_matches(
|
||||||
matches: &ArgMatches,
|
matches: &ArgMatches,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
if let Some(_) = matches.subcommand_matches("idle") {
|
if let Some(_) = matches.subcommand_matches("idle") {
|
||||||
debug!("[imap::cli::matches] idle command matched");
|
debug!("idle command matched");
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
imap_conn.idle(&config, &mbox)?;
|
imap_conn.idle(&config, &mbox)?;
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("[imap::cli::matches] nothing matched");
|
debug!("nothing matched");
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,12 +25,15 @@ pub struct ImapConnector<'a> {
|
||||||
|
|
||||||
impl<'ic> ImapConnector<'ic> {
|
impl<'ic> ImapConnector<'ic> {
|
||||||
pub fn new(account: &'ic Account) -> Result<Self> {
|
pub fn new(account: &'ic Account) -> Result<Self> {
|
||||||
|
debug!("create TLS builder");
|
||||||
|
let insecure = account.imap_insecure();
|
||||||
let tls = TlsConnector::builder()
|
let tls = TlsConnector::builder()
|
||||||
.danger_accept_invalid_certs(account.imap_insecure())
|
.danger_accept_invalid_certs(insecure)
|
||||||
.danger_accept_invalid_hostnames(account.imap_insecure())
|
.danger_accept_invalid_hostnames(insecure)
|
||||||
.build()
|
.build()
|
||||||
.chain_err(|| "Cannot create TLS connector")?;
|
.chain_err(|| "Cannot create TLS connector")?;
|
||||||
|
|
||||||
|
debug!("create client");
|
||||||
let client = if account.imap_starttls() {
|
let client = if account.imap_starttls() {
|
||||||
imap::connect_starttls(account.imap_addr(), &account.imap_host, &tls)
|
imap::connect_starttls(account.imap_addr(), &account.imap_host, &tls)
|
||||||
.chain_err(|| "Cannot connect using STARTTLS")
|
.chain_err(|| "Cannot connect using STARTTLS")
|
||||||
|
@ -39,6 +42,7 @@ impl<'ic> ImapConnector<'ic> {
|
||||||
.chain_err(|| "Cannot connect using TLS")
|
.chain_err(|| "Cannot connect using TLS")
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
|
debug!("create session");
|
||||||
let sess = client
|
let sess = client
|
||||||
.login(&account.imap_login, &account.imap_passwd()?)
|
.login(&account.imap_login, &account.imap_passwd()?)
|
||||||
.map_err(|res| res.0)
|
.map_err(|res| res.0)
|
||||||
|
@ -48,6 +52,7 @@ impl<'ic> ImapConnector<'ic> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn logout(&mut self) {
|
pub fn logout(&mut self) {
|
||||||
|
debug!("logout");
|
||||||
match self.sess.logout() {
|
match self.sess.logout() {
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -108,19 +113,19 @@ impl<'ic> ImapConnector<'ic> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn idle(&mut self, config: &Config, mbox: &str) -> Result<()> {
|
pub fn idle(&mut self, config: &Config, mbox: &str) -> Result<()> {
|
||||||
debug!("[imap::model::idle] begin");
|
debug!("begin");
|
||||||
|
|
||||||
debug!("[imap::model::idle] examine mailbox {}", mbox);
|
debug!("examine mailbox {}", mbox);
|
||||||
self.sess
|
self.sess
|
||||||
.examine(mbox)
|
.examine(mbox)
|
||||||
.chain_err(|| format!("Could not examine mailbox `{}`", mbox))?;
|
.chain_err(|| format!("Could not examine mailbox `{}`", mbox))?;
|
||||||
|
|
||||||
debug!("[imap::model::idle] init message hashset");
|
debug!("init message hashset");
|
||||||
let mut msg_set: HashSet<u32> = HashSet::from_iter(self.search_new_msgs()?.iter().cloned());
|
let mut msg_set: HashSet<u32> = HashSet::from_iter(self.search_new_msgs()?.iter().cloned());
|
||||||
trace!("[imap::model::idle] {:?}", msg_set);
|
trace!("{:?}", msg_set);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
debug!("[imap::model::idle] begin loop");
|
debug!("begin loop");
|
||||||
|
|
||||||
self.sess
|
self.sess
|
||||||
.idle()
|
.idle()
|
||||||
|
@ -132,11 +137,8 @@ impl<'ic> ImapConnector<'ic> {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|seq| msg_set.get(&seq).is_none())
|
.filter(|seq| msg_set.get(&seq).is_none())
|
||||||
.collect();
|
.collect();
|
||||||
debug!(
|
debug!("found {} new messages not in hashset", new_msgs.len());
|
||||||
"[imap::model::idle] found {} new messages not in hashset",
|
trace!("messages: {:?}", new_msgs);
|
||||||
new_msgs.len()
|
|
||||||
);
|
|
||||||
trace!("[imap::model::idle] {:?}", new_msgs);
|
|
||||||
|
|
||||||
if !new_msgs.is_empty() {
|
if !new_msgs.is_empty() {
|
||||||
let new_msgs = new_msgs
|
let new_msgs = new_msgs
|
||||||
|
@ -152,20 +154,17 @@ impl<'ic> ImapConnector<'ic> {
|
||||||
for fetch in fetches.iter() {
|
for fetch in fetches.iter() {
|
||||||
let msg = Msg::from(fetch);
|
let msg = Msg::from(fetch);
|
||||||
config.run_notify_cmd(&msg.subject, &msg.sender)?;
|
config.run_notify_cmd(&msg.subject, &msg.sender)?;
|
||||||
debug!("[imap::model::idle] notify message {}", fetch.message);
|
debug!("notify message {}", fetch.message);
|
||||||
trace!("[imap::model::idle] {:?}", msg);
|
trace!("message: {:?}", msg);
|
||||||
|
|
||||||
debug!(
|
debug!("insert msg {} to hashset", fetch.message);
|
||||||
"[imap::model::idle] insert msg {} to hashset",
|
|
||||||
fetch.message
|
|
||||||
);
|
|
||||||
msg_set.insert(fetch.message);
|
msg_set.insert(fetch.message);
|
||||||
trace!("[imap::model::idle] {:?}", msg_set);
|
trace!("messages: {:?}", msg_set);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config.exec_idle_hooks()?;
|
config.exec_idle_hooks()?;
|
||||||
debug!("[imap::model::idle] end loop");
|
debug!("end loop");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -60,30 +60,32 @@ fn run() -> Result<()> {
|
||||||
let matches = app.get_matches();
|
let matches = app.get_matches();
|
||||||
|
|
||||||
let output_fmt: OutputFmt = matches.value_of("output").unwrap().into();
|
let output_fmt: OutputFmt = matches.value_of("output").unwrap().into();
|
||||||
let log_level: LogLevel = matches.value_of("log").unwrap().into();
|
let log_level: LogLevel = matches.value_of("log-level").unwrap().into();
|
||||||
let custom_config: Option<PathBuf> = matches.value_of("config").map(|s| s.into());
|
|
||||||
init_logger(&output_fmt, &log_level)?;
|
init_logger(&output_fmt, &log_level)?;
|
||||||
debug!("[main] output format: {}", output_fmt);
|
debug!("output format: {}", output_fmt);
|
||||||
debug!("[main] log level: {}", log_level);
|
debug!("log level: {}", log_level);
|
||||||
debug!("[main] custom config path: {:?}", custom_config);
|
|
||||||
|
|
||||||
|
// Check completion matches before the config init
|
||||||
if comp_matches(build_app(), &matches)? {
|
if comp_matches(build_app(), &matches)? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("[main] init config");
|
let custom_config: Option<PathBuf> = matches.value_of("config").map(|s| s.into());
|
||||||
|
debug!("custom config path: {:?}", custom_config);
|
||||||
|
|
||||||
|
debug!("init config");
|
||||||
let config = Config::new(custom_config)?;
|
let config = Config::new(custom_config)?;
|
||||||
trace!("[main] {:#?}", config);
|
trace!("config: {:?}", config);
|
||||||
|
|
||||||
let account_name = matches.value_of("account");
|
let account_name = matches.value_of("account");
|
||||||
debug!("[main] find {} account", account_name.unwrap_or("default"));
|
debug!("init account: {}", account_name.unwrap_or("default"));
|
||||||
let account = config.find_account_by_name(account_name)?;
|
let account = config.find_account_by_name(account_name)?;
|
||||||
trace!("[main] {:#?}", account);
|
trace!("account: {:?}", account);
|
||||||
|
|
||||||
let mbox = matches.value_of("mailbox").unwrap();
|
let mbox = matches.value_of("mailbox").unwrap();
|
||||||
debug!("[main] mailbox: {}", mbox);
|
debug!("mailbox: {}", mbox);
|
||||||
|
|
||||||
debug!("[main] begin matching");
|
debug!("begin matching");
|
||||||
let _matched = mbox_matches(&account, &matches)?
|
let _matched = mbox_matches(&account, &matches)?
|
||||||
|| flag_matches(&account, &mbox, &matches)?
|
|| flag_matches(&account, &mbox, &matches)?
|
||||||
|| imap_matches(&config, &account, &mbox, &matches)?
|
|| imap_matches(&config, &account, &mbox, &matches)?
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use clap::{self, App, Arg, ArgMatches, SubCommand};
|
use clap::{self, App, Arg, ArgMatches, SubCommand};
|
||||||
use error_chain::error_chain;
|
use error_chain::error_chain;
|
||||||
use log::{debug, trace};
|
use log::{debug, info, trace};
|
||||||
|
|
||||||
use crate::{config::model::Account, imap::model::ImapConnector, info};
|
use crate::{config::model::Account, imap::model::ImapConnector};
|
||||||
|
|
||||||
error_chain! {
|
error_chain! {
|
||||||
links {
|
links {
|
||||||
|
@ -36,17 +36,17 @@ pub fn mbox_subcmds<'s>() -> Vec<App<'s, 's>> {
|
||||||
|
|
||||||
pub fn mbox_matches(account: &Account, matches: &ArgMatches) -> Result<bool> {
|
pub fn mbox_matches(account: &Account, matches: &ArgMatches) -> Result<bool> {
|
||||||
if let Some(_) = matches.subcommand_matches("mailboxes") {
|
if let Some(_) = matches.subcommand_matches("mailboxes") {
|
||||||
debug!("[mbox::cli::matches] mailboxes command matched");
|
debug!("mailboxes command matched");
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let mboxes = imap_conn.list_mboxes()?;
|
let mboxes = imap_conn.list_mboxes()?;
|
||||||
info!(&mboxes);
|
info!("{}", mboxes);
|
||||||
trace!("[mbox::cli::matches] {:#?}", mboxes);
|
trace!("mailboxes: {:?}", mboxes);
|
||||||
|
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("[mbox::cli::matches] nothing matched");
|
debug!("nothing matched");
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,10 @@ use imap;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::table::{self, DisplayRow, DisplayTable};
|
use crate::{
|
||||||
|
output::fmt::{get_output_fmt, OutputFmt, Response},
|
||||||
|
table::{self, DisplayRow, DisplayTable},
|
||||||
|
};
|
||||||
|
|
||||||
// Mbox
|
// Mbox
|
||||||
|
|
||||||
|
@ -58,6 +61,16 @@ impl<'a> DisplayTable<'a, Mbox> for Mboxes {
|
||||||
|
|
||||||
impl fmt::Display for Mboxes {
|
impl fmt::Display for Mboxes {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "\n{}", self.to_table())
|
unsafe {
|
||||||
|
match get_output_fmt() {
|
||||||
|
&OutputFmt::Plain => {
|
||||||
|
writeln!(f, "\n{}", self.to_table())
|
||||||
|
}
|
||||||
|
&OutputFmt::Json => {
|
||||||
|
let res = serde_json::to_string(&Response::new(self)).unwrap();
|
||||||
|
write!(f, "{}", res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
216
src/msg/cli.rs
216
src/msg/cli.rs
|
@ -1,13 +1,13 @@
|
||||||
use clap::{self, App, Arg, ArgMatches, SubCommand};
|
use clap::{self, App, Arg, ArgMatches, SubCommand};
|
||||||
use error_chain::error_chain;
|
use error_chain::error_chain;
|
||||||
use log::{debug, error, trace};
|
use log::{debug, error, info, trace};
|
||||||
use std::{fs, ops::Deref};
|
use std::{fs, ops::Deref};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::model::{Account, Config},
|
config::model::{Account, Config},
|
||||||
flag::model::Flag,
|
flag::model::Flag,
|
||||||
imap::model::ImapConnector,
|
imap::model::ImapConnector,
|
||||||
info, input,
|
input,
|
||||||
mbox::cli::mbox_target_arg,
|
mbox::cli::mbox_target_arg,
|
||||||
msg::model::{Attachments, Msg, Msgs, ReadableMsg},
|
msg::model::{Attachments, Msg, Msgs, ReadableMsg},
|
||||||
smtp,
|
smtp,
|
||||||
|
@ -173,44 +173,44 @@ pub fn msg_matches(
|
||||||
matches: &ArgMatches,
|
matches: &ArgMatches,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
if let Some(matches) = matches.subcommand_matches("list") {
|
if let Some(matches) = matches.subcommand_matches("list") {
|
||||||
debug!("[msg::cli::matches] list command matched");
|
debug!("list command matched");
|
||||||
|
|
||||||
let page_size: usize = matches
|
let page_size: usize = matches
|
||||||
.value_of("page-size")
|
.value_of("page-size")
|
||||||
.and_then(|s| s.parse().ok())
|
.and_then(|s| s.parse().ok())
|
||||||
.unwrap_or(config.default_page_size(&account));
|
.unwrap_or(config.default_page_size(&account));
|
||||||
debug!("[msg::cli::matches] page size: {}", &page_size);
|
debug!("page size: {}", &page_size);
|
||||||
let page: usize = matches
|
let page: usize = matches
|
||||||
.value_of("page")
|
.value_of("page")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
debug!("[msg::cli::matches] page: {}", &page);
|
debug!("page: {}", &page);
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let msgs = imap_conn.list_msgs(&mbox, &page_size, &page)?;
|
let msgs = imap_conn.list_msgs(&mbox, &page_size, &page)?;
|
||||||
let msgs = Msgs::from(&msgs);
|
let msgs = Msgs::from(&msgs);
|
||||||
info!(&msgs);
|
info!("{}", msgs);
|
||||||
trace!("[msg::cli::matches] {:#?}", msgs);
|
trace!("messages: {:?}", msgs);
|
||||||
|
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("search") {
|
if let Some(matches) = matches.subcommand_matches("search") {
|
||||||
debug!("[msg::cli::matches] search command matched");
|
debug!("search command matched");
|
||||||
|
|
||||||
let page_size: usize = matches
|
let page_size: usize = matches
|
||||||
.value_of("page-size")
|
.value_of("page-size")
|
||||||
.and_then(|s| s.parse().ok())
|
.and_then(|s| s.parse().ok())
|
||||||
.unwrap_or(config.default_page_size(&account));
|
.unwrap_or(config.default_page_size(&account));
|
||||||
debug!("[msg::cli::matches] page size: {}", &page_size);
|
debug!("page size: {}", &page_size);
|
||||||
let page: usize = matches
|
let page: usize = matches
|
||||||
.value_of("page")
|
.value_of("page")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
debug!("[msg::cli::matches] page: {}", &page);
|
debug!("page: {}", &page);
|
||||||
|
|
||||||
let query = matches
|
let query = matches
|
||||||
.values_of("query")
|
.values_of("query")
|
||||||
|
@ -236,27 +236,27 @@ pub fn msg_matches(
|
||||||
})
|
})
|
||||||
.1
|
.1
|
||||||
.join(" ");
|
.join(" ");
|
||||||
debug!("[msg::cli::matches] query: {}", &page);
|
debug!("query: {}", &page);
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let msgs = imap_conn.search_msgs(&mbox, &query, &page_size, &page)?;
|
let msgs = imap_conn.search_msgs(&mbox, &query, &page_size, &page)?;
|
||||||
let msgs = Msgs::from(&msgs);
|
let msgs = Msgs::from(&msgs);
|
||||||
info!(&msgs);
|
info!("{}", msgs);
|
||||||
trace!("[msg::cli::matches] {:#?}", msgs);
|
trace!("messages: {:?}", msgs);
|
||||||
|
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("read") {
|
if let Some(matches) = matches.subcommand_matches("read") {
|
||||||
debug!("[msg::cli::matches] read command matched");
|
debug!("read command matched");
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
debug!("[msg::cli::matches] uid: {}", uid);
|
debug!("uid: {}", uid);
|
||||||
let mime = format!("text/{}", matches.value_of("mime-type").unwrap());
|
let mime = format!("text/{}", matches.value_of("mime-type").unwrap());
|
||||||
debug!("[msg::cli::matches] mime: {}", mime);
|
debug!("mime: {}", mime);
|
||||||
let raw = matches.is_present("raw");
|
let raw = matches.is_present("raw");
|
||||||
debug!("[msg::cli::matches] raw: {}", raw);
|
debug!("raw: {}", raw);
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let msg = imap_conn.read_msg(&mbox, &uid)?;
|
let msg = imap_conn.read_msg(&mbox, &uid)?;
|
||||||
|
@ -264,10 +264,10 @@ pub fn msg_matches(
|
||||||
let msg = String::from_utf8(msg)
|
let msg = String::from_utf8(msg)
|
||||||
.chain_err(|| "Could not decode raw message as utf8 string")?;
|
.chain_err(|| "Could not decode raw message as utf8 string")?;
|
||||||
let msg = msg.trim_end_matches("\n");
|
let msg = msg.trim_end_matches("\n");
|
||||||
info!(&msg);
|
info!("{}", msg);
|
||||||
} else {
|
} else {
|
||||||
let msg = ReadableMsg::from_bytes(&mime, &msg)?;
|
let msg = ReadableMsg::from_bytes(&mime, &msg)?;
|
||||||
info!(&msg);
|
info!("{}", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
|
@ -275,36 +275,36 @@ pub fn msg_matches(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("attachments") {
|
if let Some(matches) = matches.subcommand_matches("attachments") {
|
||||||
debug!("[msg::cli::matches] attachments command matched");
|
debug!("attachments command matched");
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
debug!("[msg::cli::matches] uid: {}", &uid);
|
debug!("uid: {}", &uid);
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let msg = imap_conn.read_msg(&mbox, &uid)?;
|
let msg = imap_conn.read_msg(&mbox, &uid)?;
|
||||||
let attachments = Attachments::from_bytes(&msg)?;
|
let attachments = Attachments::from_bytes(&msg)?;
|
||||||
debug!(
|
debug!(
|
||||||
"[msg::cli::matches] {} attachment(s) found for message {}",
|
"{} attachment(s) found for message {}",
|
||||||
&attachments.0.len(),
|
&attachments.0.len(),
|
||||||
&uid
|
&uid
|
||||||
);
|
);
|
||||||
for attachment in attachments.0.iter() {
|
for attachment in attachments.0.iter() {
|
||||||
let filepath = config.downloads_filepath(&account, &attachment.filename);
|
let filepath = config.downloads_filepath(&account, &attachment.filename);
|
||||||
debug!("[msg::cli::matches] downloading {}…", &attachment.filename);
|
debug!("downloading {}…", &attachment.filename);
|
||||||
fs::write(&filepath, &attachment.raw)
|
fs::write(&filepath, &attachment.raw)
|
||||||
.chain_err(|| format!("Could not save attachment {:?}", filepath))?;
|
.chain_err(|| format!("Could not save attachment {:?}", filepath))?;
|
||||||
}
|
}
|
||||||
info!(&format!(
|
info!(
|
||||||
"{} attachment(s) successfully downloaded",
|
"{} attachment(s) successfully downloaded",
|
||||||
&attachments.0.len()
|
&attachments.0.len()
|
||||||
));
|
);
|
||||||
|
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("write") {
|
if let Some(matches) = matches.subcommand_matches("write") {
|
||||||
debug!("[msg::cli::matches] write command matched");
|
debug!("write command matched");
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let attachments = matches
|
let attachments = matches
|
||||||
|
@ -321,7 +321,7 @@ pub fn msg_matches(
|
||||||
match input::post_edit_choice() {
|
match input::post_edit_choice() {
|
||||||
Ok(choice) => match choice {
|
Ok(choice) => match choice {
|
||||||
input::PostEditChoice::Send => {
|
input::PostEditChoice::Send => {
|
||||||
debug!("[msg::cli::matches] sending message…");
|
debug!("sending message…");
|
||||||
let msg = msg.to_sendable_msg()?;
|
let msg = msg.to_sendable_msg()?;
|
||||||
smtp::send(&account, &msg)?;
|
smtp::send(&account, &msg)?;
|
||||||
imap_conn.append_msg("Sent", &msg.formatted(), &[Flag::Seen])?;
|
imap_conn.append_msg("Sent", &msg.formatted(), &[Flag::Seen])?;
|
||||||
|
@ -335,7 +335,7 @@ pub fn msg_matches(
|
||||||
}
|
}
|
||||||
input::PostEditChoice::LocalDraft => break,
|
input::PostEditChoice::LocalDraft => break,
|
||||||
input::PostEditChoice::RemoteDraft => {
|
input::PostEditChoice::RemoteDraft => {
|
||||||
debug!("[msg::cli::matches] saving to draft…");
|
debug!("saving to draft…");
|
||||||
imap_conn.append_msg("Drafts", &msg.to_vec()?, &[Flag::Seen])?;
|
imap_conn.append_msg("Drafts", &msg.to_vec()?, &[Flag::Seen])?;
|
||||||
input::remove_draft()?;
|
input::remove_draft()?;
|
||||||
info!("Message successfully saved to Drafts");
|
info!("Message successfully saved to Drafts");
|
||||||
|
@ -353,68 +353,18 @@ pub fn msg_matches(
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("template") {
|
|
||||||
debug!("[msg::cli::matches] template command matched");
|
|
||||||
|
|
||||||
if let Some(_) = matches.subcommand_matches("new") {
|
|
||||||
debug!("[msg::cli::matches] new command matched");
|
|
||||||
let tpl = Msg::build_new_tpl(&config, &account)?;
|
|
||||||
info!(&tpl);
|
|
||||||
trace!("[msg::cli::matches] tpl: {:#?}", tpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("reply") {
|
|
||||||
debug!("[msg::cli::matches] reply command matched");
|
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
|
||||||
debug!("[msg::cli::matches] uid: {}", uid);
|
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
|
||||||
let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
|
|
||||||
let tpl = if matches.is_present("reply-all") {
|
|
||||||
msg.build_reply_all_tpl(&config, &account)?
|
|
||||||
} else {
|
|
||||||
msg.build_reply_tpl(&config, &account)?
|
|
||||||
};
|
|
||||||
info!(&tpl);
|
|
||||||
trace!("[msg::cli::matches] tpl: {:#?}", tpl);
|
|
||||||
|
|
||||||
imap_conn.logout();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("forward") {
|
|
||||||
debug!("[msg::cli::matches] forward command matched");
|
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
|
||||||
debug!("[msg::cli::matches] uid: {}", uid);
|
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
|
||||||
let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
|
|
||||||
let tpl = msg.build_forward_tpl(&config, &account)?;
|
|
||||||
info!(&tpl);
|
|
||||||
trace!("[msg::cli::matches] tpl: {:#?}", tpl);
|
|
||||||
|
|
||||||
imap_conn.logout();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("reply") {
|
if let Some(matches) = matches.subcommand_matches("reply") {
|
||||||
debug!("[msg::cli::matches] reply command matched");
|
debug!("reply command matched");
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
debug!("[msg::cli::matches] uid: {}", uid);
|
debug!("uid: {}", uid);
|
||||||
let attachments = matches
|
let attachments = matches
|
||||||
.values_of("attachments")
|
.values_of("attachments")
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.map(String::from)
|
.map(String::from)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
debug!(
|
debug!("found {} attachments", attachments.len());
|
||||||
"[msg::cli::matches] found {} attachments",
|
trace!("attachments: {:?}", attachments);
|
||||||
attachments.len()
|
|
||||||
);
|
|
||||||
trace!("[msg::cli::matches] {:#?}", attachments);
|
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
|
let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
|
||||||
|
@ -432,7 +382,7 @@ pub fn msg_matches(
|
||||||
match input::post_edit_choice() {
|
match input::post_edit_choice() {
|
||||||
Ok(choice) => match choice {
|
Ok(choice) => match choice {
|
||||||
input::PostEditChoice::Send => {
|
input::PostEditChoice::Send => {
|
||||||
debug!("[msg::cli::matches] sending message…");
|
debug!("sending message…");
|
||||||
let msg = msg.to_sendable_msg()?;
|
let msg = msg.to_sendable_msg()?;
|
||||||
smtp::send(&account, &msg)?;
|
smtp::send(&account, &msg)?;
|
||||||
imap_conn.append_msg("Sent", &msg.formatted(), &[Flag::Seen])?;
|
imap_conn.append_msg("Sent", &msg.formatted(), &[Flag::Seen])?;
|
||||||
|
@ -447,7 +397,7 @@ pub fn msg_matches(
|
||||||
}
|
}
|
||||||
input::PostEditChoice::LocalDraft => break,
|
input::PostEditChoice::LocalDraft => break,
|
||||||
input::PostEditChoice::RemoteDraft => {
|
input::PostEditChoice::RemoteDraft => {
|
||||||
debug!("[msg::cli::matches] saving to draft…");
|
debug!("saving to draft…");
|
||||||
imap_conn.append_msg("Drafts", &msg.to_vec()?, &[Flag::Seen])?;
|
imap_conn.append_msg("Drafts", &msg.to_vec()?, &[Flag::Seen])?;
|
||||||
input::remove_draft()?;
|
input::remove_draft()?;
|
||||||
info!("Message successfully saved to Drafts");
|
info!("Message successfully saved to Drafts");
|
||||||
|
@ -467,20 +417,17 @@ pub fn msg_matches(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("forward") {
|
if let Some(matches) = matches.subcommand_matches("forward") {
|
||||||
debug!("[msg::cli::matches] forward command matched");
|
debug!("forward command matched");
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
debug!("[msg::cli::matches] uid: {}", uid);
|
debug!("uid: {}", uid);
|
||||||
let attachments = matches
|
let attachments = matches
|
||||||
.values_of("attachments")
|
.values_of("attachments")
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.map(String::from)
|
.map(String::from)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
debug!(
|
debug!("found {} attachments", attachments.len());
|
||||||
"[msg::cli::matches] found {} attachments",
|
trace!("attachments: {:?}", attachments);
|
||||||
attachments.len()
|
|
||||||
);
|
|
||||||
trace!("[msg::cli::matches] {:#?}", attachments);
|
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
|
let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
|
||||||
|
@ -493,7 +440,7 @@ pub fn msg_matches(
|
||||||
match input::post_edit_choice() {
|
match input::post_edit_choice() {
|
||||||
Ok(choice) => match choice {
|
Ok(choice) => match choice {
|
||||||
input::PostEditChoice::Send => {
|
input::PostEditChoice::Send => {
|
||||||
debug!("[msg::cli::matches] sending message…");
|
debug!("sending message…");
|
||||||
let msg = msg.to_sendable_msg()?;
|
let msg = msg.to_sendable_msg()?;
|
||||||
smtp::send(&account, &msg)?;
|
smtp::send(&account, &msg)?;
|
||||||
imap_conn.append_msg("Sent", &msg.formatted(), &[Flag::Seen])?;
|
imap_conn.append_msg("Sent", &msg.formatted(), &[Flag::Seen])?;
|
||||||
|
@ -507,7 +454,7 @@ pub fn msg_matches(
|
||||||
}
|
}
|
||||||
input::PostEditChoice::LocalDraft => break,
|
input::PostEditChoice::LocalDraft => break,
|
||||||
input::PostEditChoice::RemoteDraft => {
|
input::PostEditChoice::RemoteDraft => {
|
||||||
debug!("[msg::cli::matches] saving to draft…");
|
debug!("saving to draft…");
|
||||||
imap_conn.append_msg("Drafts", &msg.to_vec()?, &[Flag::Seen])?;
|
imap_conn.append_msg("Drafts", &msg.to_vec()?, &[Flag::Seen])?;
|
||||||
input::remove_draft()?;
|
input::remove_draft()?;
|
||||||
info!("Message successfully saved to Drafts");
|
info!("Message successfully saved to Drafts");
|
||||||
|
@ -526,35 +473,79 @@ pub fn msg_matches(
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(matches) = matches.subcommand_matches("template") {
|
||||||
|
debug!("template command matched");
|
||||||
|
|
||||||
|
if let Some(_) = matches.subcommand_matches("new") {
|
||||||
|
debug!("new command matched");
|
||||||
|
let tpl = Msg::build_new_tpl(&config, &account)?;
|
||||||
|
info!("{}", tpl);
|
||||||
|
trace!("tpl: {:?}", tpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(matches) = matches.subcommand_matches("reply") {
|
||||||
|
debug!("reply command matched");
|
||||||
|
|
||||||
|
let uid = matches.value_of("uid").unwrap();
|
||||||
|
debug!("uid: {}", uid);
|
||||||
|
|
||||||
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
|
let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
|
||||||
|
let tpl = if matches.is_present("reply-all") {
|
||||||
|
msg.build_reply_all_tpl(&config, &account)?
|
||||||
|
} else {
|
||||||
|
msg.build_reply_tpl(&config, &account)?
|
||||||
|
};
|
||||||
|
info!("{}", tpl);
|
||||||
|
trace!("tpl: {:?}", tpl);
|
||||||
|
|
||||||
|
imap_conn.logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(matches) = matches.subcommand_matches("forward") {
|
||||||
|
debug!("forward command matched");
|
||||||
|
|
||||||
|
let uid = matches.value_of("uid").unwrap();
|
||||||
|
debug!("uid: {}", uid);
|
||||||
|
|
||||||
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
|
let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
|
||||||
|
let tpl = msg.build_forward_tpl(&config, &account)?;
|
||||||
|
info!("{}", tpl);
|
||||||
|
trace!("tpl: {:?}", tpl);
|
||||||
|
|
||||||
|
imap_conn.logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("copy") {
|
if let Some(matches) = matches.subcommand_matches("copy") {
|
||||||
debug!("[msg::cli::matches] copy command matched");
|
debug!("copy command matched");
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
debug!("[msg::cli::matches] uid: {}", &uid);
|
debug!("uid: {}", &uid);
|
||||||
let target = matches.value_of("target").unwrap();
|
let target = matches.value_of("target").unwrap();
|
||||||
debug!("[msg::cli::matches] target: {}", &target);
|
debug!("target: {}", &target);
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
|
let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
|
||||||
let mut flags = msg.flags.deref().to_vec();
|
let mut flags = msg.flags.deref().to_vec();
|
||||||
flags.push(Flag::Seen);
|
flags.push(Flag::Seen);
|
||||||
imap_conn.append_msg(target, &msg.raw, &flags)?;
|
imap_conn.append_msg(target, &msg.raw, &flags)?;
|
||||||
info!(&format!(
|
info!("Message {} successfully copied to folder `{}`", uid, target);
|
||||||
"Message {} successfully copied to folder `{}`",
|
|
||||||
&uid, &target
|
|
||||||
));
|
|
||||||
|
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("move") {
|
if let Some(matches) = matches.subcommand_matches("move") {
|
||||||
debug!("[msg::cli::matches] move command matched");
|
debug!("move command matched");
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
debug!("[msg::cli::matches] uid: {}", &uid);
|
debug!("uid: {}", &uid);
|
||||||
let target = matches.value_of("target").unwrap();
|
let target = matches.value_of("target").unwrap();
|
||||||
debug!("[msg::cli::matches] target: {}", &target);
|
debug!("target: {}", &target);
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
|
let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
|
||||||
|
@ -562,10 +553,7 @@ pub fn msg_matches(
|
||||||
flags.push(Flag::Seen);
|
flags.push(Flag::Seen);
|
||||||
imap_conn.append_msg(target, &msg.raw, msg.flags.deref())?;
|
imap_conn.append_msg(target, &msg.raw, msg.flags.deref())?;
|
||||||
imap_conn.add_flags(&mbox, uid, "\\Seen \\Deleted")?;
|
imap_conn.add_flags(&mbox, uid, "\\Seen \\Deleted")?;
|
||||||
info!(&format!(
|
info!("Message {} successfully moved to folder `{}`", uid, target);
|
||||||
"Message {} successfully moved to folder `{}`",
|
|
||||||
&uid, &target
|
|
||||||
));
|
|
||||||
|
|
||||||
imap_conn.expunge(&mbox)?;
|
imap_conn.expunge(&mbox)?;
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
|
@ -573,14 +561,14 @@ pub fn msg_matches(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("delete") {
|
if let Some(matches) = matches.subcommand_matches("delete") {
|
||||||
debug!("[msg::cli::matches] delete command matched");
|
debug!("delete command matched");
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
debug!("[msg::cli::matches] uid: {}", &uid);
|
debug!("uid: {}", &uid);
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
imap_conn.add_flags(&mbox, uid, "\\Seen \\Deleted")?;
|
imap_conn.add_flags(&mbox, uid, "\\Seen \\Deleted")?;
|
||||||
info!(&format!("Message {} successfully deleted", &uid));
|
info!("Message {} successfully deleted", uid);
|
||||||
|
|
||||||
imap_conn.expunge(&mbox)?;
|
imap_conn.expunge(&mbox)?;
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
|
@ -588,7 +576,7 @@ pub fn msg_matches(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("send") {
|
if let Some(matches) = matches.subcommand_matches("send") {
|
||||||
debug!("[msg::cli::matches] send command matched");
|
debug!("send command matched");
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let msg = matches.value_of("message").unwrap();
|
let msg = matches.value_of("message").unwrap();
|
||||||
|
@ -602,7 +590,7 @@ pub fn msg_matches(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("save") {
|
if let Some(matches) = matches.subcommand_matches("save") {
|
||||||
debug!("[msg::cli::matches] save command matched");
|
debug!("save command matched");
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let msg = matches.value_of("message").unwrap();
|
let msg = matches.value_of("message").unwrap();
|
||||||
|
@ -614,12 +602,12 @@ pub fn msg_matches(
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
debug!("[msg::cli::matches] default list command matched");
|
debug!("default list command matched");
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let msgs = imap_conn.list_msgs(&mbox, &config.default_page_size(&account), &0)?;
|
let msgs = imap_conn.list_msgs(&mbox, &config.default_page_size(&account), &0)?;
|
||||||
let msgs = Msgs::from(&msgs);
|
let msgs = Msgs::from(&msgs);
|
||||||
info!(&msgs);
|
info!("{}", msgs);
|
||||||
|
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
|
|
@ -11,9 +11,12 @@ use std::{borrow::Cow, fmt, fs, path::PathBuf, result};
|
||||||
use tree_magic;
|
use tree_magic;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::config::model::{Account, Config};
|
use crate::{
|
||||||
use crate::flag::model::{Flag, Flags};
|
config::model::{Account, Config},
|
||||||
use crate::table::{self, DisplayRow, DisplayTable};
|
flag::model::{Flag, Flags},
|
||||||
|
output::fmt::{get_output_fmt, OutputFmt, Response},
|
||||||
|
table::{self, DisplayRow, DisplayTable},
|
||||||
|
};
|
||||||
|
|
||||||
error_chain! {
|
error_chain! {
|
||||||
foreign_links {
|
foreign_links {
|
||||||
|
@ -29,7 +32,17 @@ pub struct Tpl(String);
|
||||||
|
|
||||||
impl fmt::Display for Tpl {
|
impl fmt::Display for Tpl {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{}", self.0)
|
unsafe {
|
||||||
|
match get_output_fmt() {
|
||||||
|
&OutputFmt::Plain => {
|
||||||
|
writeln!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
&OutputFmt::Json => {
|
||||||
|
let res = serde_json::to_string(&Response::new(self)).unwrap();
|
||||||
|
write!(f, "{}", res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +131,17 @@ impl Serialize for ReadableMsg {
|
||||||
|
|
||||||
impl fmt::Display for ReadableMsg {
|
impl fmt::Display for ReadableMsg {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{}", self.content)
|
unsafe {
|
||||||
|
match get_output_fmt() {
|
||||||
|
&OutputFmt::Plain => {
|
||||||
|
writeln!(f, "{}", self.content)
|
||||||
|
}
|
||||||
|
&OutputFmt::Json => {
|
||||||
|
let res = serde_json::to_string(&Response::new(self)).unwrap();
|
||||||
|
write!(f, "{}", res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +398,7 @@ impl<'m> Msg<'m> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_new_tpl(config: &Config, account: &Account) -> Result<Tpl> {
|
pub fn build_new_tpl(config: &Config, account: &Account) -> Result<Tpl> {
|
||||||
let msg_spec = MsgSpec{
|
let msg_spec = MsgSpec {
|
||||||
in_reply_to: None,
|
in_reply_to: None,
|
||||||
to: None,
|
to: None,
|
||||||
cc: None,
|
cc: None,
|
||||||
|
@ -389,11 +412,11 @@ impl<'m> Msg<'m> {
|
||||||
let msg = &self.parse()?;
|
let msg = &self.parse()?;
|
||||||
let headers = msg.get_headers();
|
let headers = msg.get_headers();
|
||||||
let to = headers
|
let to = headers
|
||||||
.get_first_value("reply-to")
|
.get_first_value("reply-to")
|
||||||
.or(headers.get_first_value("from"));
|
.or(headers.get_first_value("from"));
|
||||||
let to = match to {
|
let to = match to {
|
||||||
Some(t) => {Some(vec![t])},
|
Some(t) => Some(vec![t]),
|
||||||
None => {None},
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let thread = self // Original msg prepend with ">"
|
let thread = self // Original msg prepend with ">"
|
||||||
|
@ -403,7 +426,7 @@ impl<'m> Msg<'m> {
|
||||||
.map(|line| format!(">{}", line))
|
.map(|line| format!(">{}", line))
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
let msg_spec = MsgSpec{
|
let msg_spec = MsgSpec {
|
||||||
in_reply_to: headers.get_first_value("message-id"),
|
in_reply_to: headers.get_first_value("message-id"),
|
||||||
to,
|
to,
|
||||||
cc: None,
|
cc: None,
|
||||||
|
@ -455,12 +478,14 @@ impl<'m> Msg<'m> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// "Cc" header
|
// "Cc" header
|
||||||
let cc = Some(headers
|
let cc = Some(
|
||||||
.get_all_values("cc")
|
headers
|
||||||
.iter()
|
.get_all_values("cc")
|
||||||
.flat_map(|addrs| addrs.split(","))
|
.iter()
|
||||||
.map(|addr| addr.trim().to_string())
|
.flat_map(|addrs| addrs.split(","))
|
||||||
.collect::<Vec<String>>());
|
.map(|addr| addr.trim().to_string())
|
||||||
|
.collect::<Vec<String>>(),
|
||||||
|
);
|
||||||
|
|
||||||
// Original msg prepend with ">"
|
// Original msg prepend with ">"
|
||||||
let thread = self
|
let thread = self
|
||||||
|
@ -469,7 +494,7 @@ impl<'m> Msg<'m> {
|
||||||
.map(|line| format!(">{}", line))
|
.map(|line| format!(">{}", line))
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
let msg_spec = MsgSpec{
|
let msg_spec = MsgSpec {
|
||||||
in_reply_to: headers.get_first_value("message-id"),
|
in_reply_to: headers.get_first_value("message-id"),
|
||||||
cc,
|
cc,
|
||||||
to: Some(vec![reply_to, to].concat()),
|
to: Some(vec![reply_to, to].concat()),
|
||||||
|
@ -483,13 +508,18 @@ impl<'m> Msg<'m> {
|
||||||
let msg = &self.parse()?;
|
let msg = &self.parse()?;
|
||||||
let headers = msg.get_headers();
|
let headers = msg.get_headers();
|
||||||
|
|
||||||
let subject = format!("Fwd: {}", headers.get_first_value("subject").unwrap_or_else(String::new));
|
let subject = format!(
|
||||||
|
"Fwd: {}",
|
||||||
|
headers
|
||||||
|
.get_first_value("subject")
|
||||||
|
.unwrap_or_else(String::new)
|
||||||
|
);
|
||||||
let original_msg = vec![
|
let original_msg = vec![
|
||||||
"-------- Forwarded Message --------".to_string(),
|
"-------- Forwarded Message --------".to_string(),
|
||||||
self.text_bodies("text/plain")?,
|
self.text_bodies("text/plain")?,
|
||||||
];
|
];
|
||||||
|
|
||||||
let msg_spec = MsgSpec{
|
let msg_spec = MsgSpec {
|
||||||
in_reply_to: None,
|
in_reply_to: None,
|
||||||
cc: None,
|
cc: None,
|
||||||
to: None,
|
to: None,
|
||||||
|
@ -520,10 +550,17 @@ impl<'m> Msg<'m> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_to_header(tpl: &mut Vec<String>, to: Option<Vec<String>>) {
|
fn add_to_header(tpl: &mut Vec<String>, to: Option<Vec<String>>) {
|
||||||
tpl.push(format!("To: {}", match to {
|
tpl.push(format!(
|
||||||
Some(t) => {t.join(", ")}
|
"To: {}",
|
||||||
None => {String::new()}
|
match to {
|
||||||
}));
|
Some(t) => {
|
||||||
|
t.join(", ")
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_subject_header(tpl: &mut Vec<String>, subject: Option<String>) {
|
fn add_subject_header(tpl: &mut Vec<String>, subject: Option<String>) {
|
||||||
|
@ -619,6 +656,16 @@ impl<'m> From<&'m imap::types::ZeroCopy<Vec<imap::types::Fetch>>> for Msgs<'m> {
|
||||||
|
|
||||||
impl<'m> fmt::Display for Msgs<'m> {
|
impl<'m> fmt::Display for Msgs<'m> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "\n{}", self.to_table())
|
unsafe {
|
||||||
|
match get_output_fmt() {
|
||||||
|
&OutputFmt::Plain => {
|
||||||
|
writeln!(f, "\n{}", self.to_table())
|
||||||
|
}
|
||||||
|
&OutputFmt::Json => {
|
||||||
|
let res = serde_json::to_string(&Response::new(self)).unwrap();
|
||||||
|
write!(f, "{}", res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,9 @@ pub fn output_args<'a>() -> Vec<Arg<'a, 'a>> {
|
||||||
.value_name("FMT")
|
.value_name("FMT")
|
||||||
.possible_values(&["plain", "json"])
|
.possible_values(&["plain", "json"])
|
||||||
.default_value("plain"),
|
.default_value("plain"),
|
||||||
Arg::with_name("log")
|
Arg::with_name("log-level")
|
||||||
.long("log")
|
.long("log-level")
|
||||||
|
.alias("log")
|
||||||
.short("l")
|
.short("l")
|
||||||
.help("Defines the logs level")
|
.help("Defines the logs level")
|
||||||
.value_name("LEVEL")
|
.value_name("LEVEL")
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
use serde::Serialize;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
static mut OUTPUT_FMT: &'static OutputFmt = &OutputFmt::Plain;
|
pub static mut OUTPUT_FMT: &'static OutputFmt = &OutputFmt::Plain;
|
||||||
|
|
||||||
pub fn set_output_fmt(output_fmt: &'static OutputFmt) {
|
pub fn set_output_fmt(output_fmt: &'static OutputFmt) {
|
||||||
unsafe { OUTPUT_FMT = output_fmt }
|
unsafe { OUTPUT_FMT = output_fmt }
|
||||||
|
@ -36,3 +37,29 @@ impl fmt::Display for OutputFmt {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct Response<T: Serialize + fmt::Display> {
|
||||||
|
response: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Serialize + fmt::Display> Response<T> {
|
||||||
|
pub fn new(response: T) -> Self {
|
||||||
|
Self { response }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Serialize + fmt::Display> fmt::Display for Response<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
unsafe {
|
||||||
|
match get_output_fmt() {
|
||||||
|
&OutputFmt::Plain => {
|
||||||
|
writeln!(f, "{}", self.response)
|
||||||
|
}
|
||||||
|
&OutputFmt::Json => {
|
||||||
|
write!(f, "{}", serde_json::to_string(self).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,31 +1,13 @@
|
||||||
|
use chrono::Local;
|
||||||
|
use env_logger;
|
||||||
use error_chain::error_chain;
|
use error_chain::error_chain;
|
||||||
use log::{self, Level, LevelFilter, Metadata, Record};
|
use log::{self, Level, LevelFilter, Metadata, Record};
|
||||||
use std::fmt;
|
use std::{fmt, io::Write};
|
||||||
|
|
||||||
use super::fmt::{set_output_fmt, OutputFmt};
|
use super::fmt::{set_output_fmt, OutputFmt};
|
||||||
|
|
||||||
error_chain! {}
|
error_chain! {}
|
||||||
|
|
||||||
// Macros
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! info {
|
|
||||||
($t:expr) => {
|
|
||||||
use crate::output::fmt::{get_output_fmt, OutputFmt};
|
|
||||||
use log::info as log_info;
|
|
||||||
unsafe {
|
|
||||||
match get_output_fmt() {
|
|
||||||
OutputFmt::Plain => log_info!("{}", $t.to_string()),
|
|
||||||
OutputFmt::Json => {
|
|
||||||
// Should be safe enough to `.unwrap()` since it's
|
|
||||||
// formatted by Himalaya itself
|
|
||||||
log_info!("{{\"response\":{}}}", serde_json::to_string($t).unwrap())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log level struct
|
// Log level struct
|
||||||
|
|
||||||
pub struct LogLevel(pub LevelFilter);
|
pub struct LogLevel(pub LevelFilter);
|
||||||
|
@ -95,16 +77,32 @@ impl log::Log for JsonLogger {
|
||||||
// Init
|
// Init
|
||||||
|
|
||||||
pub fn init(fmt: &OutputFmt, level: &LogLevel) -> Result<()> {
|
pub fn init(fmt: &OutputFmt, level: &LogLevel) -> Result<()> {
|
||||||
log::set_logger(match fmt {
|
match fmt {
|
||||||
&OutputFmt::Plain => {
|
&OutputFmt::Plain => {
|
||||||
set_output_fmt(&OutputFmt::Plain);
|
set_output_fmt(&OutputFmt::Plain);
|
||||||
&PlainLogger
|
|
||||||
}
|
}
|
||||||
&OutputFmt::Json => {
|
&OutputFmt::Json => {
|
||||||
set_output_fmt(&OutputFmt::Json);
|
set_output_fmt(&OutputFmt::Json);
|
||||||
&JsonLogger
|
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
.map(|()| log::set_max_level(level.0))
|
|
||||||
.chain_err(|| "Could not init logger")
|
env_logger::Builder::new()
|
||||||
|
.format(|buf, record| {
|
||||||
|
if let log::Level::Info = record.metadata().level() {
|
||||||
|
write!(buf, "{}", record.args())
|
||||||
|
} else {
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
"[{} {:5} {}] {}",
|
||||||
|
Local::now().format("%Y-%m-%dT%H:%M:%S"),
|
||||||
|
record.metadata().level(),
|
||||||
|
record.module_path().unwrap_or_default(),
|
||||||
|
record.args()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter_level(level.0)
|
||||||
|
.init();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue