mailparsing: refactor: Mailbox::name to Option<BString>

This commit is contained in:
Wez Furlong
2026-03-28 07:20:31 +00:00
parent 1e99d430e4
commit 0cfd886ee0
4 changed files with 31 additions and 30 deletions
+3 -3
View File
@@ -554,7 +554,7 @@ impl<'a> Compiled<'a> {
if need_to {
builder.set_to(Address::Mailbox(Mailbox {
name: recip.name.clone(),
name: recip.name.clone().map(Into::into),
address: AddrSpec::parse(&recip.email)?,
}))?;
}
@@ -586,7 +586,7 @@ impl InjectV1Request {
} => {
if let Some(from) = from {
let mailbox = Address::Mailbox(Mailbox {
name: from.name.clone(),
name: from.name.clone().map(Into::into),
address: AddrSpec::parse(&from.email)
.context("failed parsing content.from")?,
});
@@ -595,7 +595,7 @@ impl InjectV1Request {
}
if let Some(reply_to) = reply_to {
let mailbox = Address::Mailbox(Mailbox {
name: reply_to.name.clone(),
name: reply_to.name.clone().map(Into::into),
address: AddrSpec::parse(&reply_to.email)
.context("failed parsing content.reply_to")?,
});
+5 -5
View File
@@ -577,7 +577,7 @@ Ok(
fn assign_mailbox() {
let mut sender = Header::with_name_value("Sender", "");
sender.assign(Mailbox {
name: Some("John Smith".to_string()),
name: Some("John Smith".into()),
address: AddrSpec::new("john.smith", "example.com"),
});
assert_eq!(
@@ -586,7 +586,7 @@ Ok(
);
sender.assign(Mailbox {
name: Some("John \"the smith\" Smith".to_string()),
name: Some("John \"the smith\" Smith".into()),
address: AddrSpec::new("john.smith", "example.com"),
});
assert_eq!(
@@ -600,7 +600,7 @@ Ok(
let sender = Header::new(
"Sender",
Mailbox {
name: Some("John".to_string()),
name: Some("John".into()),
address: AddrSpec::new("john.smith", "example.com"),
},
);
@@ -612,7 +612,7 @@ Ok(
let sender = Header::new(
"Sender",
Mailbox {
name: Some("John".to_string()),
name: Some("John".into()),
address: AddrSpec::new("john smith", "example.com"),
},
);
@@ -627,7 +627,7 @@ Ok(
let sender = Header::new(
"Sender",
Mailbox {
name: Some("André Pirard".to_string()),
name: Some("André Pirard".into()),
address: AddrSpec::new("andre", "example.com"),
},
);
+17 -17
View File
@@ -313,7 +313,7 @@ fn name_addr(input: Span) -> IResult<Span, Mailbox> {
context(
"name_addr",
map((opt(display_name), angle_addr), |(name, address)| Mailbox {
name,
name: name.map(Into::into),
address,
}),
)
@@ -1705,7 +1705,7 @@ impl MailboxList {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Mailbox {
pub name: Option<String>,
pub name: Option<BString>,
pub address: AddrSpec,
}
@@ -2170,20 +2170,21 @@ fn test_qp_encode() {
/// Quote input string `s`, using a backslash escape, if any
/// of the characters is NOT atext. When quoting, the input
/// string is enclosed in quotes.
fn quote_string(s: &str) -> String {
fn quote_string(s: impl AsRef<[u8]>) -> BString {
let s = s.as_ref();
if s.chars().any(|c| !is_atext(c)) {
let mut result = String::with_capacity(s.len() + 4);
result.push('"');
for c in s.chars() {
let mut result = Vec::<u8>::with_capacity(s.len() + 4);
result.push(b'"');
for (start, end, c) in s.char_indices() {
if !c.is_ascii_whitespace() && !is_qtext(c) && !is_atext(c) {
result.push('\\');
result.push(b'\\');
}
result.push(c);
result.push_str(&s[start..end]);
}
result.push('"');
result
result.push(b'"');
result.into()
} else {
s.to_string()
s.into()
}
}
@@ -2209,12 +2210,11 @@ impl EncodeHeaderValue for Mailbox {
fn encode_value(&self) -> SharedString<'static> {
match &self.name {
Some(name) => {
let mut value = if name.is_ascii() {
quote_string(name)
let mut value: Vec<u8> = if name.is_ascii() {
quote_string(name).into()
} else {
qp_encode(name.as_bytes())
}
.into_bytes();
qp_encode(name.as_bytes()).into_bytes()
};
value.push_str(" <");
value.push_str(self.address.encode_value().as_bytes());
@@ -2282,7 +2282,7 @@ mod test {
#[test]
fn mailbox_encodes_at() {
let mbox = Mailbox {
name: Some("foo@bar.com".to_string()),
name: Some("foo@bar.com".into()),
address: AddrSpec {
local_part: "foo".into(),
domain: "bar.com".into(),
+6 -5
View File
@@ -1,3 +1,4 @@
use bstr::{BStr, BString, ByteSlice};
use config::any_err;
use dns_resolver::DomainClassification;
use mailparsing::{Address, AddressList, EncodeHeaderValue, Mailbox};
@@ -164,9 +165,9 @@ impl HeaderAddressList {
/// If the address list is comprised of a single entry,
/// returns just the display name portion, if any
pub fn name(&self) -> anyhow::Result<Option<&str>> {
pub fn name(&self) -> anyhow::Result<Option<&BStr>> {
let addr = self.single_address()?;
Ok(addr.name.as_deref())
Ok(addr.name.as_ref().map(|b| b.as_bstr()))
}
pub fn email(&self) -> anyhow::Result<Option<&str>> {
@@ -285,7 +286,7 @@ impl From<&Address> for HeaderAddressEntry {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HeaderAddress {
pub name: Option<String>,
pub name: Option<BString>,
pub address: Option<String>,
}
@@ -310,8 +311,8 @@ impl HeaderAddress {
pub fn email(&self) -> Option<&str> {
self.address.as_deref()
}
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
pub fn name(&self) -> Option<&BStr> {
self.name.as_ref().map(|b| b.as_bstr())
}
pub fn crack_address(&self) -> anyhow::Result<(&str, &str)> {