Files
lettre/src/message/header/mailbox.rs
2020-09-09 09:46:53 +02:00

290 lines
7.1 KiB
Rust

use crate::message::{
mailbox::{Mailbox, Mailboxes},
utf8_b,
};
use hyperx::{
header::{Formatter as HeaderFormatter, Header, RawLike},
Error as HeaderError, Result as HyperResult,
};
use std::{fmt::Result as FmtResult, slice::Iter, str::from_utf8};
/// Header which can contains multiple mailboxes
pub trait MailboxesHeader {
fn join_mailboxes(&mut self, other: Self);
}
macro_rules! mailbox_header {
($(#[$doc:meta])*($type_name: ident, $header_name: expr)) => {
$(#[$doc])*
#[derive(Debug, Clone, PartialEq)]
pub struct $type_name(pub Mailbox);
impl Header for $type_name {
fn header_name() -> &'static str {
$header_name
}
fn parse_header<'a, T>(raw: &'a T) -> HyperResult<Self> where
T: RawLike<'a>,
Self: Sized {
raw.one()
.ok_or(HeaderError::Header)
.and_then(parse_mailboxes)
.and_then(|mbs| {
mbs.into_single().ok_or(HeaderError::Header)
}).map($type_name)
}
fn fmt_header(&self, f: &mut HeaderFormatter) -> FmtResult {
f.fmt_line(&self.0.recode_name(utf8_b::encode))
}
}
};
}
macro_rules! mailboxes_header {
($(#[$doc:meta])*($type_name: ident, $header_name: expr)) => {
$(#[$doc])*
#[derive(Debug, Clone, PartialEq)]
pub struct $type_name(pub Mailboxes);
impl MailboxesHeader for $type_name {
fn join_mailboxes(&mut self, other: Self) {
self.0.extend(other.0);
}
}
impl Header for $type_name {
fn header_name() -> &'static str {
$header_name
}
fn parse_header<'a, T>(raw: &'a T) -> HyperResult<$type_name>
where
T: RawLike<'a>,
Self: Sized,
{
raw.one()
.ok_or(HeaderError::Header)
.and_then(parse_mailboxes)
.map($type_name)
}
fn fmt_header(&self, f: &mut HeaderFormatter) -> FmtResult {
format_mailboxes(self.0.iter(), f)
}
}
};
}
mailbox_header! {
/**
`Sender` header
This header contains [`Mailbox`][self::Mailbox] associated with sender.
```no_test
header::Sender("Mr. Sender <sender@example.com>".parse().unwrap())
```
*/
(Sender, "Sender")
}
mailboxes_header! {
/**
`From` header
This header contains [`Mailboxes`][self::Mailboxes].
*/
(From, "From")
}
mailboxes_header! {
/**
`Reply-To` header
This header contains [`Mailboxes`][self::Mailboxes].
*/
(ReplyTo, "Reply-To")
}
mailboxes_header! {
/**
`To` header
This header contains [`Mailboxes`][self::Mailboxes].
*/
(To, "To")
}
mailboxes_header! {
/**
`Cc` header
This header contains [`Mailboxes`][self::Mailboxes].
*/
(Cc, "Cc")
}
mailboxes_header! {
/**
`Bcc` header
This header contains [`Mailboxes`][self::Mailboxes].
*/
(Bcc, "Bcc")
}
fn parse_mailboxes(raw: &[u8]) -> HyperResult<Mailboxes> {
if let Ok(src) = from_utf8(raw) {
if let Ok(mbs) = src.parse() {
return Ok(mbs);
}
}
Err(HeaderError::Header)
}
fn format_mailboxes<'a>(mbs: Iter<'a, Mailbox>, f: &mut HeaderFormatter) -> FmtResult {
f.fmt_line(&Mailboxes::from(
mbs.map(|mb| mb.recode_name(utf8_b::encode))
.collect::<Vec<_>>(),
))
}
#[cfg(test)]
mod test {
use super::{From, Mailbox, Mailboxes};
use hyperx::header::Headers;
#[test]
fn format_single_without_name() {
let from = Mailboxes::new().with("kayo@example.com".parse().unwrap());
let mut headers = Headers::new();
headers.set(From(from));
assert_eq!(format!("{}", headers), "From: kayo@example.com\r\n");
}
#[test]
fn format_single_with_name() {
let from = Mailboxes::new().with("K. <kayo@example.com>".parse().unwrap());
let mut headers = Headers::new();
headers.set(From(from));
assert_eq!(format!("{}", headers), "From: K. <kayo@example.com>\r\n");
}
#[test]
fn format_multi_without_name() {
let from = Mailboxes::new()
.with("kayo@example.com".parse().unwrap())
.with("pony@domain.tld".parse().unwrap());
let mut headers = Headers::new();
headers.set(From(from));
assert_eq!(
format!("{}", headers),
"From: kayo@example.com, pony@domain.tld\r\n"
);
}
#[test]
fn format_multi_with_name() {
let from = vec![
"K. <kayo@example.com>".parse().unwrap(),
"Pony P. <pony@domain.tld>".parse().unwrap(),
];
let mut headers = Headers::new();
headers.set(From(from.into()));
assert_eq!(
format!("{}", headers),
"From: K. <kayo@example.com>, Pony P. <pony@domain.tld>\r\n"
);
}
#[test]
fn format_single_with_utf8_name() {
let from = vec!["Кайо <kayo@example.com>".parse().unwrap()];
let mut headers = Headers::new();
headers.set(From(from.into()));
assert_eq!(
format!("{}", headers),
"From: =?utf-8?b?0JrQsNC50L4=?= <kayo@example.com>\r\n"
);
}
#[test]
fn parse_single_without_name() {
let from = vec!["kayo@example.com".parse().unwrap()].into();
let mut headers = Headers::new();
headers.set_raw("From", "kayo@example.com");
assert_eq!(headers.get::<From>(), Some(&From(from)));
}
#[test]
fn parse_single_with_name() {
let from = vec!["K. <kayo@example.com>".parse().unwrap()].into();
let mut headers = Headers::new();
headers.set_raw("From", "K. <kayo@example.com>");
assert_eq!(headers.get::<From>(), Some(&From(from)));
}
#[test]
fn parse_multi_without_name() {
let from: Vec<Mailbox> = vec![
"kayo@example.com".parse().unwrap(),
"pony@domain.tld".parse().unwrap(),
];
let mut headers = Headers::new();
headers.set_raw("From", "kayo@example.com, pony@domain.tld");
assert_eq!(headers.get::<From>(), Some(&From(from.into())));
}
#[test]
fn parse_multi_with_name() {
let from: Vec<Mailbox> = vec![
"K. <kayo@example.com>".parse().unwrap(),
"Pony P. <pony@domain.tld>".parse().unwrap(),
];
let mut headers = Headers::new();
headers.set_raw("From", "K. <kayo@example.com>, Pony P. <pony@domain.tld>");
assert_eq!(headers.get::<From>(), Some(&From(from.into())));
}
#[test]
fn parse_single_with_utf8_name() {
let from: Vec<Mailbox> = vec!["Кайо <kayo@example.com>".parse().unwrap()];
let mut headers = Headers::new();
headers.set_raw("From", "=?utf-8?b?0JrQsNC50L4=?= <kayo@example.com>");
assert_eq!(headers.get::<From>(), Some(&From(from.into())));
}
}