melib/email.rs: use SmallVec for Address fields

memfd
Manos Pitsidianakis 2020-07-26 16:08:22 +03:00
parent 52cec59215
commit 32b4c30fee
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
4 changed files with 191 additions and 51 deletions

View File

@ -29,7 +29,7 @@ use nom::{
character::complete::digit1,
character::is_digit,
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},
};
use std::str::FromStr;
@ -1179,7 +1179,7 @@ pub fn envelope(input: &[u8]) -> IResult<&[u8], Envelope> {
}
if let Some(bcc) = bcc {
env.set_bcc(bcc);
env.set_bcc(bcc.to_vec());
}
if let Some(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
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((
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, envelopes) =
many1(delimited(tag("("), envelope_address, tag(")")))(input.ltrim())?;
let (input, envelopes) = fold_many1(
delimited(tag("("), envelope_address, tag(")")),
SmallVec::new(),
|mut acc, item| {
acc.push(item);
acc
},
)(input.ltrim())?;
let (input, _) = tag(")")(input)?;
Ok((input, Some(envelopes)))
},

View File

@ -141,17 +141,17 @@ pub struct EmailObject {
#[serde(default)]
message_id: Vec<String>,
#[serde(default)]
to: Vec<EmailAddress>,
to: SmallVec<[EmailAddress; 1]>,
#[serde(default)]
bcc: Option<Vec<EmailAddress>>,
#[serde(default)]
reply_to: Option<Vec<EmailAddress>>,
#[serde(default)]
cc: Option<Vec<EmailAddress>>,
cc: Option<SmallVec<[EmailAddress; 1]>>,
#[serde(default)]
sender: Option<Vec<EmailAddress>>,
#[serde(default)]
from: Vec<EmailAddress>,
from: SmallVec<[EmailAddress; 1]>,
#[serde(default)]
in_reply_to: Option<Vec<String>>,
#[serde(default)]
@ -269,24 +269,24 @@ impl std::convert::From<EmailObject> for crate::Envelope {
}
env.set_from(
std::mem::replace(&mut t.from, Vec::new())
std::mem::replace(&mut t.from, SmallVec::new())
.into_iter()
.map(|addr| addr.into())
.collect::<Vec<crate::email::Address>>(),
.collect::<SmallVec<[crate::email::Address; 1]>>(),
);
env.set_to(
std::mem::replace(&mut t.to, Vec::new())
std::mem::replace(&mut t.to, SmallVec::new())
.into_iter()
.map(|addr| addr.into())
.collect::<Vec<crate::email::Address>>(),
.collect::<SmallVec<[crate::email::Address; 1]>>(),
);
if let Some(ref mut cc) = t.cc {
env.set_cc(
std::mem::replace(cc, Vec::new())
std::mem::replace(cc, SmallVec::new())
.into_iter()
.map(|addr| addr.into())
.collect::<Vec<crate::email::Address>>(),
.collect::<SmallVec<[crate::email::Address; 1]>>(),
);
}

View File

@ -131,9 +131,9 @@ pub type EnvelopeHash = u64;
#[derive(Clone, Serialize, Deserialize)]
pub struct Envelope {
date: String,
from: Vec<Address>,
to: Vec<Address>,
cc: Vec<Address>,
from: SmallVec<[Address; 1]>,
to: SmallVec<[Address; 1]>,
cc: SmallVec<[Address; 1]>,
bcc: Vec<Address>,
subject: Option<String>,
message_id: MessageID,
@ -175,9 +175,9 @@ impl Envelope {
pub fn new(hash: EnvelopeHash) -> Self {
Envelope {
date: String::new(),
from: Vec::new(),
to: Vec::new(),
cc: Vec::new(),
from: SmallVec::new(),
to: SmallVec::new(),
cc: SmallVec::new(),
bcc: Vec::new(),
subject: None,
message_id: MessageID::default(),
@ -279,7 +279,7 @@ impl Envelope {
let parse_result = parser::address::rfc2822address_list(value);
if parse_result.is_ok() {
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") {
let parse_result = parser::address::rfc2822address_list(value);
@ -384,31 +384,102 @@ impl Envelope {
pub fn date_as_str(&self) -> &str {
&self.date
}
pub fn from(&self) -> &Vec<Address> {
&self.from
pub fn from(&self) -> &[Address] {
self.from.as_slice()
}
pub fn field_bcc_to_string(&self) -> String {
let _strings: Vec<String> = self.bcc.iter().map(|a| format!("{}", a)).collect();
_strings.join(", ")
if self.bcc.is_empty() {
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 {
let _strings: Vec<String> = self.cc.iter().map(|a| format!("{}", a)).collect();
_strings.join(", ")
if self.cc.is_empty() {
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 {
let _strings: Vec<String> = self.from().iter().map(|a| format!("{}", a)).collect();
_strings.join(", ")
if self.from.is_empty() {
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> {
&self.to
pub fn to(&self) -> &[Address] {
self.to.as_slice()
}
pub fn field_to_to_string(&self) -> String {
let _strings: Vec<String> = self.to.iter().map(|a| format!("{}", a)).collect();
_strings.join(", ")
if self.to.is_empty() {
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 {
let _strings: Vec<String> = self.references().iter().map(|a| a.to_string()).collect();
_strings.join(", ")
let refs = self.references();
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 {
@ -474,13 +545,13 @@ impl Envelope {
pub fn set_bcc(&mut self, new_val: Vec<Address>) {
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;
}
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;
}
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;
}
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 {
Some(ref s) => s
.refs
.iter()
.fold(Vec::with_capacity(s.refs.len()), |mut acc, x| {
acc.push(x);
acc
}),
None => Vec::new(),
Some(ref s) => s.refs.iter().fold(SmallVec::new(), |mut acc, x| {
acc.push(x);
acc
}),
None => SmallVec::new(),
}
}

View File

@ -1348,8 +1348,8 @@ pub mod address {
// ws!(alt_complete!(mailbox | group))
}
pub fn rfc2822address_list(input: &[u8]) -> IResult<&[u8], Vec<Address>> {
separated_list(is_a(","), address)(input.ltrim())
pub fn rfc2822address_list(input: &[u8]) -> IResult<&[u8], SmallVec<[Address; 1]>> {
separated_list_smallvec(is_a(","), address)(input.ltrim())
// 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!(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)]
@ -1497,7 +1561,7 @@ mod tests {
assert_eq!(
(
&s[0..0],
vec![
smallvec::smallvec![
make_address!("Obit Oppidum", "user@domain"),
make_address!("list", "list@domain.tld"),
make_address!("list2", "list2@domain.tld"),