melib/email.rs: use SmallVec for Address fields
parent
52cec59215
commit
32b4c30fee
|
@ -29,7 +29,7 @@ use nom::{
|
||||||
character::complete::digit1,
|
character::complete::digit1,
|
||||||
character::is_digit,
|
character::is_digit,
|
||||||
combinator::{map, map_res, opt},
|
combinator::{map, map_res, opt},
|
||||||
multi::{length_data, many0, many1, separated_list, separated_nonempty_list},
|
multi::{fold_many1, length_data, many0, separated_list, separated_nonempty_list},
|
||||||
sequence::{delimited, preceded},
|
sequence::{delimited, preceded},
|
||||||
};
|
};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
@ -1179,7 +1179,7 @@ pub fn envelope(input: &[u8]) -> IResult<&[u8], Envelope> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(bcc) = bcc {
|
if let Some(bcc) = bcc {
|
||||||
env.set_bcc(bcc);
|
env.set_bcc(bcc.to_vec());
|
||||||
}
|
}
|
||||||
if let Some(in_reply_to) = in_reply_to {
|
if let Some(in_reply_to) = in_reply_to {
|
||||||
env.set_in_reply_to(&in_reply_to);
|
env.set_in_reply_to(&in_reply_to);
|
||||||
|
@ -1268,13 +1268,21 @@ macro_rules! str_builder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse a list of addresses in the format of the ENVELOPE structure
|
// Parse a list of addresses in the format of the ENVELOPE structure
|
||||||
pub fn envelope_addresses<'a>(input: &'a [u8]) -> IResult<&'a [u8], Option<Vec<Address>>> {
|
pub fn envelope_addresses<'a>(
|
||||||
|
input: &'a [u8],
|
||||||
|
) -> IResult<&'a [u8], Option<SmallVec<[Address; 1]>>> {
|
||||||
alt((
|
alt((
|
||||||
map(tag("NIL"), |_| None),
|
map(tag("NIL"), |_| None),
|
||||||
|input: &'a [u8]| -> IResult<&'a [u8], Option<Vec<Address>>> {
|
|input: &'a [u8]| -> IResult<&'a [u8], Option<SmallVec<[Address; 1]>>> {
|
||||||
let (input, _) = tag("(")(input)?;
|
let (input, _) = tag("(")(input)?;
|
||||||
let (input, envelopes) =
|
let (input, envelopes) = fold_many1(
|
||||||
many1(delimited(tag("("), envelope_address, tag(")")))(input.ltrim())?;
|
delimited(tag("("), envelope_address, tag(")")),
|
||||||
|
SmallVec::new(),
|
||||||
|
|mut acc, item| {
|
||||||
|
acc.push(item);
|
||||||
|
acc
|
||||||
|
},
|
||||||
|
)(input.ltrim())?;
|
||||||
let (input, _) = tag(")")(input)?;
|
let (input, _) = tag(")")(input)?;
|
||||||
Ok((input, Some(envelopes)))
|
Ok((input, Some(envelopes)))
|
||||||
},
|
},
|
||||||
|
|
|
@ -141,17 +141,17 @@ pub struct EmailObject {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
message_id: Vec<String>,
|
message_id: Vec<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
to: Vec<EmailAddress>,
|
to: SmallVec<[EmailAddress; 1]>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
bcc: Option<Vec<EmailAddress>>,
|
bcc: Option<Vec<EmailAddress>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
reply_to: Option<Vec<EmailAddress>>,
|
reply_to: Option<Vec<EmailAddress>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
cc: Option<Vec<EmailAddress>>,
|
cc: Option<SmallVec<[EmailAddress; 1]>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
sender: Option<Vec<EmailAddress>>,
|
sender: Option<Vec<EmailAddress>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
from: Vec<EmailAddress>,
|
from: SmallVec<[EmailAddress; 1]>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
in_reply_to: Option<Vec<String>>,
|
in_reply_to: Option<Vec<String>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -269,24 +269,24 @@ impl std::convert::From<EmailObject> for crate::Envelope {
|
||||||
}
|
}
|
||||||
|
|
||||||
env.set_from(
|
env.set_from(
|
||||||
std::mem::replace(&mut t.from, Vec::new())
|
std::mem::replace(&mut t.from, SmallVec::new())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|addr| addr.into())
|
.map(|addr| addr.into())
|
||||||
.collect::<Vec<crate::email::Address>>(),
|
.collect::<SmallVec<[crate::email::Address; 1]>>(),
|
||||||
);
|
);
|
||||||
env.set_to(
|
env.set_to(
|
||||||
std::mem::replace(&mut t.to, Vec::new())
|
std::mem::replace(&mut t.to, SmallVec::new())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|addr| addr.into())
|
.map(|addr| addr.into())
|
||||||
.collect::<Vec<crate::email::Address>>(),
|
.collect::<SmallVec<[crate::email::Address; 1]>>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(ref mut cc) = t.cc {
|
if let Some(ref mut cc) = t.cc {
|
||||||
env.set_cc(
|
env.set_cc(
|
||||||
std::mem::replace(cc, Vec::new())
|
std::mem::replace(cc, SmallVec::new())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|addr| addr.into())
|
.map(|addr| addr.into())
|
||||||
.collect::<Vec<crate::email::Address>>(),
|
.collect::<SmallVec<[crate::email::Address; 1]>>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -131,9 +131,9 @@ pub type EnvelopeHash = u64;
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct Envelope {
|
pub struct Envelope {
|
||||||
date: String,
|
date: String,
|
||||||
from: Vec<Address>,
|
from: SmallVec<[Address; 1]>,
|
||||||
to: Vec<Address>,
|
to: SmallVec<[Address; 1]>,
|
||||||
cc: Vec<Address>,
|
cc: SmallVec<[Address; 1]>,
|
||||||
bcc: Vec<Address>,
|
bcc: Vec<Address>,
|
||||||
subject: Option<String>,
|
subject: Option<String>,
|
||||||
message_id: MessageID,
|
message_id: MessageID,
|
||||||
|
@ -175,9 +175,9 @@ impl Envelope {
|
||||||
pub fn new(hash: EnvelopeHash) -> Self {
|
pub fn new(hash: EnvelopeHash) -> Self {
|
||||||
Envelope {
|
Envelope {
|
||||||
date: String::new(),
|
date: String::new(),
|
||||||
from: Vec::new(),
|
from: SmallVec::new(),
|
||||||
to: Vec::new(),
|
to: SmallVec::new(),
|
||||||
cc: Vec::new(),
|
cc: SmallVec::new(),
|
||||||
bcc: Vec::new(),
|
bcc: Vec::new(),
|
||||||
subject: None,
|
subject: None,
|
||||||
message_id: MessageID::default(),
|
message_id: MessageID::default(),
|
||||||
|
@ -279,7 +279,7 @@ impl Envelope {
|
||||||
let parse_result = parser::address::rfc2822address_list(value);
|
let parse_result = parser::address::rfc2822address_list(value);
|
||||||
if parse_result.is_ok() {
|
if parse_result.is_ok() {
|
||||||
let value = parse_result.unwrap().1;
|
let value = parse_result.unwrap().1;
|
||||||
self.set_bcc(value);
|
self.set_bcc(value.to_vec());
|
||||||
};
|
};
|
||||||
} else if name.eq_ignore_ascii_case(b"from") {
|
} else if name.eq_ignore_ascii_case(b"from") {
|
||||||
let parse_result = parser::address::rfc2822address_list(value);
|
let parse_result = parser::address::rfc2822address_list(value);
|
||||||
|
@ -384,31 +384,102 @@ impl Envelope {
|
||||||
pub fn date_as_str(&self) -> &str {
|
pub fn date_as_str(&self) -> &str {
|
||||||
&self.date
|
&self.date
|
||||||
}
|
}
|
||||||
pub fn from(&self) -> &Vec<Address> {
|
pub fn from(&self) -> &[Address] {
|
||||||
&self.from
|
self.from.as_slice()
|
||||||
}
|
}
|
||||||
pub fn field_bcc_to_string(&self) -> String {
|
pub fn field_bcc_to_string(&self) -> String {
|
||||||
let _strings: Vec<String> = self.bcc.iter().map(|a| format!("{}", a)).collect();
|
if self.bcc.is_empty() {
|
||||||
_strings.join(", ")
|
self.other_headers
|
||||||
|
.get("Bcc")
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string()
|
||||||
|
} else {
|
||||||
|
self.bcc.iter().fold(String::new(), |mut acc, x| {
|
||||||
|
if !acc.is_empty() {
|
||||||
|
acc.push_str(", ");
|
||||||
|
}
|
||||||
|
acc.push_str(&x.to_string());
|
||||||
|
acc
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn field_cc_to_string(&self) -> String {
|
pub fn field_cc_to_string(&self) -> String {
|
||||||
let _strings: Vec<String> = self.cc.iter().map(|a| format!("{}", a)).collect();
|
if self.cc.is_empty() {
|
||||||
_strings.join(", ")
|
self.other_headers
|
||||||
|
.get("Cc")
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string()
|
||||||
|
} else {
|
||||||
|
self.cc.iter().fold(String::new(), |mut acc, x| {
|
||||||
|
if !acc.is_empty() {
|
||||||
|
acc.push_str(", ");
|
||||||
|
}
|
||||||
|
acc.push_str(&x.to_string());
|
||||||
|
acc
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn field_from_to_string(&self) -> String {
|
pub fn field_from_to_string(&self) -> String {
|
||||||
let _strings: Vec<String> = self.from().iter().map(|a| format!("{}", a)).collect();
|
if self.from.is_empty() {
|
||||||
_strings.join(", ")
|
self.other_headers
|
||||||
|
.get("From")
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string()
|
||||||
|
} else {
|
||||||
|
self.from.iter().fold(String::new(), |mut acc, x| {
|
||||||
|
if !acc.is_empty() {
|
||||||
|
acc.push_str(", ");
|
||||||
|
}
|
||||||
|
acc.push_str(&x.to_string());
|
||||||
|
acc
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn to(&self) -> &Vec<Address> {
|
pub fn to(&self) -> &[Address] {
|
||||||
&self.to
|
self.to.as_slice()
|
||||||
}
|
}
|
||||||
pub fn field_to_to_string(&self) -> String {
|
pub fn field_to_to_string(&self) -> String {
|
||||||
let _strings: Vec<String> = self.to.iter().map(|a| format!("{}", a)).collect();
|
if self.to.is_empty() {
|
||||||
_strings.join(", ")
|
self.other_headers
|
||||||
|
.get("To")
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string()
|
||||||
|
} else {
|
||||||
|
self.to
|
||||||
|
.iter()
|
||||||
|
.map(|a| format!("{}", a))
|
||||||
|
.fold(String::new(), |mut acc, x| {
|
||||||
|
if !acc.is_empty() {
|
||||||
|
acc.push_str(", ");
|
||||||
|
}
|
||||||
|
acc.push_str(&x);
|
||||||
|
acc
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn field_references_to_string(&self) -> String {
|
pub fn field_references_to_string(&self) -> String {
|
||||||
let _strings: Vec<String> = self.references().iter().map(|a| a.to_string()).collect();
|
let refs = self.references();
|
||||||
_strings.join(", ")
|
if refs.is_empty() {
|
||||||
|
self.other_headers
|
||||||
|
.get("References")
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string()
|
||||||
|
} else {
|
||||||
|
refs.iter()
|
||||||
|
.map(|a| a.to_string())
|
||||||
|
.fold(String::new(), |mut acc, x| {
|
||||||
|
if !acc.is_empty() {
|
||||||
|
acc.push_str(", ");
|
||||||
|
}
|
||||||
|
acc.push_str(&x);
|
||||||
|
acc
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn body_bytes(&self, bytes: &[u8]) -> Attachment {
|
pub fn body_bytes(&self, bytes: &[u8]) -> Attachment {
|
||||||
|
@ -474,13 +545,13 @@ impl Envelope {
|
||||||
pub fn set_bcc(&mut self, new_val: Vec<Address>) {
|
pub fn set_bcc(&mut self, new_val: Vec<Address>) {
|
||||||
self.bcc = new_val;
|
self.bcc = new_val;
|
||||||
}
|
}
|
||||||
pub fn set_cc(&mut self, new_val: Vec<Address>) {
|
pub fn set_cc(&mut self, new_val: SmallVec<[Address; 1]>) {
|
||||||
self.cc = new_val;
|
self.cc = new_val;
|
||||||
}
|
}
|
||||||
pub fn set_from(&mut self, new_val: Vec<Address>) {
|
pub fn set_from(&mut self, new_val: SmallVec<[Address; 1]>) {
|
||||||
self.from = new_val;
|
self.from = new_val;
|
||||||
}
|
}
|
||||||
pub fn set_to(&mut self, new_val: Vec<Address>) {
|
pub fn set_to(&mut self, new_val: SmallVec<[Address; 1]>) {
|
||||||
self.to = new_val;
|
self.to = new_val;
|
||||||
}
|
}
|
||||||
pub fn set_in_reply_to(&mut self, new_val: &[u8]) {
|
pub fn set_in_reply_to(&mut self, new_val: &[u8]) {
|
||||||
|
@ -563,16 +634,13 @@ impl Envelope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn references(&self) -> Vec<&MessageID> {
|
pub fn references(&self) -> SmallVec<[&MessageID; 8]> {
|
||||||
match self.references {
|
match self.references {
|
||||||
Some(ref s) => s
|
Some(ref s) => s.refs.iter().fold(SmallVec::new(), |mut acc, x| {
|
||||||
.refs
|
acc.push(x);
|
||||||
.iter()
|
acc
|
||||||
.fold(Vec::with_capacity(s.refs.len()), |mut acc, x| {
|
}),
|
||||||
acc.push(x);
|
None => SmallVec::new(),
|
||||||
acc
|
|
||||||
}),
|
|
||||||
None => Vec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1348,8 +1348,8 @@ pub mod address {
|
||||||
// ws!(alt_complete!(mailbox | group))
|
// ws!(alt_complete!(mailbox | group))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rfc2822address_list(input: &[u8]) -> IResult<&[u8], Vec<Address>> {
|
pub fn rfc2822address_list(input: &[u8]) -> IResult<&[u8], SmallVec<[Address; 1]>> {
|
||||||
separated_list(is_a(","), address)(input.ltrim())
|
separated_list_smallvec(is_a(","), address)(input.ltrim())
|
||||||
// ws!( separated_list!(is_a!(","), address))
|
// ws!( separated_list!(is_a!(","), address))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1417,6 +1417,70 @@ pub mod address {
|
||||||
separated_list(is_a(" \n\t\r"), message_id_peek)(input)
|
separated_list(is_a(" \n\t\r"), message_id_peek)(input)
|
||||||
// separated_list!(complete!(is_a!(" \n\t\r")), message_id_peek));
|
// separated_list!(complete!(is_a!(" \n\t\r")), message_id_peek));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
pub fn separated_list_smallvec<I, O, Sep, E, F, G>(
|
||||||
|
sep: G,
|
||||||
|
f: F,
|
||||||
|
) -> impl FnMut(I) -> IResult<I, SmallVec<[O; 1]>, E>
|
||||||
|
where
|
||||||
|
I: Clone + PartialEq,
|
||||||
|
F: Fn(I) -> IResult<I, O, E>,
|
||||||
|
G: Fn(I) -> IResult<I, Sep, E>,
|
||||||
|
E: nom::error::ParseError<I>,
|
||||||
|
{
|
||||||
|
move |i: I| {
|
||||||
|
let mut res = SmallVec::new();
|
||||||
|
let mut i = i.clone();
|
||||||
|
|
||||||
|
// Parse the first element
|
||||||
|
match f(i.clone()) {
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
Ok((i1, o)) => {
|
||||||
|
if i1 == i {
|
||||||
|
return Err(nom::Err::Error(E::from_error_kind(
|
||||||
|
i1,
|
||||||
|
ErrorKind::SeparatedList,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push(o);
|
||||||
|
i = i1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match sep(i.clone()) {
|
||||||
|
Err(nom::Err::Error(_)) => return Ok((i, res)),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
Ok((i1, _)) => {
|
||||||
|
if i1 == i {
|
||||||
|
return Err(nom::Err::Error(E::from_error_kind(
|
||||||
|
i1,
|
||||||
|
ErrorKind::SeparatedList,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
match f(i1.clone()) {
|
||||||
|
Err(nom::Err::Error(_)) => return Ok((i, res)),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
Ok((i2, o)) => {
|
||||||
|
if i2 == i {
|
||||||
|
return Err(nom::Err::Error(E::from_error_kind(
|
||||||
|
i2,
|
||||||
|
ErrorKind::SeparatedList,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push(o);
|
||||||
|
i = i2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1497,7 +1561,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(
|
(
|
||||||
&s[0..0],
|
&s[0..0],
|
||||||
vec![
|
smallvec::smallvec![
|
||||||
make_address!("Obit Oppidum", "user@domain"),
|
make_address!("Obit Oppidum", "user@domain"),
|
||||||
make_address!("list", "list@domain.tld"),
|
make_address!("list", "list@domain.tld"),
|
||||||
make_address!("list2", "list2@domain.tld"),
|
make_address!("list2", "list2@domain.tld"),
|
||||||
|
|
Loading…
Reference in New Issue