move HeaderMap function implementation to trait

This commit is contained in:
2026-09-13 18:41:31 +02:00
parent fcf0b47d7d
commit a0e29a6c48
2 changed files with 170 additions and 191 deletions
+166 -42
View File
@@ -3,71 +3,129 @@ use mailparse::MailHeader;
use super::{Header, build_header}; use super::{Header, build_header};
use crate::{CowMailError, extend_over_newline}; use crate::{CowMailError, extend_over_newline};
/// Find, iterate and edit a collection of headers by key.
///
/// Implementors only have to hand out their headers; everything else is
/// provided. Keys match case-insensitively throughout, and an `index` argument
/// counts occurrences of `key`, not positions in the collection.
pub trait HeaderMap<'a, H: Header<'a> + 'a> { pub trait HeaderMap<'a, H: Header<'a> + 'a> {
fn iter_headers(&'a self, key: &str) -> impl Iterator<Item = &'a H>; /// The headers, in the order they appear in the message.
fn iter_headers_mut(&'a mut self, key: &str) -> impl Iterator<Item = &'a mut H>; fn headers(&self) -> &[H];
fn iter_values(&'a self, key: &str) -> impl Iterator<Item = String>;
fn find_header(&self, key: &str) -> Option<&H>;
fn find_header_mut(&mut self, key: &str) -> Option<&mut H>;
fn find_value(&self, key: &str) -> Option<String>;
fn add_header<K: ToString, V: ToString>(&mut self, key: K, value: V);
fn insert_header<K: ToString, V: ToString>(&mut self, key: K, value: V, index: usize);
fn replace_header<K: ToString, V: ToString>(&mut self, key: K, value: V, index: usize);
fn replace_all_headers<K: ToString, V: ToString>(&mut self, key: K, value: V);
fn remove_header(&mut self, key: &str, index: usize);
fn remove_all_headers(&mut self, key: &str);
}
impl<'a, H: Header<'a> + 'a> HeaderMap<'a, H> for Vec<H> { /// The headers, mutably. A `Vec` rather than a slice, because inserting and
fn iter_headers(&'a self, key: &str) -> impl Iterator<Item = &'a H> { /// removing go through it.
self.iter().filter(|h| h.key_eq(key)) fn headers_mut(&mut self) -> &mut Vec<H>;
fn iter_headers<'b>(&'b self, key: &str) -> impl Iterator<Item = &'b H>
where
H: 'b,
{
self.headers().iter().filter(move |h| h.key_eq(key))
} }
fn iter_headers_mut(&'a mut self, key: &str) -> impl Iterator<Item = &'a mut H> { fn iter_headers_mut<'b>(&'b mut self, key: &str) -> impl Iterator<Item = &'b mut H>
self.iter_mut() where
.filter(|h| h.key_eq(key)) H: 'b,
{
self.headers_mut().iter_mut().filter(move |h| h.key_eq(key))
} }
fn iter_values(&'a self, key: &str) -> impl Iterator<Item = String> { /// Examples
/// ```
/// use email_message::{CowMail, HeaderMap};
/// let mail = CowMail::try_from(concat!(
/// "Subject: Test\n",
/// "Subject: Test1\n",
/// "\n",
/// "This is a test message").as_bytes())
/// .unwrap();
/// let mut values = mail.iter_values("Subject");
/// assert_eq!(values.next().unwrap(), "Test");
/// assert_eq!(values.next().unwrap(), "Test1");
/// assert!(values.next().is_none());
/// ```
fn iter_values<'b>(&'b self, key: &str) -> impl Iterator<Item = String>
where
H: 'b,
{
self.iter_headers(key).map(|h| h.get_value()) self.iter_headers(key).map(|h| h.get_value())
} }
fn find_header(&self, key: &str) -> Option<&H> { fn find_header(&self, key: &str) -> Option<&H> {
self.iter().find(|h| h.key_eq(key)) self.headers().iter().find(|h| h.key_eq(key))
} }
fn find_header_mut(&mut self, key: &str) -> Option<&mut H> { fn find_header_mut(&mut self, key: &str) -> Option<&mut H> {
self.iter_mut().find(|h| h.key_eq(key)) self.headers_mut().iter_mut().find(|h| h.key_eq(key))
} }
/// Examples
/// ```
/// use email_message::{CowMail, HeaderMap};
/// let mail = CowMail::try_from(concat!(
/// "Subject: Test\n",
/// "Subject: Test1",
/// "\n",
/// "This is a test message").as_bytes())
/// .unwrap();
/// assert_eq!(mail.find_value("Subject").unwrap(), "Test");
/// ```
fn find_value(&self, key: &str) -> Option<String> { fn find_value(&self, key: &str) -> Option<String> {
self.find_header(key).map(|h| h.get_value()) self.find_header(key).map(|h| h.get_value())
} }
/// Examples
/// ```
/// use email_message::{CowMail, HeaderMap};
/// let mut mail = CowMail::default();
/// mail.add_header("Subject", "Test");
/// assert_eq!(mail.find_value("Subject").unwrap(), "Test");
/// ```
fn add_header<K: ToString, V: ToString>(&mut self, key: K, value: V) { fn add_header<K: ToString, V: ToString>(&mut self, key: K, value: V) {
self.push(H::new(key, value)) self.headers_mut().push(H::new(key, value))
} }
/// Examples
/// ```
/// use email_message::{CowMail, Header, HeaderMap};
/// let mut mail = CowMail::try_from(concat!(
/// "Subject: Test\n",
/// "To: \"John Doe\" <john.doe@example.com>\n",
/// "\n",
/// "This is a test message").as_bytes())
/// .unwrap();
/// mail.insert_header("From", "\"me\" <me@example.com>", 1);
/// assert_eq!(mail.headers[0].get_key(), "Subject");
/// assert_eq!(mail.headers[1].get_key(), "From");
/// assert_eq!(mail.headers[2].get_key(), "To");
/// ```
fn insert_header<K: ToString, V: ToString>(&mut self, key: K, value: V, index: usize) { fn insert_header<K: ToString, V: ToString>(&mut self, key: K, value: V, index: usize) {
let index = std::cmp::min(index, self.len()); let headers = self.headers_mut();
let index = std::cmp::min(index, headers.len());
let header = H::new(key, value); let header = H::new(key, value);
self.insert(index, header); headers.insert(index, header);
}
fn replace_all_headers<K: ToString, V: ToString>(&mut self, key: K, value: V) {
let key = key.to_string();
for header in self {
if !header.get_key().eq_ignore_ascii_case(&key) {
continue;
}
header.set_value(value.to_string());
}
} }
/// Examples
/// ```
/// use email_message::{CowMail, Header, HeaderMap};
/// let mut mail = CowMail::try_from(concat!(
/// "Subject: Test1\n",
/// "To: \"John Doe\" <john.doe@example.com>\n",
/// "Subject: Test2\n",
/// "\n",
/// "This is a test message").as_bytes())
/// .unwrap();
/// mail.replace_header("Subject", "Test3", 0);
/// mail.replace_header("Subject", "Test4", 1);
/// assert_eq!(mail.headers[0].get_value_ref(), "Test3");
/// assert_eq!(mail.headers[1].get_value_ref(), "\"John Doe\" <john.doe@example.com>");
/// assert_eq!(mail.headers[2].get_value_ref(), "Test4");
/// ```
fn replace_header<K: ToString, V: ToString>(&mut self, key: K, value: V, index: usize) { fn replace_header<K: ToString, V: ToString>(&mut self, key: K, value: V, index: usize) {
let key = key.to_string(); let key = key.to_string();
let mut count: usize = 0; let mut count: usize = 0;
for header in self { for header in self.headers_mut() {
if !header.get_key().eq_ignore_ascii_case(&key) { if !header.get_key().eq_ignore_ascii_case(&key) {
continue; continue;
} }
@@ -79,20 +137,86 @@ impl<'a, H: Header<'a> + 'a> HeaderMap<'a, H> for Vec<H> {
} }
} }
fn remove_all_headers(&mut self, key: &str) { /// Examples
self.retain(|h| !h.key_eq(key)); /// ```
/// use email_message::{CowMail, Header, HeaderMap};
/// let mut mail = CowMail::try_from(concat!(
/// "Subject: Test\n",
/// "To: \"John Doe\" <john.doe@example.com>\n",
/// "Subject: Test1\n",
/// "\n",
/// "This is a test message").as_bytes())
/// .unwrap();
/// mail.replace_all_headers("Subject", "Test2");
/// assert_eq!(mail.headers[0].get_value_ref(), "Test2");
/// assert_eq!(mail.headers[1].get_value_ref(), "\"John Doe\" <john.doe@example.com>");
/// assert_eq!(mail.headers[2].get_value_ref(), "Test2");
/// ```
fn replace_all_headers<K: ToString, V: ToString>(&mut self, key: K, value: V) {
let key = key.to_string();
for header in self.headers_mut() {
if !header.get_key().eq_ignore_ascii_case(&key) {
continue;
}
header.set_value(value.to_string());
}
} }
/// Examples
/// ```
/// use email_message::{CowMail, Header, HeaderMap};
/// let mut mail = CowMail::try_from(concat!(
/// "Subject: Test1\n",
/// "To: \"John Doe\" <john.doe@example.com>\n",
/// "Subject: Test2\n",
/// "\n",
/// "This is a test message").as_bytes())
/// .unwrap();
/// mail.remove_header("Subject", 1);
/// assert_eq!(mail.headers.len(), 2);
/// assert_eq!(mail.headers[0].get_value_ref(), "Test1");
/// ```
fn remove_header(&mut self, key: &str, index: usize) { fn remove_header(&mut self, key: &str, index: usize) {
if let Some((index, _)) = self let found = self
.headers()
.iter() .iter()
.enumerate() .enumerate()
.filter(move |h| h.1.key_eq(key)) .filter(|h| h.1.key_eq(key))
.nth(index) .nth(index)
{ .map(|(index, _)| index);
self.remove(index);
if let Some(index) = found {
self.headers_mut().remove(index);
} }
} }
/// Examples
/// ```
/// use email_message::{CowMail, Header, HeaderMap};
/// let mut mail = CowMail::try_from(concat!(
/// "Subject: Test1\n",
/// "To: \"John Doe\" <john.doe@example.com>\n",
/// "Subject: Test2\n",
/// "\n",
/// "This is a test message").as_bytes())
/// .unwrap();
/// mail.remove_all_headers("Subject");
/// assert_eq!(mail.headers.len(), 1);
/// assert_eq!(mail.headers[0].get_value_ref(), "\"John Doe\" <john.doe@example.com>");
/// ```
fn remove_all_headers(&mut self, key: &str) {
self.headers_mut().retain(|h| !h.key_eq(key));
}
}
impl<'a, H: Header<'a> + 'a> HeaderMap<'a, H> for Vec<H> {
fn headers(&self) -> &[H] {
self
}
fn headers_mut(&mut self) -> &mut Vec<H> {
self
}
} }
/// Rebuild the header lines mailparse has already scanned, so that a part's /// Rebuild the header lines mailparse has already scanned, so that a part's
+4 -149
View File
@@ -666,157 +666,12 @@ impl<'a, H: Header<'a> + 'a> CowMail<'a, H> {
} }
impl<'a, H: Header<'a> + 'a> HeaderMap<'a, H> for CowMail<'a, H> { impl<'a, H: Header<'a> + 'a> HeaderMap<'a, H> for CowMail<'a, H> {
fn iter_headers(&'a self, key: &str) -> impl Iterator<Item = &'a H> { fn headers(&self) -> &[H] {
self.headers.iter_headers(key) &self.headers
} }
fn iter_headers_mut(&'a mut self, key: &str) -> impl Iterator<Item = &'a mut H> { fn headers_mut(&mut self) -> &mut Vec<H> {
self.headers.iter_headers_mut(key) &mut self.headers
}
fn find_header(&self, key: &str) -> Option<&H> {
self.headers.find_header(key)
}
fn find_header_mut(&mut self, key: &str) -> Option<&mut H> {
self.headers.find_header_mut(key)
}
/// Examples
/// ```
/// use email_message::{CowMail, HeaderMap};
/// let mail = CowMail::try_from(concat!(
/// "Subject: Test\n",
/// "Subject: Test1\n",
/// "\n",
/// "This is a test message").as_bytes())
/// .unwrap();
/// let mut values = mail.iter_values("Subject");
/// assert_eq!(values.next().unwrap(), "Test");
/// assert_eq!(values.next().unwrap(), "Test1");
/// assert!(values.next().is_none());
/// ```
fn iter_values(&'a self, key: &str) -> impl Iterator<Item = String> {
self.headers.iter_values(key)
}
/// Examples
/// ```
/// use email_message::{CowMail, HeaderMap};
/// let mail = CowMail::try_from(concat!(
/// "Subject: Test\n",
/// "Subject: Test1",
/// "\n",
/// "This is a test message").as_bytes())
/// .unwrap();
/// assert_eq!(mail.find_value("Subject").unwrap(), "Test");
/// ```
fn find_value(&self, key: &str) -> Option<String> {
self.headers.find_value(key)
}
/// Examples
/// ```
/// use email_message::{CowMail, HeaderMap};
/// let mut mail = CowMail::default();
/// mail.add_header("Subject", "Test");
/// assert_eq!(mail.find_value("Subject").unwrap(), "Test");
/// ```
fn add_header<K: ToString, V: ToString>(&mut self, key: K, value: V) {
self.headers.add_header(key, value)
}
/// Examples
/// ```
/// use email_message::{CowMail, Header, HeaderMap};
/// let mut mail = CowMail::try_from(concat!(
/// "Subject: Test\n",
/// "To: \"John Doe\" <john.doe@example.com>\n",
/// "\n",
/// "This is a test message").as_bytes())
/// .unwrap();
/// mail.insert_header("From", "\"me\" <me@example.com>", 1);
/// assert_eq!(mail.headers[0].get_key(), "Subject");
/// assert_eq!(mail.headers[1].get_key(), "From");
/// assert_eq!(mail.headers[2].get_key(), "To");
/// ```
fn insert_header<K: ToString, V: ToString>(&mut self, key: K, value: V, index: usize) {
self.headers.insert_header(key, value, index)
}
/// Examples
/// ```
/// use email_message::{CowMail, Header, HeaderMap};
/// let mut mail = CowMail::try_from(concat!(
/// "Subject: Test\n",
/// "To: \"John Doe\" <john.doe@example.com>\n",
/// "Subject: Test1\n",
/// "\n",
/// "This is a test message").as_bytes())
/// .unwrap();
/// mail.replace_all_headers("Subject", "Test2");
/// assert_eq!(mail.headers[0].get_value_ref(), "Test2");
/// assert_eq!(mail.headers[1].get_value_ref(), "\"John Doe\" <john.doe@example.com>");
/// assert_eq!(mail.headers[2].get_value_ref(), "Test2");
/// ```
fn replace_all_headers<K: ToString, V: ToString>(&mut self, key: K, value: V) {
self.headers.replace_all_headers(key, value)
}
/// Examples
/// ```
/// use email_message::{CowMail, Header, HeaderMap};
/// let mut mail = CowMail::try_from(concat!(
/// "Subject: Test1\n",
/// "To: \"John Doe\" <john.doe@example.com>\n",
/// "Subject: Test2\n",
/// "\n",
/// "This is a test message").as_bytes())
/// .unwrap();
/// mail.replace_header("Subject", "Test3", 0);
/// mail.replace_header("Subject", "Test4", 1);
/// assert_eq!(mail.headers[0].get_value_ref(), "Test3");
/// assert_eq!(mail.headers[1].get_value_ref(), "\"John Doe\" <john.doe@example.com>");
/// assert_eq!(mail.headers[2].get_value_ref(), "Test4");
/// ```
fn replace_header<K: ToString, V: ToString>(&mut self, key: K, value: V, index: usize) {
self.headers.replace_header(key, value, index)
}
/// Examples
/// ```
/// use email_message::{CowMail, Header, HeaderMap};
/// let mut mail = CowMail::try_from(concat!(
/// "Subject: Test1\n",
/// "To: \"John Doe\" <john.doe@example.com>\n",
/// "Subject: Test2\n",
/// "\n",
/// "This is a test message").as_bytes())
/// .unwrap();
/// mail.remove_all_headers("Subject");
/// assert_eq!(mail.headers.len(), 1);
/// assert_eq!(mail.headers[0].get_value_ref(), "\"John Doe\" <john.doe@example.com>");
/// ```
fn remove_all_headers(&mut self, key: &str) {
self.headers.remove_all_headers(key)
}
/// Examples
/// ```
/// use email_message::{CowMail, Header, HeaderMap};
/// let mut mail = CowMail::try_from(concat!(
/// "Subject: Test1\n",
/// "To: \"John Doe\" <john.doe@example.com>\n",
/// "Subject: Test2\n",
/// "\n",
/// "This is a test message").as_bytes())
/// .unwrap();
/// mail.remove_header("Subject", 1);
/// assert_eq!(mail.headers.len(), 2);
/// assert_eq!(mail.headers[0].get_value_ref(), "Test1");
/// ```
fn remove_header(&mut self, key: &str, index: usize) {
self.headers.remove_header(key, index)
} }
} }