replace error-chain by anyhow (#152)

This commit is contained in:
Clément DOUIN 2021-09-13 11:52:20 +02:00
parent fa622ba1db
commit c619f06206
No known key found for this signature in database
GPG key ID: 69C9B9CFFDEE2DEF
18 changed files with 180 additions and 454 deletions

View file

@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Pagination for list and search cmd starts from 1 instead of 0 [#186] - Pagination for list and search cmd starts from 1 instead of 0 [#186]
- Errors management with `anyhow` [#152]
### Fixed ### 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 [#141]: https://github.com/soywod/himalaya/issues/141
[#144]: https://github.com/soywod/himalaya/issues/144 [#144]: https://github.com/soywod/himalaya/issues/144
[#146]: https://github.com/soywod/himalaya/issues/146 [#146]: https://github.com/soywod/himalaya/issues/146
[#152]: https://github.com/soywod/himalaya/issues/152
[#160]: https://github.com/soywod/himalaya/issues/160 [#160]: https://github.com/soywod/himalaya/issues/160
[#176]: https://github.com/soywod/himalaya/issues/176 [#176]: https://github.com/soywod/himalaya/issues/176
[#186]: https://github.com/soywod/himalaya/issues/186 [#186]: https://github.com/soywod/himalaya/issues/186

83
Cargo.lock generated
View file

@ -2,21 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 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]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.15" version = "0.7.15"
@ -35,6 +20,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "anyhow"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
[[package]] [[package]]
name = "ascii_utils" name = "ascii_utils"
version = "0.9.3" version = "0.9.3"
@ -64,21 +55,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 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]] [[package]]
name = "base64" name = "base64"
version = "0.9.3" version = "0.9.3"
@ -198,12 +174,6 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "colorful"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bca1619ff57dd7a56b58a8e25ef4199f123e78e503fe1653410350a1b98ae65"
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.1" version = "0.9.1"
@ -342,16 +312,6 @@ dependencies = [
"termcolor", "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]] [[package]]
name = "fast_chemail" name = "fast_chemail"
version = "0.9.6" version = "0.9.6"
@ -464,12 +424,6 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "gimli"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.11.2" version = "0.11.2"
@ -489,12 +443,11 @@ dependencies = [
name = "himalaya" name = "himalaya"
version = "0.4.0" version = "0.4.0"
dependencies = [ dependencies = [
"anyhow",
"atty", "atty",
"chrono", "chrono",
"clap", "clap",
"colorful",
"env_logger", "env_logger",
"error-chain",
"imap", "imap",
"imap-proto", "imap-proto",
"lettre 0.10.0-rc.3", "lettre 0.10.0-rc.3",
@ -732,16 +685,6 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6595bb28ed34f43c3fe088e48f6cfb2e033cab45f25a5384d5fdf564fbc8c4b2" 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]] [[package]]
name = "native-tls" name = "native-tls"
version = "0.2.8" version = "0.2.8"
@ -811,12 +754,6 @@ dependencies = [
"autocfg 1.0.1", "autocfg 1.0.1",
] ]
[[package]]
name = "object"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170"
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.8.0" version = "1.8.0"
@ -1216,12 +1153,6 @@ dependencies = [
"quoted_printable", "quoted_printable",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.5" version = "1.0.5"

View file

@ -6,12 +6,11 @@ authors = ["soywod <clement.douin@posteo.net>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
anyhow = "1.0.44"
atty = "0.2.14" atty = "0.2.14"
chrono = "0.4.19" chrono = "0.4.19"
clap = { version = "2.33.3", default-features = false, features = ["suggestions", "color"] } clap = { version = "2.33.3", default-features = false, features = ["suggestions", "color"] }
colorful = "0.2.1"
env_logger = "0.8.3" env_logger = "0.8.3"
error-chain = "0.12.4"
imap = "3.0.0-alpha.4" imap = "3.0.0-alpha.4"
imap-proto = "0.14.3" imap-proto = "0.14.3"
# This commit includes the de/serialization of the ContentType # This commit includes the de/serialization of the ContentType

View file

@ -1,11 +1,8 @@
use anyhow::Result;
use clap::{self, App, Arg, ArgMatches, Shell, SubCommand}; use clap::{self, App, Arg, ArgMatches, Shell, SubCommand};
use error_chain::error_chain;
use log::debug; use log::debug;
use std::io; use std::io;
error_chain! {}
// == Main functions ==
pub fn subcmds<'s>() -> Vec<App<'s, 's>> { pub fn subcmds<'s>() -> Vec<App<'s, 's>> {
vec![SubCommand::with_name("completion") vec![SubCommand::with_name("completion")
.about("Generates the completion script for the given shell") .about("Generates the completion script for the given shell")

View file

@ -1,4 +1,4 @@
use error_chain::error_chain; use anyhow::{anyhow, Context, Result};
use lettre::transport::smtp::authentication::Credentials as SmtpCredentials; use lettre::transport::smtp::authentication::Credentials as SmtpCredentials;
use log::debug; use log::debug;
use serde::Deserialize; use serde::Deserialize;
@ -15,8 +15,6 @@ use toml;
use crate::output::utils::run_cmd; use crate::output::utils::run_cmd;
error_chain! {}
const DEFAULT_PAGE_SIZE: usize = 10; const DEFAULT_PAGE_SIZE: usize = 10;
// --- Account --- // --- Account ---
@ -79,7 +77,7 @@ impl Account {
/// Runs the given command in your password string and returns it. /// Runs the given command in your password string and returns it.
pub fn imap_passwd(&self) -> Result<String> { 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 let passwd = passwd
.trim_end_matches(|c| c == '\r' || c == '\n') .trim_end_matches(|c| c == '\r' || c == '\n')
.to_owned(); .to_owned();
@ -108,7 +106,7 @@ impl Account {
} }
pub fn smtp_creds(&self) -> Result<SmtpCredentials> { 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 let passwd = passwd
.trim_end_matches(|c| c == '\r' || c == '\n') .trim_end_matches(|c| c == '\r' || c == '\n')
.to_owned(); .to_owned();
@ -254,8 +252,7 @@ pub struct Config {
impl Config { impl Config {
fn path_from_xdg() -> Result<PathBuf> { fn path_from_xdg() -> Result<PathBuf> {
let path = let path = env::var("XDG_CONFIG_HOME").context("cannot find `XDG_CONFIG_HOME` env var")?;
env::var("XDG_CONFIG_HOME").chain_err(|| "Cannot find `XDG_CONFIG_HOME` env var")?;
let mut path = PathBuf::from(path); let mut path = PathBuf::from(path);
path.push("himalaya"); path.push("himalaya");
path.push("config.toml"); path.push("config.toml");
@ -270,7 +267,7 @@ impl Config {
"HOME" "HOME"
}; };
let mut path: PathBuf = env::var(home_var) 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(); .into();
path.push(".config"); path.push(".config");
path.push("himalaya"); path.push("himalaya");
@ -286,7 +283,7 @@ impl Config {
"HOME" "HOME"
}; };
let mut path: PathBuf = env::var(home_var) 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(); .into();
path.push(".himalayarc"); path.push(".himalayarc");
@ -300,15 +297,15 @@ impl Config {
None => Self::path_from_xdg() None => Self::path_from_xdg()
.or_else(|_| Self::path_from_xdg_alt()) .or_else(|_| Self::path_from_xdg_alt())
.or_else(|_| Self::path_from_home()) .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![]; let mut content = vec![];
file.read_to_end(&mut content) 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. /// Returns the account by the given name.
@ -320,11 +317,11 @@ impl Config {
.iter() .iter()
.find(|(_, account)| account.default.unwrap_or(false)) .find(|(_, account)| account.default.unwrap_or(false))
.map(|(_, account)| account) .map(|(_, account)| account)
.ok_or_else(|| "Cannot find default account".into()), .ok_or_else(|| anyhow!("cannot find default account")),
Some(name) => self Some(name) => self
.accounts .accounts
.get(name) .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)) .map(|cmd| format!(r#"{} {:?} {:?}"#, cmd, subject, sender))
.unwrap_or(default_cmd); .unwrap_or(default_cmd);
run_cmd(&cmd).chain_err(|| "Cannot run notify cmd")?; run_cmd(&cmd).context("cannot run notify cmd")?;
Ok(()) Ok(())
} }

View file

@ -1,15 +1,9 @@
use anyhow::Result;
use clap; use clap;
use error_chain::error_chain;
use log::debug; use log::debug;
use crate::{ctx::Ctx, flag::model::Flags, imap::model::ImapConnector, msg::cli::uid_arg}; 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> { fn flags_arg<'a>() -> clap::Arg<'a, 'a> {
clap::Arg::with_name("flags") 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\"") .help("IMAP flags (see https://tools.ietf.org/html/rfc3501#page-11). Just write the flag name without the backslash. Example: --flags \"Seen Answered\"")

View file

@ -1,16 +1,9 @@
use anyhow::Result;
use clap; use clap;
use error_chain::error_chain;
use log::debug; use log::debug;
use crate::{ctx::Ctx, imap::model::ImapConnector}; 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>> { pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
vec![ vec![
clap::SubCommand::with_name("notify") clap::SubCommand::with_name("notify")

View file

@ -1,20 +1,10 @@
use error_chain::error_chain; use anyhow::{anyhow, Context, Result};
use imap; use imap;
use log::{debug, trace}; use log::{debug, trace};
use native_tls::{self, TlsConnector, TlsStream}; use native_tls::{self, TlsConnector, TlsStream};
use std::{collections::HashSet, convert::TryFrom, iter::FromIterator, net::TcpStream}; use std::{collections::HashSet, convert::TryFrom, iter::FromIterator, net::TcpStream};
use crate::config::model::Account; use crate::{config::model::Account, ctx::Ctx, flag::model::Flags, msg::model::Msg};
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);
}
}
/// A little helper function to create a similiar error output. (to avoid duplicated code) /// A little helper function to create a similiar error output. (to avoid duplicated code)
fn format_err_msg(description: &str, account: &Account) -> String { 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_certs(insecure)
.danger_accept_invalid_hostnames(insecure) .danger_accept_invalid_hostnames(insecure)
.build() .build()
.chain_err(|| format_err_msg("Could not create TLS connector", account))?; .context(format_err_msg("cannot create TLS connector", account))?;
debug!("create client"); debug!("create client");
let mut client_builder = imap::ClientBuilder::new(&account.imap_host, account.imap_port); 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 let client = client_builder
.connect(|domain, tcp| Ok(TlsConnector::connect(&ssl_conn, domain, tcp)?)) .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"); 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)
.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 }) Ok(Self { account, sess })
} }
@ -113,11 +103,11 @@ impl<'a> ImapConnector<'a> {
self.sess self.sess
.select(mbox) .select(mbox)
.chain_err(|| format!("Could not select mailbox `{}`", mbox))?; .context(format!("cannot select mailbox `{}`", mbox))?;
self.sess self.sess
.uid_store(uid_seq, format!("FLAGS ({})", flags)) .uid_store(uid_seq, format!("FLAGS ({})", flags))
.chain_err(|| format!("Could not set flags `{}`", &flags))?; .context(format!("cannot set flags `{}`", &flags))?;
Ok(()) Ok(())
} }
@ -147,11 +137,11 @@ impl<'a> ImapConnector<'a> {
self.sess self.sess
.select(mbox) .select(mbox)
.chain_err(|| format!("Could not select mailbox `{}`", mbox))?; .context(format!("cannot select mailbox `{}`", mbox))?;
self.sess self.sess
.uid_store(uid_seq, format!("+FLAGS ({})", flags)) .uid_store(uid_seq, format!("+FLAGS ({})", flags))
.chain_err(|| format!("Could not add flags `{}`", &flags))?; .context(format!("cannot add flags `{}`", &flags))?;
Ok(()) Ok(())
} }
@ -163,11 +153,11 @@ impl<'a> ImapConnector<'a> {
self.sess self.sess
.select(mbox) .select(mbox)
.chain_err(|| format!("Could not select mailbox `{}`", mbox))?; .context(format!("cannot select mailbox `{}`", mbox))?;
self.sess self.sess
.uid_store(uid_seq, format!("-FLAGS ({})", flags)) .uid_store(uid_seq, format!("-FLAGS ({})", flags))
.chain_err(|| format!("Could not remove flags `{}`", &flags))?; .context(format!("cannot remove flags `{}`", &flags))?;
Ok(()) Ok(())
} }
@ -176,7 +166,7 @@ impl<'a> ImapConnector<'a> {
let uids: Vec<u32> = self let uids: Vec<u32> = self
.sess .sess
.uid_search("NEW") .uid_search("NEW")
.chain_err(|| "Could not search new messages")? .context("cannot search new messages")?
.into_iter() .into_iter()
.collect(); .collect();
debug!("found {} new messages", uids.len()); debug!("found {} new messages", uids.len());
@ -189,7 +179,7 @@ impl<'a> ImapConnector<'a> {
debug!("examine mailbox: {}", &ctx.mbox); debug!("examine mailbox: {}", &ctx.mbox);
self.sess self.sess
.examine(&ctx.mbox) .examine(&ctx.mbox)
.chain_err(|| format!("Could not examine mailbox `{}`", &ctx.mbox))?; .context(format!("cannot examine mailbox `{}`", &ctx.mbox))?;
debug!("init messages hashset"); debug!("init messages hashset");
let mut msgs_set: HashSet<u32> = let mut msgs_set: HashSet<u32> =
@ -208,7 +198,7 @@ impl<'a> ImapConnector<'a> {
false false
}) })
}) })
.chain_err(|| "Could not start the idle mode")?; .context("cannot start the idle mode")?;
let uids: Vec<u32> = self let uids: Vec<u32> = self
.search_new_msgs()? .search_new_msgs()?
@ -227,12 +217,12 @@ impl<'a> ImapConnector<'a> {
let fetches = self let fetches = self
.sess .sess
.uid_fetch(uids, "(ENVELOPE)") .uid_fetch(uids, "(ENVELOPE)")
.chain_err(|| "Could not fetch new messages enveloppe")?; .context("cannot fetch new messages enveloppe")?;
for fetch in fetches.iter() { for fetch in fetches.iter() {
let msg = Msg::try_from(fetch)?; let msg = Msg::try_from(fetch)?;
let uid = fetch.uid.ok_or_else(|| { 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(); let subject = msg.headers.subject.clone().unwrap_or_default();
@ -255,7 +245,7 @@ impl<'a> ImapConnector<'a> {
debug!("examine mailbox: {}", &ctx.mbox); debug!("examine mailbox: {}", &ctx.mbox);
self.sess self.sess
.examine(&ctx.mbox) .examine(&ctx.mbox)
.chain_err(|| format!("Could not examine mailbox `{}`", &ctx.mbox))?; .context(format!("cannot examine mailbox `{}`", &ctx.mbox))?;
loop { loop {
debug!("begin loop"); debug!("begin loop");
@ -269,7 +259,7 @@ impl<'a> ImapConnector<'a> {
false false
}) })
}) })
.chain_err(|| "Could not start the idle mode")?; .context("cannot start the idle mode")?;
ctx.config.exec_watch_cmds(&ctx.account)?; ctx.config.exec_watch_cmds(&ctx.account)?;
debug!("end loop"); debug!("end loop");
} }
@ -279,7 +269,7 @@ impl<'a> ImapConnector<'a> {
let names = self let names = self
.sess .sess
.list(Some(""), Some("*")) .list(Some(""), Some("*"))
.chain_err(|| "Could not list mailboxes")?; .context("cannot list mailboxes")?;
Ok(names) Ok(names)
} }
@ -293,7 +283,7 @@ impl<'a> ImapConnector<'a> {
let last_seq = self let last_seq = self
.sess .sess
.select(mbox) .select(mbox)
.chain_err(|| format!("Could not select mailbox `{}`", mbox))? .context(format!("cannot select mailbox `{}`", mbox))?
.exists as i64; .exists as i64;
if last_seq == 0 { if last_seq == 0 {
@ -313,7 +303,7 @@ impl<'a> ImapConnector<'a> {
let fetches = self let fetches = self
.sess .sess
.fetch(range, "(UID FLAGS ENVELOPE INTERNALDATE)") .fetch(range, "(UID FLAGS ENVELOPE INTERNALDATE)")
.chain_err(|| "Could not fetch messages")?; .context("cannot fetch messages")?;
Ok(Some(fetches)) Ok(Some(fetches))
} }
@ -327,14 +317,17 @@ impl<'a> ImapConnector<'a> {
) -> Result<Option<imap::types::ZeroCopy<Vec<imap::types::Fetch>>>> { ) -> Result<Option<imap::types::ZeroCopy<Vec<imap::types::Fetch>>>> {
self.sess self.sess
.select(mbox) .select(mbox)
.chain_err(|| format!("Could not select mailbox `{}`", mbox))?; .context(format!("cannot select mailbox `{}`", mbox))?;
let begin = page * page_size; let begin = page * page_size;
let end = begin + (page_size - 1); let end = begin + (page_size - 1);
let uids: Vec<String> = self let uids: Vec<String> = self
.sess .sess
.search(query) .search(query)
.chain_err(|| format!("Could not search in `{}` with query `{}`", mbox, query))? .context(format!(
"cannot search in `{}` with query `{}`",
mbox, query
))?
.iter() .iter()
.map(|seq| seq.to_string()) .map(|seq| seq.to_string())
.collect(); .collect();
@ -347,7 +340,7 @@ impl<'a> ImapConnector<'a> {
let fetches = self let fetches = self
.sess .sess
.fetch(&range, "(UID FLAGS ENVELOPE INTERNALDATE)") .fetch(&range, "(UID FLAGS ENVELOPE INTERNALDATE)")
.chain_err(|| format!("Could not fetch range `{}`", &range))?; .context(format!("cannot fetch range `{}`", &range))?;
Ok(Some(fetches)) Ok(Some(fetches))
} }
@ -356,15 +349,15 @@ impl<'a> ImapConnector<'a> {
pub fn get_msg(&mut self, mbox: &str, uid: &str) -> Result<Msg> { pub fn get_msg(&mut self, mbox: &str, uid: &str) -> Result<Msg> {
self.sess self.sess
.select(mbox) .select(mbox)
.chain_err(|| format!("Could not select mailbox `{}`", mbox))?; .context(format!("cannot select mailbox `{}`", mbox))?;
match self match self
.sess .sess
.uid_fetch(uid, "(FLAGS BODY[] ENVELOPE INTERNALDATE)") .uid_fetch(uid, "(FLAGS BODY[] ENVELOPE INTERNALDATE)")
.chain_err(|| "Could not fetch bodies")? .context("cannot fetch bodies")?
.first() .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)?), Some(fetch) => Ok(Msg::try_from(fetch)?),
} }
} }
@ -378,7 +371,7 @@ impl<'a> ImapConnector<'a> {
.append(mbox, &body) .append(mbox, &body)
.flags(flags) .flags(flags)
.finish() .finish()
.chain_err(|| format!("Could not append message to `{}`", mbox))?; .context(format!("cannot append message to `{}`", mbox))?;
Ok(()) Ok(())
} }
@ -386,7 +379,7 @@ impl<'a> ImapConnector<'a> {
pub fn expunge(&mut self, mbox: &str) -> Result<()> { pub fn expunge(&mut self, mbox: &str) -> Result<()> {
self.sess self.sess
.expunge() .expunge()
.chain_err(|| format!("Could not expunge `{}`", mbox))?; .context(format!("cannot expunge `{}`", mbox))?;
Ok(()) Ok(())
} }

View file

@ -1,4 +1,4 @@
use error_chain::error_chain; use anyhow::{anyhow, Context, Result};
use log::{debug, error, trace}; use log::{debug, error, trace};
use std::{ use std::{
env, env,
@ -8,12 +8,6 @@ use std::{
process::Command, process::Command,
}; };
error_chain! {
foreign_links {
Utf8(std::string::FromUtf8Error);
}
}
fn draft_path() -> PathBuf { fn draft_path() -> PathBuf {
env::temp_dir().join("himalaya-draft.mail") env::temp_dir().join("himalaya-draft.mail")
} }
@ -25,7 +19,7 @@ pub fn remove_draft() -> Result<()> {
debug!("[input] draft path: {:?}", draft_path); debug!("[input] draft path: {:?}", draft_path);
fs::remove_file(&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> { 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 { Ok(choice) => match choice {
PreEditChoice::Edit => return open_editor_with_draft(), PreEditChoice::Edit => return open_editor_with_draft(),
PreEditChoice::Discard => break, PreEditChoice::Discard => break,
PreEditChoice::Quit => return Err("Edition aborted".into()), PreEditChoice::Quit => return Err(anyhow!("Edition aborted")),
}, },
Err(err) => error!("{}", err), Err(err) => error!("{}", err),
} }
@ -51,22 +45,22 @@ pub fn open_editor_with_tpl(tpl: &[u8]) -> Result<String> {
debug!("[Input] create draft"); debug!("[Input] create draft");
File::create(&draft_path) 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) .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"); 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) .arg(&draft_path)
.status() .status()
.chain_err(|| "Could not launch editor")?; .with_context(|| "cannot launch editor")?;
debug!("[Input] read draft"); debug!("[Input] read draft");
let mut draft = String::new(); let mut draft = String::new();
File::open(&draft_path) 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) .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) Ok(draft)
} }
@ -78,17 +72,17 @@ pub fn open_editor_with_draft() -> Result<String> {
debug!("[input] draft path: {:?}", draft_path); debug!("[input] draft path: {:?}", draft_path);
// Opens editor and saves user input to draft file // 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) .arg(&draft_path)
.status() .status()
.chain_err(|| "Could not launch editor")?; .with_context(|| "cannot launch editor")?;
// Extracts draft file content // Extracts draft file content
let mut draft = String::new(); let mut draft = String::new();
File::open(&draft_path) 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) .read_to_string(&mut draft)
.chain_err(|| format!("Could not read file {:?}", draft_path))?; .with_context(|| format!("cannot read file {:?}", draft_path))?;
Ok(draft) Ok(draft)
} }
@ -106,12 +100,12 @@ pub fn pre_edit_choice() -> Result<PreEditChoice> {
print!("(e)dit, (d)iscard or (q)uit? "); print!("(e)dit, (d)iscard or (q)uit? ");
io::stdout() io::stdout()
.flush() .flush()
.chain_err(|| "Could not flush stdout")?; .with_context(|| "cannot flush stdout")?;
let mut buf = String::new(); let mut buf = String::new();
io::stdin() io::stdin()
.read_line(&mut buf) .read_line(&mut buf)
.chain_err(|| "Could not read stdin")?; .with_context(|| "cannot read stdin")?;
match buf.bytes().next().map(|bytes| bytes as char) { match buf.bytes().next().map(|bytes| bytes as char) {
Some('e') => { Some('e') => {
@ -128,11 +122,11 @@ pub fn pre_edit_choice() -> Result<PreEditChoice> {
} }
Some(choice) => { Some(choice) => {
debug!("[input] pre edit choice: invalid choice {}", choice); debug!("[input] pre edit choice: invalid choice {}", choice);
Err(format!("Invalid choice `{}`", choice).into()) Err(anyhow!(format!("Invalid choice `{}`", choice)))
} }
None => { None => {
debug!("[input] pre edit choice: empty choice"); 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? "); print!("(s)end, (e)dit, (l)ocal/(r)emote draft or (d)iscard? ");
io::stdout() io::stdout()
.flush() .flush()
.chain_err(|| "Could not flush stdout")?; .with_context(|| "cannot flush stdout")?;
let mut buf = String::new(); let mut buf = String::new();
io::stdin() io::stdin()
.read_line(&mut buf) .read_line(&mut buf)
.chain_err(|| "Could not read stdin")?; .with_context(|| "cannot read stdin")?;
match buf.bytes().next().map(|bytes| bytes as char) { match buf.bytes().next().map(|bytes| bytes as char) {
Some('s') => Ok(PostEditChoice::Send), Some('s') => Ok(PostEditChoice::Send),
@ -162,7 +156,7 @@ pub fn post_edit_choice() -> Result<PostEditChoice> {
Some('r') => Ok(PostEditChoice::RemoteDraft), Some('r') => Ok(PostEditChoice::RemoteDraft),
Some('e') => Ok(PostEditChoice::Edit), Some('e') => Ok(PostEditChoice::Edit),
Some('d') => Ok(PostEditChoice::Discard), Some('d') => Ok(PostEditChoice::Discard),
Some(choice) => Err(format!("Invalid choice `{}`", choice).into()), Some(choice) => Err(anyhow!(format!("Invalid choice `{}`", choice))),
None => Err("Empty choice".into()), None => Err(anyhow!("Empty choice")),
} }
} }

View file

@ -1,8 +1,8 @@
use anyhow::Result;
use clap::{self, ArgMatches}; use clap::{self, ArgMatches};
use env_logger; use env_logger;
use error_chain::error_chain; use log::{debug, trace};
use log::{debug, error, trace}; use std::{env, path::PathBuf};
use std::{env, path::PathBuf, process::exit};
use url::{self, Url}; use url::{self, Url};
use himalaya::{ use himalaya::{
@ -14,20 +14,6 @@ use himalaya::{
output::{cli::output_args, model::Output}, 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> { fn parse_args<'a>() -> clap::App<'a, 'a> {
clap::App::new(env!("CARGO_PKG_NAME")) clap::App::new(env!("CARGO_PKG_NAME"))
.version(env!("CARGO_PKG_VERSION")) .version(env!("CARGO_PKG_VERSION"))
@ -44,7 +30,7 @@ fn parse_args<'a>() -> clap::App<'a, 'a> {
.subcommands(comp::cli::subcmds()) .subcommands(comp::cli::subcmds())
} }
fn run() -> Result<()> { fn main() -> Result<()> {
env_logger::init_from_env( env_logger::init_from_env(
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "off"), env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "off"),
); );
@ -99,26 +85,3 @@ fn run() -> Result<()> {
Ok(()) 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);
}
}

View file

@ -1,16 +1,9 @@
use anyhow::Result;
use clap; use clap;
use error_chain::error_chain;
use log::{debug, trace}; use log::{debug, trace};
use crate::{ctx::Ctx, imap::model::ImapConnector, mbox::model::Mboxes}; 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>> { pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
vec![clap::SubCommand::with_name("mailboxes") vec![clap::SubCommand::with_name("mailboxes")
.aliases(&["mailbox", "mboxes", "mbox", "m"]) .aliases(&["mailbox", "mboxes", "mbox", "m"])

View file

@ -1,23 +1,9 @@
use anyhow::{Error, Result};
use lettre::message::header::ContentType; use lettre::message::header::ContentType;
use mailparse::{DispositionType, ParsedMail}; use mailparse::{DispositionType, ParsedMail};
use std::convert::TryFrom;
use std::fs;
use std::path::Path;
use serde::Serialize; 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. /// This struct represents an attachment.
#[derive(Debug, Serialize, Clone, PartialEq, Eq)] #[derive(Debug, Serialize, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]

View file

@ -1,17 +1,6 @@
use error_chain::error_chain; use serde::Serialize;
use std::fmt; 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: /// This struct represents the body/content of a msg. For example:
/// ///
/// ```text /// ```text

View file

@ -1,14 +1,9 @@
use super::body::Body; use anyhow::{anyhow, Context, Result};
use super::headers::Headers;
use super::model::{Msg, MsgSerialized, Msgs};
use url::Url;
use atty::Stream; use atty::Stream;
use clap; use clap;
use error_chain::error_chain; use imap::types::Flag;
use lettre::message::header::ContentTransferEncoding; use lettre::message::header::ContentTransferEncoding;
use log::{debug, error, trace}; use log::{debug, error, trace};
use std::{ use std::{
borrow::Cow, borrow::Cow,
collections::HashMap, collections::HashMap,
@ -16,26 +11,18 @@ use std::{
fs, fs,
io::{self, BufRead}, io::{self, BufRead},
}; };
use url::Url;
use imap::types::Flag; use super::{
body::Body,
headers::Headers,
model::{Msg, MsgSerialized, Msgs},
};
use crate::{ use crate::{
ctx::Ctx, flag::model::Flags, imap::model::ImapConnector, input, mbox::cli::mbox_target_arg, ctx::Ctx, flag::model::Flags, imap::model::ImapConnector, input, mbox::cli::mbox_target_arg,
smtp, 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>> { pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
vec![ vec![
clap::SubCommand::with_name("list") clap::SubCommand::with_name("list")
@ -384,7 +371,7 @@ fn msg_matches_attachments(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool
debug!("downloading {}…", &attachment.filename); debug!("downloading {}…", &attachment.filename);
fs::write(&filepath, &attachment.body_raw) fs::write(&filepath, &attachment.body_raw)
.chain_err(|| format!("Could not save attachment {:?}", filepath))?; .with_context(|| format!("cannot save attachment {:?}", filepath))?;
} }
debug!( 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), ("forward", Some(matches)) => tpl_matches_forward(ctx, matches),
// TODO: find a way to show the help message for template subcommand // 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"); ctx.output.print("Message successfully saved to Drafts");
} }
Err(err) => { 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()); return Err(err.into());
} }
}; };

View file

@ -1,32 +1,10 @@
use std::borrow::Cow; use anyhow::{anyhow, Error, Result};
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 lettre::message::header::ContentTransferEncoding; 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] /// 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 /// 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 /// 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())? { let from = match convert_vec_address_to_string(envelope.from.as_ref())? {
Some(from) => from, 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 // 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 reply_to = convert_vec_address_to_string(envelope.reply_to.as_ref())?;
let to = match convert_vec_address_to_string(envelope.to.as_ref())? { let to = match convert_vec_address_to_string(envelope.to.as_ref())? {
Some(to) => to, 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 cc = convert_vec_address_to_string(envelope.cc.as_ref())?;
let bcc = convert_vec_address_to_string(envelope.bcc.as_ref())?; let bcc = convert_vec_address_to_string(envelope.bcc.as_ref())?;

View file

@ -1,13 +1,9 @@
use super::attachment::Attachment; use anyhow::{anyhow, Context, Error, Result};
use super::body::Body;
use super::headers::Headers;
use log::debug;
use imap::types::{Fetch, Flag, ZeroCopy}; use imap::types::{Fetch, Flag, ZeroCopy};
use log::debug;
use mailparse; use mailparse;
use super::{attachment::Attachment, body::Body, headers::Headers};
use crate::{ use crate::{
ctx::Ctx, ctx::Ctx,
flag::model::Flags, flag::model::Flags,
@ -29,46 +25,6 @@ use std::{
fmt, 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. /// 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 /// This struct-type makes it also possible to print the msg in a serialized form or in a normal
/// form. /// form.
@ -528,13 +484,13 @@ impl Msg {
/// ``` /// ```
pub fn parse_from_str(&mut self, content: &str) -> Result<()> { pub fn parse_from_str(&mut self, content: &str) -> Result<()> {
let parsed = mailparse::parse_mail(content.as_bytes()) 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); self.headers = Headers::from(&parsed);
match parsed.get_body() { match parsed.get_body() {
Ok(body) => self.body = Body::new_with_text(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(()) Ok(())
@ -623,76 +579,72 @@ impl Msg {
// -- Must-have-fields -- // -- Must-have-fields --
// add "from" // add "from"
for mailaddress in &self.headers.from { for mailaddress in &self.headers.from {
msg = msg.from(match mailaddress.parse() { msg = msg.from(
Ok(from) => from, match mailaddress
Err(err) => { .parse()
return Err( .with_context(|| "cannot parse `From` header")
ErrorKind::Header(err.to_string(), "From", mailaddress.to_string()).into(), {
) Ok(from) => from,
} Err(err) => return Err(anyhow!(err.to_string())),
}); },
);
} }
// add "to" // add "to"
for mailaddress in &self.headers.to { for mailaddress in &self.headers.to {
msg = msg.to(match mailaddress.parse() { msg = msg.to(
Ok(to) => to, match mailaddress
Err(err) => { .parse()
return Err( .with_context(|| "cannot parse `To` header")
ErrorKind::Header(err.to_string(), "To", mailaddress.to_string()).into(), {
) Ok(from) => from,
} Err(err) => return Err(anyhow!(err.to_string())),
}); },
);
} }
// -- Optional fields -- // -- Optional fields --
// add "bcc" // add "bcc"
if let Some(bcc) = &self.headers.bcc { if let Some(bcc) = &self.headers.bcc {
for mailaddress in bcc { for mailaddress in bcc {
msg = msg.bcc(match mailaddress.parse() { msg = msg.bcc(
Ok(bcc) => bcc, match mailaddress
Err(err) => { .parse()
return Err(ErrorKind::Header( .with_context(|| "cannot parse `Bcc` header")
err.to_string(), {
"Bcc", Ok(from) => from,
mailaddress.to_string(), Err(err) => return Err(anyhow!(err.to_string())),
) },
.into()) );
}
});
} }
} }
// add "cc" // add "cc"
if let Some(cc) = &self.headers.cc { if let Some(cc) = &self.headers.cc {
for mailaddress in cc { for mailaddress in cc {
msg = msg.cc(match mailaddress.parse() { msg = msg.cc(
Ok(cc) => cc, match mailaddress
Err(err) => { .parse()
return Err(ErrorKind::Header( .with_context(|| "cannot parse `Cc` header")
err.to_string(), {
"Cc", Ok(from) => from,
mailaddress.to_string(), Err(err) => return Err(anyhow!(err.to_string())),
) },
.into()) );
}
});
} }
} }
// add "in_reply_to" // add "in_reply_to"
if let Some(in_reply_to) = &self.headers.in_reply_to { if let Some(in_reply_to) = &self.headers.in_reply_to {
msg = msg.in_reply_to(match in_reply_to.parse() { msg = msg.in_reply_to(
Ok(in_reply_to) => in_reply_to, match in_reply_to
Err(err) => { .parse()
return Err(ErrorKind::Header( .with_context(|| "cannot parse `In-Reply-To` header")
err.to_string(), {
"In-Reply-To", Ok(from) => from,
in_reply_to.to_string(), Err(err) => return Err(anyhow!(err.to_string())),
) },
.into()) );
}
});
} }
// add message-id if it exists // add message-id if it exists
@ -713,30 +665,29 @@ impl Msg {
// add "reply-to" // add "reply-to"
if let Some(reply_to) = &self.headers.reply_to { if let Some(reply_to) = &self.headers.reply_to {
for mailaddress in reply_to { for mailaddress in reply_to {
msg = msg.reply_to(match mailaddress.parse() { msg = msg.reply_to(
Ok(reply_to) => reply_to, match mailaddress
Err(err) => { .parse()
return Err(ErrorKind::Header( .with_context(|| "cannot parse `Reply-To` header")
err.to_string(), {
"Reply-to", Ok(from) => from,
mailaddress.to_string(), Err(err) => return Err(anyhow!(err.to_string())),
) },
.into()) );
}
});
} }
} }
// add "sender" // add "sender"
if let Some(sender) = &self.headers.sender { if let Some(sender) = &self.headers.sender {
msg = msg.sender(match sender.parse() { msg = msg.sender(
Ok(sender) => sender, match sender
Err(err) => { .parse()
return Err( .with_context(|| "cannot parse `Sender` header")
ErrorKind::Header(err.to_string(), "Sender", sender.to_string()).into(), {
) Ok(from) => from,
} Err(err) => return Err(anyhow!(err.to_string())),
}); },
);
} }
// add subject // add subject
@ -779,7 +730,7 @@ impl Msg {
.multipart(msg_parts) .multipart(msg_parts)
// whenever an error appears, print out the messge as well to see what might be the // whenever an error appears, print out the messge as well to see what might be the
// error // error
.chain_err(|| format!("-- Current Message --\n{}", self))?) .context(format!("-- Current Message --\n{}", self))?)
} }
/// Returns the uid of the msg. /// 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. /// Returns the raw mail as a string instead of a Vector of bytes.
pub fn get_raw_as_string(&self) -> Result<String> { pub fn get_raw_as_string(&self) -> Result<String> {
let raw_message = String::from_utf8(self.raw.clone()).chain_err(|| { let raw_message = String::from_utf8(self.raw.clone())
format!( .context(format!("cannot parse raw message as string"))?;
"[{}]: Couldn't parse the raw message as string.",
"Error".red()
)
})?;
Ok(raw_message) Ok(raw_message)
} }
@ -974,8 +921,7 @@ impl TryFrom<&Fetch> for Msg {
// the body of the mail but something else. Log that! // the body of the mail but something else. Log that!
else { else {
println!( println!(
"[{}] Unknown attachment with the following mime-type: {}\n", "Unknown attachment with the following mime-type: {}",
"Warning".yellow(),
subpart.ctype.mimetype, subpart.ctype.mimetype,
); );
} }
@ -1219,7 +1165,7 @@ mod tests {
// -- for general tests -- // -- for general tests --
let ctx = Ctx { let ctx = Ctx {
account: Account::new(Some("Name"), "some@address.asdf"), account: Account::new(Some("Name"), "some@address.asdf"),
config: config, config,
mbox: String::from("INBOX"), mbox: String::from("INBOX"),
..Ctx::default() ..Ctx::default()
}; };

View file

@ -1,14 +1,7 @@
use error_chain::error_chain; use anyhow::Result;
use serde::ser::{self, SerializeStruct}; use serde::ser::{self, SerializeStruct};
use std::{fmt, process::Command, result}; use std::{fmt, process::Command, result};
error_chain! {
foreign_links {
Utf8(std::string::FromUtf8Error);
Io(std::io::Error);
}
}
pub struct Info(pub String); pub struct Info(pub String);
impl fmt::Display for Info { impl fmt::Display for Info {

View file

@ -1,4 +1,4 @@
use error_chain::error_chain; use anyhow::Result;
use lettre::{ use lettre::{
self, self,
transport::{smtp::client::Tls, smtp::client::TlsParameters, smtp::SmtpTransport}, transport::{smtp::client::Tls, smtp::client::TlsParameters, smtp::SmtpTransport},
@ -7,15 +7,6 @@ use lettre::{
use crate::config::model::Account; 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<()> { pub fn send(account: &Account, msg: &lettre::Message) -> Result<()> {
let smtp_relay = if account.smtp_starttls() { let smtp_relay = if account.smtp_starttls() {
SmtpTransport::starttls_relay SmtpTransport::starttls_relay