introduce read_headers in account config (#338)

This commit is contained in:
Clément DOUIN 2022-03-12 15:25:35 +01:00
parent d3968461e2
commit f9bed5f3c2
No known key found for this signature in database
GPG key ID: 353E4A18EE0FAB72
6 changed files with 82 additions and 29 deletions

View file

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- SMTP pre-send hook [#178]
- Customize headers to show at the top of a read message [#338]
### Changed
@ -503,3 +504,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#329]: https://github.com/soywod/himalaya/issues/329
[#331]: https://github.com/soywod/himalaya/issues/331
[#335]: https://github.com/soywod/himalaya/issues/335
[#338]: https://github.com/soywod/himalaya/issues/338

View file

@ -32,6 +32,9 @@ pub struct AccountConfig {
/// Represents the text/plain format as defined in the
/// [RFC2646](https://www.ietf.org/rfc/rfc2646.txt)
pub format: Format,
/// Overrides the default headers displayed at the top of
/// the read message.
pub read_headers: Vec<String>,
/// Represents mailbox aliases.
pub mailboxes: HashMap<String, String>,
@ -157,6 +160,7 @@ impl<'a> AccountConfig {
.unwrap_or(&vec![])
.to_owned(),
format: base_account.format.unwrap_or_default(),
read_headers: base_account.read_headers,
mailboxes: base_account.mailboxes.clone(),
hooks: base_account.hooks.unwrap_or_default(),
default: base_account.default.unwrap_or_default(),

View file

@ -45,7 +45,7 @@ macro_rules! make_account_config {
pub signature: Option<String>,
/// Overrides the signature delimiter for this account.
pub signature_delimiter: Option<String>,
/// Overrides the default page size for this account.
/// Overrides the default page size for this account.
pub default_page_size: Option<usize>,
/// Overrides the notify command for this account.
pub notify_cmd: Option<String>,
@ -56,6 +56,10 @@ macro_rules! make_account_config {
/// Represents the text/plain format as defined in the
/// [RFC2646](https://www.ietf.org/rfc/rfc2646.txt)
pub format: Option<Format>,
/// Overrides the default headers displayed at the top of
/// the read message.
#[serde(default)]
pub read_headers: Vec<String>,
/// Makes this account the default one.
pub default: Option<bool>,
@ -102,6 +106,7 @@ macro_rules! make_account_config {
notify_query: self.notify_query.clone(),
watch_cmds: self.watch_cmds.clone(),
format: self.format.clone(),
read_headers: self.read_headers.clone(),
default: self.default.clone(),
email: self.email.clone(),

View file

@ -222,7 +222,16 @@ fn main() -> Result<()> {
return msg_handlers::move_(seq, mbox, mbox_dst, &mut printer, backend);
}
Some(msg_args::Cmd::Read(seq, text_mime, raw, headers)) => {
return msg_handlers::read(seq, text_mime, raw, headers, mbox, &mut printer, backend);
return msg_handlers::read(
seq,
text_mime,
raw,
headers,
mbox,
&account_config,
&mut printer,
backend,
);
}
Some(msg_args::Cmd::Reply(seq, all, attachment_paths, encrypt)) => {
return msg_handlers::reply(

View file

@ -729,11 +729,29 @@ impl Msg {
/// message is like a template, except that:
/// - headers part is customizable (can be omitted if empty filter given in argument)
/// - body type is customizable (plain or html)
pub fn to_readable_string(&self, text_mime: &str, headers: Vec<&str>) -> Result<String> {
let mut readable_msg = String::new();
pub fn to_readable_string(
&self,
text_mime: &str,
headers: Vec<&str>,
config: &AccountConfig,
) -> Result<String> {
let mut all_headers = vec![];
for h in config.read_headers.iter() {
let h = h.to_lowercase();
if !all_headers.contains(&h) {
all_headers.push(h)
}
}
for h in headers.iter() {
let h = h.to_lowercase();
if !all_headers.contains(&h) {
all_headers.push(h)
}
}
for h in headers {
match h.to_lowercase().as_str() {
let mut readable_msg = String::new();
for h in all_headers {
match h.as_str() {
"message-id" => match self.message_id {
Some(ref message_id) if !message_id.is_empty() => {
readable_msg.push_str(&format!("Message-Id: {}\n", message_id));
@ -833,9 +851,8 @@ impl TryInto<lettre::address::Envelope> for &Msg {
#[cfg(test)]
mod tests {
use std::iter::FromIterator;
use mailparse::SingleInfo;
use std::iter::FromIterator;
use crate::msg::Addr;
@ -982,6 +999,7 @@ mod tests {
#[test]
fn test_to_readable() {
let config = AccountConfig::default();
let msg = Msg {
parts: Parts(vec![Part::TextPlain(TextPlainPart {
content: String::from("hello, world!"),
@ -989,21 +1007,22 @@ mod tests {
..Msg::default()
};
// empty msg, empty headers
// empty msg headers, empty headers, empty config
assert_eq!(
"hello, world!",
msg.to_readable_string("plain", vec![]).unwrap()
msg.to_readable_string("plain", vec![], &config).unwrap()
);
// empty msg, basic headers
// empty msg headers, basic headers
assert_eq!(
"hello, world!",
msg.to_readable_string("plain", vec!["from", "date", "custom-header"])
msg.to_readable_string("plain", vec!["From", "DATE", "custom-hEader"], &config)
.unwrap()
);
// empty msg, subject header
// empty msg headers, multiple subject headers
assert_eq!(
"Subject: \n\nhello, world!",
msg.to_readable_string("plain", vec!["subject"]).unwrap()
msg.to_readable_string("plain", vec!["subject", "Subject", "SUBJECT"], &config)
.unwrap()
);
let msg = Msg {
@ -1023,26 +1042,39 @@ mod tests {
..Msg::default()
};
// header present in msg headers
// header present in msg headers, empty config
assert_eq!(
"From: \"Test\" <test@local>\n\nhello, world!",
msg.to_readable_string("plain", vec!["from"]).unwrap()
);
// header present but empty in msg headers
assert_eq!(
"hello, world!",
msg.to_readable_string("plain", vec!["cc"]).unwrap()
);
// custom header present in msg headers
assert_eq!(
"Custom-Header: custom value\n\nhello, world!",
msg.to_readable_string("plain", vec!["custom-header"])
msg.to_readable_string("plain", vec!["from"], &config)
.unwrap()
);
// custom header present in msg headers (case insensitivity)
// header present but empty in msg headers, empty config
assert_eq!(
"hello, world!",
msg.to_readable_string("plain", vec!["cc"], &config)
.unwrap()
);
// multiple same custom headers present in msg headers, empty
// config
assert_eq!(
"Custom-Header: custom value\n\nhello, world!",
msg.to_readable_string("plain", vec!["CUSTOM-hEaDer"])
msg.to_readable_string("plain", vec!["custom-header", "cuSTom-HeaDer"], &config)
.unwrap()
);
let config = AccountConfig {
read_headers: vec![
"CusTOM-heaDER".into(),
"Subject".into(),
"from".into(),
"cc".into(),
],
..AccountConfig::default()
};
// header present but empty in msg headers, empty config
assert_eq!(
"Custom-Header: custom value\nSubject: \nFrom: \"Test\" <test@local>\nMessage-Id: <message-id>\n\nhello, world!",
msg.to_readable_string("plain", vec!["cc", "message-ID"], &config)
.unwrap()
);
}

View file

@ -209,6 +209,7 @@ pub fn read<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
raw: bool,
headers: Vec<&str>,
mbox: &str,
config: &AccountConfig,
printer: &mut P,
backend: Box<&'a mut B>,
) -> Result<()> {
@ -218,7 +219,7 @@ pub fn read<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
// Emails don't always have valid utf8. Using "lossy" to display what we can.
String::from_utf8_lossy(&msg.raw).into_owned()
} else {
msg.to_readable_string(text_mime, headers)?
msg.to_readable_string(text_mime, headers, config)?
})
}