mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-22 02:50:19 +00:00
replace error-chain by anyhow (#152)
This commit is contained in:
parent
fa622ba1db
commit
c619f06206
18 changed files with 180 additions and 454 deletions
|
@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Changed
|
||||
|
||||
- Pagination for list and search cmd starts from 1 instead of 0 [#186]
|
||||
- Errors management with `anyhow` [#152]
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -309,6 +310,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
[#141]: https://github.com/soywod/himalaya/issues/141
|
||||
[#144]: https://github.com/soywod/himalaya/issues/144
|
||||
[#146]: https://github.com/soywod/himalaya/issues/146
|
||||
[#152]: https://github.com/soywod/himalaya/issues/152
|
||||
[#160]: https://github.com/soywod/himalaya/issues/160
|
||||
[#176]: https://github.com/soywod/himalaya/issues/176
|
||||
[#186]: https://github.com/soywod/himalaya/issues/186
|
||||
|
|
83
Cargo.lock
generated
83
Cargo.lock
generated
|
@ -2,21 +2,6 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7a2e47a1fbe209ee101dd6d61285226744c6c8d3c21c8dc878ba6cb9f467f3a"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.15"
|
||||
|
@ -35,6 +20,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
|
||||
|
||||
[[package]]
|
||||
name = "ascii_utils"
|
||||
version = "0.9.3"
|
||||
|
@ -64,21 +55,6 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4717cfcbfaa661a0fd48f8453951837ae7e8f81e481fbb136e3202d72805a744"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.9.3"
|
||||
|
@ -198,12 +174,6 @@ dependencies = [
|
|||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorful"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bca1619ff57dd7a56b58a8e25ef4199f123e78e503fe1653410350a1b98ae65"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.1"
|
||||
|
@ -342,16 +312,6 @@ dependencies = [
|
|||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-chain"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"version_check 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fast_chemail"
|
||||
version = "0.9.6"
|
||||
|
@ -464,12 +424,6 @@ dependencies = [
|
|||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
|
@ -489,12 +443,11 @@ dependencies = [
|
|||
name = "himalaya"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"atty",
|
||||
"chrono",
|
||||
"clap",
|
||||
"colorful",
|
||||
"env_logger",
|
||||
"error-chain",
|
||||
"imap",
|
||||
"imap-proto",
|
||||
"lettre 0.10.0-rc.3",
|
||||
|
@ -732,16 +685,6 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6595bb28ed34f43c3fe088e48f6cfb2e033cab45f25a5384d5fdf564fbc8c4b2"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"autocfg 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.8"
|
||||
|
@ -811,12 +754,6 @@ dependencies = [
|
|||
"autocfg 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.8.0"
|
||||
|
@ -1216,12 +1153,6 @@ dependencies = [
|
|||
"quoted_printable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
|
|
|
@ -6,12 +6,11 @@ authors = ["soywod <clement.douin@posteo.net>"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.44"
|
||||
atty = "0.2.14"
|
||||
chrono = "0.4.19"
|
||||
clap = { version = "2.33.3", default-features = false, features = ["suggestions", "color"] }
|
||||
colorful = "0.2.1"
|
||||
env_logger = "0.8.3"
|
||||
error-chain = "0.12.4"
|
||||
imap = "3.0.0-alpha.4"
|
||||
imap-proto = "0.14.3"
|
||||
# This commit includes the de/serialization of the ContentType
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
use anyhow::Result;
|
||||
use clap::{self, App, Arg, ArgMatches, Shell, SubCommand};
|
||||
use error_chain::error_chain;
|
||||
use log::debug;
|
||||
use std::io;
|
||||
|
||||
error_chain! {}
|
||||
|
||||
// == Main functions ==
|
||||
pub fn subcmds<'s>() -> Vec<App<'s, 's>> {
|
||||
vec![SubCommand::with_name("completion")
|
||||
.about("Generates the completion script for the given shell")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use error_chain::error_chain;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use lettre::transport::smtp::authentication::Credentials as SmtpCredentials;
|
||||
use log::debug;
|
||||
use serde::Deserialize;
|
||||
|
@ -15,8 +15,6 @@ use toml;
|
|||
|
||||
use crate::output::utils::run_cmd;
|
||||
|
||||
error_chain! {}
|
||||
|
||||
const DEFAULT_PAGE_SIZE: usize = 10;
|
||||
|
||||
// --- Account ---
|
||||
|
@ -79,7 +77,7 @@ impl Account {
|
|||
|
||||
/// Runs the given command in your password string and returns it.
|
||||
pub fn imap_passwd(&self) -> Result<String> {
|
||||
let passwd = run_cmd(&self.imap_passwd_cmd).chain_err(|| "Cannot run IMAP passwd cmd")?;
|
||||
let passwd = run_cmd(&self.imap_passwd_cmd).context("cannot run IMAP passwd cmd")?;
|
||||
let passwd = passwd
|
||||
.trim_end_matches(|c| c == '\r' || c == '\n')
|
||||
.to_owned();
|
||||
|
@ -108,7 +106,7 @@ impl Account {
|
|||
}
|
||||
|
||||
pub fn smtp_creds(&self) -> Result<SmtpCredentials> {
|
||||
let passwd = run_cmd(&self.smtp_passwd_cmd).chain_err(|| "Cannot run SMTP passwd cmd")?;
|
||||
let passwd = run_cmd(&self.smtp_passwd_cmd).context("cannot run SMTP passwd cmd")?;
|
||||
let passwd = passwd
|
||||
.trim_end_matches(|c| c == '\r' || c == '\n')
|
||||
.to_owned();
|
||||
|
@ -254,8 +252,7 @@ pub struct Config {
|
|||
|
||||
impl Config {
|
||||
fn path_from_xdg() -> Result<PathBuf> {
|
||||
let path =
|
||||
env::var("XDG_CONFIG_HOME").chain_err(|| "Cannot find `XDG_CONFIG_HOME` env var")?;
|
||||
let path = env::var("XDG_CONFIG_HOME").context("cannot find `XDG_CONFIG_HOME` env var")?;
|
||||
let mut path = PathBuf::from(path);
|
||||
path.push("himalaya");
|
||||
path.push("config.toml");
|
||||
|
@ -270,7 +267,7 @@ impl Config {
|
|||
"HOME"
|
||||
};
|
||||
let mut path: PathBuf = env::var(home_var)
|
||||
.chain_err(|| format!("Cannot find `{}` env var", home_var))?
|
||||
.context(format!("cannot find `{}` env var", home_var))?
|
||||
.into();
|
||||
path.push(".config");
|
||||
path.push("himalaya");
|
||||
|
@ -286,7 +283,7 @@ impl Config {
|
|||
"HOME"
|
||||
};
|
||||
let mut path: PathBuf = env::var(home_var)
|
||||
.chain_err(|| format!("Cannot find `{}` env var", home_var))?
|
||||
.context(format!("cannot find `{}` env var", home_var))?
|
||||
.into();
|
||||
path.push(".himalayarc");
|
||||
|
||||
|
@ -300,15 +297,15 @@ impl Config {
|
|||
None => Self::path_from_xdg()
|
||||
.or_else(|_| Self::path_from_xdg_alt())
|
||||
.or_else(|_| Self::path_from_home())
|
||||
.chain_err(|| "Cannot find config path")?,
|
||||
.context("cannot find config path")?,
|
||||
};
|
||||
|
||||
let mut file = File::open(path).chain_err(|| "Cannot open config file")?;
|
||||
let mut file = File::open(path).context("cannot open config file")?;
|
||||
let mut content = vec![];
|
||||
file.read_to_end(&mut content)
|
||||
.chain_err(|| "Cannot read config file")?;
|
||||
.context("cannot read config file")?;
|
||||
|
||||
Ok(toml::from_slice(&content).chain_err(|| "Cannot parse config file")?)
|
||||
Ok(toml::from_slice(&content).context("cannot parse config file")?)
|
||||
}
|
||||
|
||||
/// Returns the account by the given name.
|
||||
|
@ -320,11 +317,11 @@ impl Config {
|
|||
.iter()
|
||||
.find(|(_, account)| account.default.unwrap_or(false))
|
||||
.map(|(_, account)| account)
|
||||
.ok_or_else(|| "Cannot find default account".into()),
|
||||
.ok_or_else(|| anyhow!("cannot find default account")),
|
||||
Some(name) => self
|
||||
.accounts
|
||||
.get(name)
|
||||
.ok_or_else(|| format!("Cannot find account `{}`", name).into()),
|
||||
.ok_or_else(|| anyhow!(format!("cannot find account `{}`", name))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -414,7 +411,7 @@ impl Config {
|
|||
.map(|cmd| format!(r#"{} {:?} {:?}"#, cmd, subject, sender))
|
||||
.unwrap_or(default_cmd);
|
||||
|
||||
run_cmd(&cmd).chain_err(|| "Cannot run notify cmd")?;
|
||||
run_cmd(&cmd).context("cannot run notify cmd")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
use anyhow::Result;
|
||||
use clap;
|
||||
use error_chain::error_chain;
|
||||
use log::debug;
|
||||
|
||||
use crate::{ctx::Ctx, flag::model::Flags, imap::model::ImapConnector, msg::cli::uid_arg};
|
||||
|
||||
error_chain! {
|
||||
links {
|
||||
Imap(crate::imap::model::Error, crate::imap::model::ErrorKind);
|
||||
}
|
||||
}
|
||||
|
||||
fn flags_arg<'a>() -> clap::Arg<'a, 'a> {
|
||||
clap::Arg::with_name("flags")
|
||||
.help("IMAP flags (see https://tools.ietf.org/html/rfc3501#page-11). Just write the flag name without the backslash. Example: --flags \"Seen Answered\"")
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
use anyhow::Result;
|
||||
use clap;
|
||||
use error_chain::error_chain;
|
||||
use log::debug;
|
||||
|
||||
use crate::{ctx::Ctx, imap::model::ImapConnector};
|
||||
|
||||
error_chain! {
|
||||
links {
|
||||
Config(crate::config::model::Error, crate::config::model::ErrorKind);
|
||||
Imap(crate::imap::model::Error, crate::imap::model::ErrorKind);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||
vec![
|
||||
clap::SubCommand::with_name("notify")
|
||||
|
|
|
@ -1,20 +1,10 @@
|
|||
use error_chain::error_chain;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use imap;
|
||||
use log::{debug, trace};
|
||||
use native_tls::{self, TlsConnector, TlsStream};
|
||||
use std::{collections::HashSet, convert::TryFrom, iter::FromIterator, net::TcpStream};
|
||||
|
||||
use crate::config::model::Account;
|
||||
use crate::ctx::Ctx;
|
||||
use crate::flag::model::Flags;
|
||||
use crate::msg::model::Msg;
|
||||
|
||||
error_chain! {
|
||||
links {
|
||||
Config(crate::config::model::Error, crate::config::model::ErrorKind);
|
||||
MessageError(crate::msg::model::Error, crate::msg::model::ErrorKind);
|
||||
}
|
||||
}
|
||||
use crate::{config::model::Account, ctx::Ctx, flag::model::Flags, msg::model::Msg};
|
||||
|
||||
/// A little helper function to create a similiar error output. (to avoid duplicated code)
|
||||
fn format_err_msg(description: &str, account: &Account) -> String {
|
||||
|
@ -56,7 +46,7 @@ impl<'a> ImapConnector<'a> {
|
|||
.danger_accept_invalid_certs(insecure)
|
||||
.danger_accept_invalid_hostnames(insecure)
|
||||
.build()
|
||||
.chain_err(|| format_err_msg("Could not create TLS connector", account))?;
|
||||
.context(format_err_msg("cannot create TLS connector", account))?;
|
||||
|
||||
debug!("create client");
|
||||
let mut client_builder = imap::ClientBuilder::new(&account.imap_host, account.imap_port);
|
||||
|
@ -66,13 +56,13 @@ impl<'a> ImapConnector<'a> {
|
|||
}
|
||||
let client = client_builder
|
||||
.connect(|domain, tcp| Ok(TlsConnector::connect(&ssl_conn, domain, tcp)?))
|
||||
.chain_err(|| format_err_msg("Could not connect to IMAP server", account))?;
|
||||
.context(format_err_msg("cannot connect to IMAP server", account))?;
|
||||
|
||||
debug!("create session");
|
||||
let sess = client
|
||||
.login(&account.imap_login, &account.imap_passwd()?)
|
||||
.map_err(|res| res.0)
|
||||
.chain_err(|| format_err_msg("Could not login to IMAP server", account))?;
|
||||
.context(format_err_msg("cannot login to IMAP server", account))?;
|
||||
|
||||
Ok(Self { account, sess })
|
||||
}
|
||||
|
@ -113,11 +103,11 @@ impl<'a> ImapConnector<'a> {
|
|||
|
||||
self.sess
|
||||
.select(mbox)
|
||||
.chain_err(|| format!("Could not select mailbox `{}`", mbox))?;
|
||||
.context(format!("cannot select mailbox `{}`", mbox))?;
|
||||
|
||||
self.sess
|
||||
.uid_store(uid_seq, format!("FLAGS ({})", flags))
|
||||
.chain_err(|| format!("Could not set flags `{}`", &flags))?;
|
||||
.context(format!("cannot set flags `{}`", &flags))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -147,11 +137,11 @@ impl<'a> ImapConnector<'a> {
|
|||
|
||||
self.sess
|
||||
.select(mbox)
|
||||
.chain_err(|| format!("Could not select mailbox `{}`", mbox))?;
|
||||
.context(format!("cannot select mailbox `{}`", mbox))?;
|
||||
|
||||
self.sess
|
||||
.uid_store(uid_seq, format!("+FLAGS ({})", flags))
|
||||
.chain_err(|| format!("Could not add flags `{}`", &flags))?;
|
||||
.context(format!("cannot add flags `{}`", &flags))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -163,11 +153,11 @@ impl<'a> ImapConnector<'a> {
|
|||
|
||||
self.sess
|
||||
.select(mbox)
|
||||
.chain_err(|| format!("Could not select mailbox `{}`", mbox))?;
|
||||
.context(format!("cannot select mailbox `{}`", mbox))?;
|
||||
|
||||
self.sess
|
||||
.uid_store(uid_seq, format!("-FLAGS ({})", flags))
|
||||
.chain_err(|| format!("Could not remove flags `{}`", &flags))?;
|
||||
.context(format!("cannot remove flags `{}`", &flags))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -176,7 +166,7 @@ impl<'a> ImapConnector<'a> {
|
|||
let uids: Vec<u32> = self
|
||||
.sess
|
||||
.uid_search("NEW")
|
||||
.chain_err(|| "Could not search new messages")?
|
||||
.context("cannot search new messages")?
|
||||
.into_iter()
|
||||
.collect();
|
||||
debug!("found {} new messages", uids.len());
|
||||
|
@ -189,7 +179,7 @@ impl<'a> ImapConnector<'a> {
|
|||
debug!("examine mailbox: {}", &ctx.mbox);
|
||||
self.sess
|
||||
.examine(&ctx.mbox)
|
||||
.chain_err(|| format!("Could not examine mailbox `{}`", &ctx.mbox))?;
|
||||
.context(format!("cannot examine mailbox `{}`", &ctx.mbox))?;
|
||||
|
||||
debug!("init messages hashset");
|
||||
let mut msgs_set: HashSet<u32> =
|
||||
|
@ -208,7 +198,7 @@ impl<'a> ImapConnector<'a> {
|
|||
false
|
||||
})
|
||||
})
|
||||
.chain_err(|| "Could not start the idle mode")?;
|
||||
.context("cannot start the idle mode")?;
|
||||
|
||||
let uids: Vec<u32> = self
|
||||
.search_new_msgs()?
|
||||
|
@ -227,12 +217,12 @@ impl<'a> ImapConnector<'a> {
|
|||
let fetches = self
|
||||
.sess
|
||||
.uid_fetch(uids, "(ENVELOPE)")
|
||||
.chain_err(|| "Could not fetch new messages enveloppe")?;
|
||||
.context("cannot fetch new messages enveloppe")?;
|
||||
|
||||
for fetch in fetches.iter() {
|
||||
let msg = Msg::try_from(fetch)?;
|
||||
let uid = fetch.uid.ok_or_else(|| {
|
||||
format!("Could not retrieve message {}'s UID", fetch.message)
|
||||
anyhow!(format!("cannot retrieve message {}'s UID", fetch.message))
|
||||
})?;
|
||||
|
||||
let subject = msg.headers.subject.clone().unwrap_or_default();
|
||||
|
@ -255,7 +245,7 @@ impl<'a> ImapConnector<'a> {
|
|||
debug!("examine mailbox: {}", &ctx.mbox);
|
||||
self.sess
|
||||
.examine(&ctx.mbox)
|
||||
.chain_err(|| format!("Could not examine mailbox `{}`", &ctx.mbox))?;
|
||||
.context(format!("cannot examine mailbox `{}`", &ctx.mbox))?;
|
||||
|
||||
loop {
|
||||
debug!("begin loop");
|
||||
|
@ -269,7 +259,7 @@ impl<'a> ImapConnector<'a> {
|
|||
false
|
||||
})
|
||||
})
|
||||
.chain_err(|| "Could not start the idle mode")?;
|
||||
.context("cannot start the idle mode")?;
|
||||
ctx.config.exec_watch_cmds(&ctx.account)?;
|
||||
debug!("end loop");
|
||||
}
|
||||
|
@ -279,7 +269,7 @@ impl<'a> ImapConnector<'a> {
|
|||
let names = self
|
||||
.sess
|
||||
.list(Some(""), Some("*"))
|
||||
.chain_err(|| "Could not list mailboxes")?;
|
||||
.context("cannot list mailboxes")?;
|
||||
|
||||
Ok(names)
|
||||
}
|
||||
|
@ -293,7 +283,7 @@ impl<'a> ImapConnector<'a> {
|
|||
let last_seq = self
|
||||
.sess
|
||||
.select(mbox)
|
||||
.chain_err(|| format!("Could not select mailbox `{}`", mbox))?
|
||||
.context(format!("cannot select mailbox `{}`", mbox))?
|
||||
.exists as i64;
|
||||
|
||||
if last_seq == 0 {
|
||||
|
@ -313,7 +303,7 @@ impl<'a> ImapConnector<'a> {
|
|||
let fetches = self
|
||||
.sess
|
||||
.fetch(range, "(UID FLAGS ENVELOPE INTERNALDATE)")
|
||||
.chain_err(|| "Could not fetch messages")?;
|
||||
.context("cannot fetch messages")?;
|
||||
|
||||
Ok(Some(fetches))
|
||||
}
|
||||
|
@ -327,14 +317,17 @@ impl<'a> ImapConnector<'a> {
|
|||
) -> Result<Option<imap::types::ZeroCopy<Vec<imap::types::Fetch>>>> {
|
||||
self.sess
|
||||
.select(mbox)
|
||||
.chain_err(|| format!("Could not select mailbox `{}`", mbox))?;
|
||||
.context(format!("cannot select mailbox `{}`", mbox))?;
|
||||
|
||||
let begin = page * page_size;
|
||||
let end = begin + (page_size - 1);
|
||||
let uids: Vec<String> = self
|
||||
.sess
|
||||
.search(query)
|
||||
.chain_err(|| format!("Could not search in `{}` with query `{}`", mbox, query))?
|
||||
.context(format!(
|
||||
"cannot search in `{}` with query `{}`",
|
||||
mbox, query
|
||||
))?
|
||||
.iter()
|
||||
.map(|seq| seq.to_string())
|
||||
.collect();
|
||||
|
@ -347,7 +340,7 @@ impl<'a> ImapConnector<'a> {
|
|||
let fetches = self
|
||||
.sess
|
||||
.fetch(&range, "(UID FLAGS ENVELOPE INTERNALDATE)")
|
||||
.chain_err(|| format!("Could not fetch range `{}`", &range))?;
|
||||
.context(format!("cannot fetch range `{}`", &range))?;
|
||||
|
||||
Ok(Some(fetches))
|
||||
}
|
||||
|
@ -356,15 +349,15 @@ impl<'a> ImapConnector<'a> {
|
|||
pub fn get_msg(&mut self, mbox: &str, uid: &str) -> Result<Msg> {
|
||||
self.sess
|
||||
.select(mbox)
|
||||
.chain_err(|| format!("Could not select mailbox `{}`", mbox))?;
|
||||
.context(format!("cannot select mailbox `{}`", mbox))?;
|
||||
|
||||
match self
|
||||
.sess
|
||||
.uid_fetch(uid, "(FLAGS BODY[] ENVELOPE INTERNALDATE)")
|
||||
.chain_err(|| "Could not fetch bodies")?
|
||||
.context("cannot fetch bodies")?
|
||||
.first()
|
||||
{
|
||||
None => Err(format!("Could not find message `{}`", uid).into()),
|
||||
None => Err(anyhow!(format!("cannot find message `{}`", uid))),
|
||||
Some(fetch) => Ok(Msg::try_from(fetch)?),
|
||||
}
|
||||
}
|
||||
|
@ -378,7 +371,7 @@ impl<'a> ImapConnector<'a> {
|
|||
.append(mbox, &body)
|
||||
.flags(flags)
|
||||
.finish()
|
||||
.chain_err(|| format!("Could not append message to `{}`", mbox))?;
|
||||
.context(format!("cannot append message to `{}`", mbox))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -386,7 +379,7 @@ impl<'a> ImapConnector<'a> {
|
|||
pub fn expunge(&mut self, mbox: &str) -> Result<()> {
|
||||
self.sess
|
||||
.expunge()
|
||||
.chain_err(|| format!("Could not expunge `{}`", mbox))?;
|
||||
.context(format!("cannot expunge `{}`", mbox))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
48
src/input.rs
48
src/input.rs
|
@ -1,4 +1,4 @@
|
|||
use error_chain::error_chain;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use log::{debug, error, trace};
|
||||
use std::{
|
||||
env,
|
||||
|
@ -8,12 +8,6 @@ use std::{
|
|||
process::Command,
|
||||
};
|
||||
|
||||
error_chain! {
|
||||
foreign_links {
|
||||
Utf8(std::string::FromUtf8Error);
|
||||
}
|
||||
}
|
||||
|
||||
fn draft_path() -> PathBuf {
|
||||
env::temp_dir().join("himalaya-draft.mail")
|
||||
}
|
||||
|
@ -25,7 +19,7 @@ pub fn remove_draft() -> Result<()> {
|
|||
debug!("[input] draft path: {:?}", draft_path);
|
||||
|
||||
fs::remove_file(&draft_path)
|
||||
.chain_err(|| format!("Could not delete draft file {:?}", draft_path))
|
||||
.with_context(|| format!("cannot delete draft file {:?}", draft_path))
|
||||
}
|
||||
|
||||
pub fn open_editor_with_tpl(tpl: &[u8]) -> Result<String> {
|
||||
|
@ -42,7 +36,7 @@ pub fn open_editor_with_tpl(tpl: &[u8]) -> Result<String> {
|
|||
Ok(choice) => match choice {
|
||||
PreEditChoice::Edit => return open_editor_with_draft(),
|
||||
PreEditChoice::Discard => break,
|
||||
PreEditChoice::Quit => return Err("Edition aborted".into()),
|
||||
PreEditChoice::Quit => return Err(anyhow!("Edition aborted")),
|
||||
},
|
||||
Err(err) => error!("{}", err),
|
||||
}
|
||||
|
@ -51,22 +45,22 @@ pub fn open_editor_with_tpl(tpl: &[u8]) -> Result<String> {
|
|||
|
||||
debug!("[Input] create draft");
|
||||
File::create(&draft_path)
|
||||
.chain_err(|| format!("Could not create draft file {:?}", draft_path))?
|
||||
.with_context(|| format!("cannot create draft file {:?}", draft_path))?
|
||||
.write(tpl)
|
||||
.chain_err(|| format!("Could not write draft file {:?}", draft_path))?;
|
||||
.with_context(|| format!("cannot write draft file {:?}", draft_path))?;
|
||||
|
||||
debug!("[Input] open editor");
|
||||
Command::new(env::var("EDITOR").chain_err(|| "Could not find `EDITOR` env var")?)
|
||||
Command::new(env::var("EDITOR").with_context(|| "cannot find `EDITOR` env var")?)
|
||||
.arg(&draft_path)
|
||||
.status()
|
||||
.chain_err(|| "Could not launch editor")?;
|
||||
.with_context(|| "cannot launch editor")?;
|
||||
|
||||
debug!("[Input] read draft");
|
||||
let mut draft = String::new();
|
||||
File::open(&draft_path)
|
||||
.chain_err(|| format!("Could not open draft file {:?}", draft_path))?
|
||||
.with_context(|| format!("cannot open draft file {:?}", draft_path))?
|
||||
.read_to_string(&mut draft)
|
||||
.chain_err(|| format!("Could not read draft file {:?}", draft_path))?;
|
||||
.with_context(|| format!("cannot read draft file {:?}", draft_path))?;
|
||||
|
||||
Ok(draft)
|
||||
}
|
||||
|
@ -78,17 +72,17 @@ pub fn open_editor_with_draft() -> Result<String> {
|
|||
debug!("[input] draft path: {:?}", draft_path);
|
||||
|
||||
// Opens editor and saves user input to draft file
|
||||
Command::new(env::var("EDITOR").chain_err(|| "Could not find `EDITOR` env var")?)
|
||||
Command::new(env::var("EDITOR").with_context(|| "cannot find `EDITOR` env var")?)
|
||||
.arg(&draft_path)
|
||||
.status()
|
||||
.chain_err(|| "Could not launch editor")?;
|
||||
.with_context(|| "cannot launch editor")?;
|
||||
|
||||
// Extracts draft file content
|
||||
let mut draft = String::new();
|
||||
File::open(&draft_path)
|
||||
.chain_err(|| format!("Could not open file {:?}", draft_path))?
|
||||
.with_context(|| format!("cannot open file {:?}", draft_path))?
|
||||
.read_to_string(&mut draft)
|
||||
.chain_err(|| format!("Could not read file {:?}", draft_path))?;
|
||||
.with_context(|| format!("cannot read file {:?}", draft_path))?;
|
||||
|
||||
Ok(draft)
|
||||
}
|
||||
|
@ -106,12 +100,12 @@ pub fn pre_edit_choice() -> Result<PreEditChoice> {
|
|||
print!("(e)dit, (d)iscard or (q)uit? ");
|
||||
io::stdout()
|
||||
.flush()
|
||||
.chain_err(|| "Could not flush stdout")?;
|
||||
.with_context(|| "cannot flush stdout")?;
|
||||
|
||||
let mut buf = String::new();
|
||||
io::stdin()
|
||||
.read_line(&mut buf)
|
||||
.chain_err(|| "Could not read stdin")?;
|
||||
.with_context(|| "cannot read stdin")?;
|
||||
|
||||
match buf.bytes().next().map(|bytes| bytes as char) {
|
||||
Some('e') => {
|
||||
|
@ -128,11 +122,11 @@ pub fn pre_edit_choice() -> Result<PreEditChoice> {
|
|||
}
|
||||
Some(choice) => {
|
||||
debug!("[input] pre edit choice: invalid choice {}", choice);
|
||||
Err(format!("Invalid choice `{}`", choice).into())
|
||||
Err(anyhow!(format!("Invalid choice `{}`", choice)))
|
||||
}
|
||||
None => {
|
||||
debug!("[input] pre edit choice: empty choice");
|
||||
Err("Empty choice".into())
|
||||
Err(anyhow!("Empty choice"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -149,12 +143,12 @@ pub fn post_edit_choice() -> Result<PostEditChoice> {
|
|||
print!("(s)end, (e)dit, (l)ocal/(r)emote draft or (d)iscard? ");
|
||||
io::stdout()
|
||||
.flush()
|
||||
.chain_err(|| "Could not flush stdout")?;
|
||||
.with_context(|| "cannot flush stdout")?;
|
||||
|
||||
let mut buf = String::new();
|
||||
io::stdin()
|
||||
.read_line(&mut buf)
|
||||
.chain_err(|| "Could not read stdin")?;
|
||||
.with_context(|| "cannot read stdin")?;
|
||||
|
||||
match buf.bytes().next().map(|bytes| bytes as char) {
|
||||
Some('s') => Ok(PostEditChoice::Send),
|
||||
|
@ -162,7 +156,7 @@ pub fn post_edit_choice() -> Result<PostEditChoice> {
|
|||
Some('r') => Ok(PostEditChoice::RemoteDraft),
|
||||
Some('e') => Ok(PostEditChoice::Edit),
|
||||
Some('d') => Ok(PostEditChoice::Discard),
|
||||
Some(choice) => Err(format!("Invalid choice `{}`", choice).into()),
|
||||
None => Err("Empty choice".into()),
|
||||
Some(choice) => Err(anyhow!(format!("Invalid choice `{}`", choice))),
|
||||
None => Err(anyhow!("Empty choice")),
|
||||
}
|
||||
}
|
||||
|
|
45
src/main.rs
45
src/main.rs
|
@ -1,8 +1,8 @@
|
|||
use anyhow::Result;
|
||||
use clap::{self, ArgMatches};
|
||||
use env_logger;
|
||||
use error_chain::error_chain;
|
||||
use log::{debug, error, trace};
|
||||
use std::{env, path::PathBuf, process::exit};
|
||||
use log::{debug, trace};
|
||||
use std::{env, path::PathBuf};
|
||||
use url::{self, Url};
|
||||
|
||||
use himalaya::{
|
||||
|
@ -14,20 +14,6 @@ use himalaya::{
|
|||
output::{cli::output_args, model::Output},
|
||||
};
|
||||
|
||||
error_chain! {
|
||||
links {
|
||||
CompletionCli(himalaya::comp::cli::Error, himalaya::comp::cli::ErrorKind);
|
||||
Config(himalaya::config::model::Error, himalaya::config::model::ErrorKind);
|
||||
FlagCli(himalaya::flag::cli::Error, himalaya::flag::cli::ErrorKind);
|
||||
ImapCli(himalaya::imap::cli::Error, himalaya::imap::cli::ErrorKind);
|
||||
MboxCli(himalaya::mbox::cli::Error, himalaya::mbox::cli::ErrorKind);
|
||||
MsgCli(himalaya::msg::cli::Error, himalaya::msg::cli::ErrorKind);
|
||||
}
|
||||
foreign_links {
|
||||
Url(url::ParseError);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_args<'a>() -> clap::App<'a, 'a> {
|
||||
clap::App::new(env!("CARGO_PKG_NAME"))
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
|
@ -44,7 +30,7 @@ fn parse_args<'a>() -> clap::App<'a, 'a> {
|
|||
.subcommands(comp::cli::subcmds())
|
||||
}
|
||||
|
||||
fn run() -> Result<()> {
|
||||
fn main() -> Result<()> {
|
||||
env_logger::init_from_env(
|
||||
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "off"),
|
||||
);
|
||||
|
@ -99,26 +85,3 @@ fn run() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(ref errs) = run() {
|
||||
let mut errs = errs.iter();
|
||||
|
||||
match errs.next() {
|
||||
None => (),
|
||||
Some(err) => {
|
||||
error!("{}", err);
|
||||
eprintln!("{}", err);
|
||||
|
||||
errs.for_each(|err| {
|
||||
error!("{}", err);
|
||||
eprintln!(" ↳ {}", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
exit(1);
|
||||
} else {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
use anyhow::Result;
|
||||
use clap;
|
||||
use error_chain::error_chain;
|
||||
use log::{debug, trace};
|
||||
|
||||
use crate::{ctx::Ctx, imap::model::ImapConnector, mbox::model::Mboxes};
|
||||
|
||||
error_chain! {
|
||||
links {
|
||||
Imap(crate::imap::model::Error, crate::imap::model::ErrorKind);
|
||||
}
|
||||
}
|
||||
|
||||
// == Main functions ==
|
||||
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||
vec![clap::SubCommand::with_name("mailboxes")
|
||||
.aliases(&["mailbox", "mboxes", "mbox", "m"])
|
||||
|
|
|
@ -1,23 +1,9 @@
|
|||
use anyhow::{Error, Result};
|
||||
use lettre::message::header::ContentType;
|
||||
|
||||
use mailparse::{DispositionType, ParsedMail};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use serde::Serialize;
|
||||
use std::{convert::TryFrom, fs, path::Path};
|
||||
|
||||
use error_chain::error_chain;
|
||||
|
||||
error_chain! {
|
||||
foreign_links {
|
||||
ContentType(lettre::message::header::ContentTypeErr);
|
||||
FileSytem(std::io::Error);
|
||||
}
|
||||
}
|
||||
|
||||
// == Structs ==
|
||||
/// This struct represents an attachment.
|
||||
#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
|
|
@ -1,17 +1,6 @@
|
|||
use error_chain::error_chain;
|
||||
|
||||
use serde::Serialize;
|
||||
use std::fmt;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
// == Macros ==
|
||||
error_chain! {
|
||||
foreign_links {
|
||||
ParseContentType(lettre::message::header::ContentTypeErr);
|
||||
}
|
||||
}
|
||||
|
||||
// == Structs ==
|
||||
/// This struct represents the body/content of a msg. For example:
|
||||
///
|
||||
/// ```text
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
use super::body::Body;
|
||||
use super::headers::Headers;
|
||||
use super::model::{Msg, MsgSerialized, Msgs};
|
||||
use url::Url;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use atty::Stream;
|
||||
use clap;
|
||||
use error_chain::error_chain;
|
||||
use imap::types::Flag;
|
||||
use lettre::message::header::ContentTransferEncoding;
|
||||
use log::{debug, error, trace};
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
|
@ -16,26 +11,18 @@ use std::{
|
|||
fs,
|
||||
io::{self, BufRead},
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
use imap::types::Flag;
|
||||
|
||||
use super::{
|
||||
body::Body,
|
||||
headers::Headers,
|
||||
model::{Msg, MsgSerialized, Msgs},
|
||||
};
|
||||
use crate::{
|
||||
ctx::Ctx, flag::model::Flags, imap::model::ImapConnector, input, mbox::cli::mbox_target_arg,
|
||||
smtp,
|
||||
};
|
||||
|
||||
error_chain! {
|
||||
links {
|
||||
Imap(crate::imap::model::Error, crate::imap::model::ErrorKind);
|
||||
Input(crate::input::Error, crate::input::ErrorKind);
|
||||
MsgModel(super::model::Error, super::model::ErrorKind);
|
||||
Smtp(crate::smtp::Error, crate::smtp::ErrorKind);
|
||||
}
|
||||
foreign_links {
|
||||
Utf8(std::string::FromUtf8Error);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||
vec![
|
||||
clap::SubCommand::with_name("list")
|
||||
|
@ -384,7 +371,7 @@ fn msg_matches_attachments(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool
|
|||
debug!("downloading {}…", &attachment.filename);
|
||||
|
||||
fs::write(&filepath, &attachment.body_raw)
|
||||
.chain_err(|| format!("Could not save attachment {:?}", filepath))?;
|
||||
.with_context(|| format!("cannot save attachment {:?}", filepath))?;
|
||||
}
|
||||
|
||||
debug!(
|
||||
|
@ -681,7 +668,7 @@ pub fn msg_matches_tpl(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
|||
("forward", Some(matches)) => tpl_matches_forward(ctx, matches),
|
||||
|
||||
// TODO: find a way to show the help message for template subcommand
|
||||
_ => Err("Subcommand not found".into()),
|
||||
_ => Err(anyhow!("Subcommand not found")),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -887,7 +874,7 @@ fn msg_interaction(ctx: &Ctx, msg: &mut Msg, imap_conn: &mut ImapConnector) -> R
|
|||
ctx.output.print("Message successfully saved to Drafts");
|
||||
}
|
||||
Err(err) => {
|
||||
ctx.output.print("Couldn't save it to the server...");
|
||||
ctx.output.print("Cannot save draft to the server");
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,32 +1,10 @@
|
|||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
|
||||
use log::{debug, warn};
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
use rfc2047_decoder;
|
||||
|
||||
use error_chain::error_chain;
|
||||
|
||||
use anyhow::{anyhow, Error, Result};
|
||||
use lettre::message::header::ContentTransferEncoding;
|
||||
use log::{debug, warn};
|
||||
use rfc2047_decoder;
|
||||
use serde::Serialize;
|
||||
use std::{borrow::Cow, collections::HashMap, convert::TryFrom, fmt};
|
||||
|
||||
error_chain! {
|
||||
errors {
|
||||
Convertion(field: &'static str) {
|
||||
display("Couldn't get the data from the '{}:' field.", field),
|
||||
}
|
||||
}
|
||||
|
||||
foreign_links {
|
||||
StringFromUtf8(std::string::FromUtf8Error);
|
||||
Rfc2047Decoder(rfc2047_decoder::Error);
|
||||
}
|
||||
}
|
||||
|
||||
// == Structs ==
|
||||
/// This struct is a wrapper for the [Envelope struct] of the [imap_proto]
|
||||
/// crate. It's should mainly help to interact with the mails by using more
|
||||
/// common data types like `Vec` or `String` since a `[u8]` array is a little
|
||||
|
@ -245,7 +223,7 @@ impl TryFrom<Option<&imap_proto::types::Envelope<'_>>> for Headers {
|
|||
|
||||
let from = match convert_vec_address_to_string(envelope.from.as_ref())? {
|
||||
Some(from) => from,
|
||||
None => return Err(ErrorKind::Convertion("From").into()),
|
||||
None => return Err(anyhow!("cannot extract senders from envelope")),
|
||||
};
|
||||
|
||||
// only the first address is used, because how should multiple machines send the same
|
||||
|
@ -266,7 +244,7 @@ impl TryFrom<Option<&imap_proto::types::Envelope<'_>>> for Headers {
|
|||
let reply_to = convert_vec_address_to_string(envelope.reply_to.as_ref())?;
|
||||
let to = match convert_vec_address_to_string(envelope.to.as_ref())? {
|
||||
Some(to) => to,
|
||||
None => return Err(ErrorKind::Convertion("To").into()),
|
||||
None => return Err(anyhow!("cannot extract recipients from envelope")),
|
||||
};
|
||||
let cc = convert_vec_address_to_string(envelope.cc.as_ref())?;
|
||||
let bcc = convert_vec_address_to_string(envelope.bcc.as_ref())?;
|
||||
|
|
200
src/msg/model.rs
200
src/msg/model.rs
|
@ -1,13 +1,9 @@
|
|||
use super::attachment::Attachment;
|
||||
use super::body::Body;
|
||||
use super::headers::Headers;
|
||||
|
||||
use log::debug;
|
||||
|
||||
use anyhow::{anyhow, Context, Error, Result};
|
||||
use imap::types::{Fetch, Flag, ZeroCopy};
|
||||
|
||||
use log::debug;
|
||||
use mailparse;
|
||||
|
||||
use super::{attachment::Attachment, body::Body, headers::Headers};
|
||||
use crate::{
|
||||
ctx::Ctx,
|
||||
flag::model::Flags,
|
||||
|
@ -29,46 +25,6 @@ use std::{
|
|||
fmt,
|
||||
};
|
||||
|
||||
use colorful::Colorful;
|
||||
|
||||
// == Macros ==
|
||||
error_chain::error_chain! {
|
||||
errors {
|
||||
ParseBody (err: String) {
|
||||
description("An error appeared, when trying to parse the body of the msg!"),
|
||||
display("Couldn't get the body of the parsed msg: {}", err),
|
||||
}
|
||||
|
||||
/// Is mainly used in the "to_sendable_msg" function
|
||||
Header(error_msg: String, header_name: &'static str, header_input: String) {
|
||||
|
||||
description("An error happened, when trying to parse a header-field."),
|
||||
display(concat![
|
||||
"[{}] {}\n",
|
||||
"Header-Field-Name: '{}'\n",
|
||||
"The word which let this error occur: '{}'"],
|
||||
"Error".red(),
|
||||
error_msg.clone().light_red(),
|
||||
header_name.light_blue(),
|
||||
header_input.clone().light_cyan()),
|
||||
}
|
||||
}
|
||||
|
||||
links {
|
||||
Attachment(super::attachment::Error, super::attachment::ErrorKind);
|
||||
Headers(super::headers::Error, super::headers::ErrorKind);
|
||||
Input(crate::input::Error, crate::input::ErrorKind);
|
||||
}
|
||||
|
||||
foreign_links {
|
||||
MailParse(mailparse::MailParseError);
|
||||
Lettre(lettre::error::Error);
|
||||
LettreAddress(lettre::address::AddressError);
|
||||
FromUtf8Error(std::string::FromUtf8Error);
|
||||
}
|
||||
}
|
||||
|
||||
// == Msg ==
|
||||
/// Represents the msg in a serializeable form with additional values.
|
||||
/// This struct-type makes it also possible to print the msg in a serialized form or in a normal
|
||||
/// form.
|
||||
|
@ -528,13 +484,13 @@ impl Msg {
|
|||
/// ```
|
||||
pub fn parse_from_str(&mut self, content: &str) -> Result<()> {
|
||||
let parsed = mailparse::parse_mail(content.as_bytes())
|
||||
.chain_err(|| format!("How the message looks like currently:\n{}", self))?;
|
||||
.with_context(|| format!("How the message looks like currently:\n{}", self))?;
|
||||
|
||||
self.headers = Headers::from(&parsed);
|
||||
|
||||
match parsed.get_body() {
|
||||
Ok(body) => self.body = Body::new_with_text(body),
|
||||
Err(err) => return Err(ErrorKind::ParseBody(err.to_string()).into()),
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
|
@ -623,76 +579,72 @@ impl Msg {
|
|||
// -- Must-have-fields --
|
||||
// add "from"
|
||||
for mailaddress in &self.headers.from {
|
||||
msg = msg.from(match mailaddress.parse() {
|
||||
Ok(from) => from,
|
||||
Err(err) => {
|
||||
return Err(
|
||||
ErrorKind::Header(err.to_string(), "From", mailaddress.to_string()).into(),
|
||||
)
|
||||
}
|
||||
});
|
||||
msg = msg.from(
|
||||
match mailaddress
|
||||
.parse()
|
||||
.with_context(|| "cannot parse `From` header")
|
||||
{
|
||||
Ok(from) => from,
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// add "to"
|
||||
for mailaddress in &self.headers.to {
|
||||
msg = msg.to(match mailaddress.parse() {
|
||||
Ok(to) => to,
|
||||
Err(err) => {
|
||||
return Err(
|
||||
ErrorKind::Header(err.to_string(), "To", mailaddress.to_string()).into(),
|
||||
)
|
||||
}
|
||||
});
|
||||
msg = msg.to(
|
||||
match mailaddress
|
||||
.parse()
|
||||
.with_context(|| "cannot parse `To` header")
|
||||
{
|
||||
Ok(from) => from,
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// -- Optional fields --
|
||||
// add "bcc"
|
||||
if let Some(bcc) = &self.headers.bcc {
|
||||
for mailaddress in bcc {
|
||||
msg = msg.bcc(match mailaddress.parse() {
|
||||
Ok(bcc) => bcc,
|
||||
Err(err) => {
|
||||
return Err(ErrorKind::Header(
|
||||
err.to_string(),
|
||||
"Bcc",
|
||||
mailaddress.to_string(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
});
|
||||
msg = msg.bcc(
|
||||
match mailaddress
|
||||
.parse()
|
||||
.with_context(|| "cannot parse `Bcc` header")
|
||||
{
|
||||
Ok(from) => from,
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// add "cc"
|
||||
if let Some(cc) = &self.headers.cc {
|
||||
for mailaddress in cc {
|
||||
msg = msg.cc(match mailaddress.parse() {
|
||||
Ok(cc) => cc,
|
||||
Err(err) => {
|
||||
return Err(ErrorKind::Header(
|
||||
err.to_string(),
|
||||
"Cc",
|
||||
mailaddress.to_string(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
});
|
||||
msg = msg.cc(
|
||||
match mailaddress
|
||||
.parse()
|
||||
.with_context(|| "cannot parse `Cc` header")
|
||||
{
|
||||
Ok(from) => from,
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// add "in_reply_to"
|
||||
if let Some(in_reply_to) = &self.headers.in_reply_to {
|
||||
msg = msg.in_reply_to(match in_reply_to.parse() {
|
||||
Ok(in_reply_to) => in_reply_to,
|
||||
Err(err) => {
|
||||
return Err(ErrorKind::Header(
|
||||
err.to_string(),
|
||||
"In-Reply-To",
|
||||
in_reply_to.to_string(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
});
|
||||
msg = msg.in_reply_to(
|
||||
match in_reply_to
|
||||
.parse()
|
||||
.with_context(|| "cannot parse `In-Reply-To` header")
|
||||
{
|
||||
Ok(from) => from,
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// add message-id if it exists
|
||||
|
@ -713,30 +665,29 @@ impl Msg {
|
|||
// add "reply-to"
|
||||
if let Some(reply_to) = &self.headers.reply_to {
|
||||
for mailaddress in reply_to {
|
||||
msg = msg.reply_to(match mailaddress.parse() {
|
||||
Ok(reply_to) => reply_to,
|
||||
Err(err) => {
|
||||
return Err(ErrorKind::Header(
|
||||
err.to_string(),
|
||||
"Reply-to",
|
||||
mailaddress.to_string(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
});
|
||||
msg = msg.reply_to(
|
||||
match mailaddress
|
||||
.parse()
|
||||
.with_context(|| "cannot parse `Reply-To` header")
|
||||
{
|
||||
Ok(from) => from,
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// add "sender"
|
||||
if let Some(sender) = &self.headers.sender {
|
||||
msg = msg.sender(match sender.parse() {
|
||||
Ok(sender) => sender,
|
||||
Err(err) => {
|
||||
return Err(
|
||||
ErrorKind::Header(err.to_string(), "Sender", sender.to_string()).into(),
|
||||
)
|
||||
}
|
||||
});
|
||||
msg = msg.sender(
|
||||
match sender
|
||||
.parse()
|
||||
.with_context(|| "cannot parse `Sender` header")
|
||||
{
|
||||
Ok(from) => from,
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// add subject
|
||||
|
@ -779,7 +730,7 @@ impl Msg {
|
|||
.multipart(msg_parts)
|
||||
// whenever an error appears, print out the messge as well to see what might be the
|
||||
// error
|
||||
.chain_err(|| format!("-- Current Message --\n{}", self))?)
|
||||
.context(format!("-- Current Message --\n{}", self))?)
|
||||
}
|
||||
|
||||
/// Returns the uid of the msg.
|
||||
|
@ -802,12 +753,8 @@ impl Msg {
|
|||
|
||||
/// Returns the raw mail as a string instead of a Vector of bytes.
|
||||
pub fn get_raw_as_string(&self) -> Result<String> {
|
||||
let raw_message = String::from_utf8(self.raw.clone()).chain_err(|| {
|
||||
format!(
|
||||
"[{}]: Couldn't parse the raw message as string.",
|
||||
"Error".red()
|
||||
)
|
||||
})?;
|
||||
let raw_message = String::from_utf8(self.raw.clone())
|
||||
.context(format!("cannot parse raw message as string"))?;
|
||||
|
||||
Ok(raw_message)
|
||||
}
|
||||
|
@ -974,8 +921,7 @@ impl TryFrom<&Fetch> for Msg {
|
|||
// the body of the mail but something else. Log that!
|
||||
else {
|
||||
println!(
|
||||
"[{}] Unknown attachment with the following mime-type: {}\n",
|
||||
"Warning".yellow(),
|
||||
"Unknown attachment with the following mime-type: {}",
|
||||
subpart.ctype.mimetype,
|
||||
);
|
||||
}
|
||||
|
@ -1219,7 +1165,7 @@ mod tests {
|
|||
// -- for general tests --
|
||||
let ctx = Ctx {
|
||||
account: Account::new(Some("Name"), "some@address.asdf"),
|
||||
config: config,
|
||||
config,
|
||||
mbox: String::from("INBOX"),
|
||||
..Ctx::default()
|
||||
};
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
use error_chain::error_chain;
|
||||
use anyhow::Result;
|
||||
use serde::ser::{self, SerializeStruct};
|
||||
use std::{fmt, process::Command, result};
|
||||
|
||||
error_chain! {
|
||||
foreign_links {
|
||||
Utf8(std::string::FromUtf8Error);
|
||||
Io(std::io::Error);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Info(pub String);
|
||||
|
||||
impl fmt::Display for Info {
|
||||
|
|
11
src/smtp.rs
11
src/smtp.rs
|
@ -1,4 +1,4 @@
|
|||
use error_chain::error_chain;
|
||||
use anyhow::Result;
|
||||
use lettre::{
|
||||
self,
|
||||
transport::{smtp::client::Tls, smtp::client::TlsParameters, smtp::SmtpTransport},
|
||||
|
@ -7,15 +7,6 @@ use lettre::{
|
|||
|
||||
use crate::config::model::Account;
|
||||
|
||||
error_chain! {
|
||||
links {
|
||||
Config(crate::config::model::Error, crate::config::model::ErrorKind);
|
||||
}
|
||||
foreign_links {
|
||||
Smtp(lettre::transport::smtp::Error);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send(account: &Account, msg: &lettre::Message) -> Result<()> {
|
||||
let smtp_relay = if account.smtp_starttls() {
|
||||
SmtpTransport::starttls_relay
|
||||
|
|
Loading…
Reference in a new issue