fix errors

This commit is contained in:
2024-11-01 05:07:40 +01:00
parent df2e260b91
commit 947641578f
3 changed files with 67 additions and 64 deletions
+4
View File
@@ -93,6 +93,10 @@ pub trait Header<'a>: Clone + Display {
decode_latin1(self.get_key_raw()) decode_latin1(self.get_key_raw())
} }
fn key_eq(&self, key: &str) -> bool {
self.get_key().eq_ignore_ascii_case(key)
}
fn get_value_ref(&self) -> Cow<str> { fn get_value_ref(&self) -> Cow<str> {
// RFC 6532 says that header values can be UTF-8. Let's try that first, and // RFC 6532 says that header values can be UTF-8. Let's try that first, and
// fall back to latin1 if that fails // fall back to latin1 if that fails
+22 -23
View File
@@ -1,12 +1,12 @@
use crate::header::{Header, parse_header}; use crate::header::{Header, parse_header};
pub trait HeaderMap<'a, H: Header<'a>> { pub trait HeaderMap<'a, H: Header<'a> + 'a> {
fn get_all_headers(&self, key: &str) -> Vec<&H>; fn iter_headers(&'a self, key: &str) -> impl Iterator<Item = &'a H>;
fn get_all_headers_mut(&mut self, key: &str) -> Vec<&mut H>; fn iter_headers_mut(&'a mut self, key: &str) -> impl Iterator<Item = &'a mut H>;
fn get_first_header(&self, key: &str) -> Option<&H>; fn iter_values(&'a self, key: &str) -> impl Iterator<Item = String>;
fn get_first_header_mut(&mut self, key: &str) -> Option<&mut H>; fn find_header(&self, key: &str) -> Option<&H>;
fn get_all_values(&self, key: &str) -> Vec<String>; fn find_header_mut(&mut self, key: &str) -> Option<&mut H>;
fn get_first_value(&self, key: &str) -> Option<String>; fn find_value(&self, key: &str) -> Option<String>;
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);
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);
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);
@@ -15,31 +15,30 @@ pub trait HeaderMap<'a, H: Header<'a>> {
fn remove_all_headers(&mut self, key: &str); fn remove_all_headers(&mut self, key: &str);
} }
impl<'a, H: Header<'a>> HeaderMap<'a, H> for Vec<H> { impl<'a, H: Header<'a> + 'a> HeaderMap<'a, H> for Vec<H> {
fn get_all_headers(&self, key: &str) -> Vec<&H> { fn iter_headers(&'a self, key: &str) -> impl Iterator<Item = &'a H> {
self.iter().filter(|h| h.get_key().eq_ignore_ascii_case(key)).collect() self.iter().filter(|h| h.key_eq(key))
} }
fn get_all_headers_mut(&mut self, key: &str) -> Vec<&mut H> { fn iter_headers_mut(&'a mut self, key: &str) -> impl Iterator<Item = &'a mut H> {
self.iter_mut() self.iter_mut()
.filter(|h| h.get_key().eq_ignore_ascii_case(key)) .filter(|h| h.key_eq(key))
.collect()
} }
fn get_first_header(&self, key: &str) -> Option<&H> { fn iter_values(&'a self, key: &str) -> impl Iterator<Item = String> {
self.iter().find(|h| h.get_key().eq_ignore_ascii_case(key)) self.iter_headers(key).map(|h| h.get_value())
} }
fn get_first_header_mut(&mut self, key: &str) -> Option<&mut H> { fn find_header(&self, key: &str) -> Option<&H> {
self.iter_mut().find(|h| h.get_key().eq_ignore_ascii_case(key)) self.iter().find(|h| h.key_eq(key))
} }
fn get_all_values(&self, key: &str) -> Vec<String> { fn find_header_mut(&mut self, key: &str) -> Option<&mut H> {
self.get_all_headers(key).into_iter().map(|h| h.get_value()).collect() self.iter_mut().find(|h| h.key_eq(key))
} }
fn get_first_value(&self, key: &str) -> Option<String> { fn find_value(&self, key: &str) -> Option<String> {
self.get_first_header(key).map(|h| h.get_value()) self.find_header(key).map(|h| h.get_value())
} }
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) {
@@ -78,14 +77,14 @@ impl<'a, H: Header<'a>> HeaderMap<'a, H> for Vec<H> {
} }
fn remove_all_headers(&mut self, key: &str) { fn remove_all_headers(&mut self, key: &str) {
self.retain(|h| !h.get_key().eq_ignore_ascii_case(key)); self.retain(|h| !h.key_eq(key));
} }
fn remove_header(&mut self, key: &str, index: usize) { fn remove_header(&mut self, key: &str, index: usize) {
if let Some((index, _)) = self if let Some((index, _)) = self
.iter() .iter()
.enumerate() .enumerate()
.filter(move |h| h.1.get_key().eq_ignore_ascii_case(key)) .filter(move |h| h.1.key_eq(key))
.nth(index) .nth(index)
{ {
self.remove(index); self.remove(index);
+41 -41
View File
@@ -114,7 +114,7 @@ impl From<&str> for ContentType {
} }
} }
pub trait Mail<'a, T: Header<'a>>: HeaderMap<'a, T> { pub trait Mail<'a, T: Header<'a> + 'a>: HeaderMap<'a, T> {
fn clear(&mut self); fn clear(&mut self);
fn clear_content(&mut self); fn clear_content(&mut self);
fn default_ctype(&self) -> ContentType; fn default_ctype(&self) -> ContentType;
@@ -122,7 +122,7 @@ pub trait Mail<'a, T: Header<'a>>: HeaderMap<'a, T> {
fn raw_body_mut(&mut self) -> &mut Vec<u8>; fn raw_body_mut(&mut self) -> &mut Vec<u8>;
fn get_content_type(&self) -> ContentType { fn get_content_type(&self) -> ContentType {
self.get_first_header("Content-Type") self.find_header("Content-Type")
.map(|h| ContentType::from(h.get_value_ref().as_ref())) .map(|h| ContentType::from(h.get_value_ref().as_ref()))
.unwrap_or_else(|| self.default_ctype()) .unwrap_or_else(|| self.default_ctype())
} }
@@ -151,7 +151,7 @@ pub trait Mail<'a, T: Header<'a>>: HeaderMap<'a, T> {
} }
fn get_content_disposition(&self) -> Option<ParsedContentDisposition> { fn get_content_disposition(&self) -> Option<ParsedContentDisposition> {
if let Some(header) = self.get_first_header("Content-Disposition") { if let Some(header) = self.find_header("Content-Disposition") {
let value = header.get_value(); let value = header.get_value();
return Some(parse_content_disposition(&value)); return Some(parse_content_disposition(&value));
} }
@@ -167,7 +167,7 @@ pub trait Mail<'a, T: Header<'a>>: HeaderMap<'a, T> {
fn get_content(&self) -> Result<String, CowMailError> { fn get_content(&self) -> Result<String, CowMailError> {
let ctype = self.get_content_type(); let ctype = self.get_content_type();
let transfer_encoding = self.get_first_value("Content-Transfer-Encoding"); let transfer_encoding = self.find_value("Content-Transfer-Encoding");
match self.get_content_encoded(&ctype, &transfer_encoding) { match self.get_content_encoded(&ctype, &transfer_encoding) {
Body::Base64(body) | Body::QuotedPrintable(body) => Ok(body.get_decoded_as_string()?), Body::Base64(body) | Body::QuotedPrintable(body) => Ok(body.get_decoded_as_string()?),
Body::SevenBit(body) | Body::EightBit(body) => Ok(body.get_as_string()?), Body::SevenBit(body) | Body::EightBit(body) => Ok(body.get_as_string()?),
@@ -177,7 +177,7 @@ pub trait Mail<'a, T: Header<'a>>: HeaderMap<'a, T> {
fn get_content_raw(&self) -> Result<Vec<u8>, CowMailError> { fn get_content_raw(&self) -> Result<Vec<u8>, CowMailError> {
let ctype = self.get_content_type(); let ctype = self.get_content_type();
let transfer_encoding = self.get_first_value("Content-Transfer-Encoding"); let transfer_encoding = self.find_value("Content-Transfer-Encoding");
match self.get_content_encoded(&ctype, &transfer_encoding) { match self.get_content_encoded(&ctype, &transfer_encoding) {
Body::Base64(body) | Body::QuotedPrintable(body) => Ok(body.get_decoded()?), Body::Base64(body) | Body::QuotedPrintable(body) => Ok(body.get_decoded()?),
Body::SevenBit(body) | Body::EightBit(body) => Ok(Vec::<u8>::from(body.get_raw())), Body::SevenBit(body) | Body::EightBit(body) => Ok(Vec::<u8>::from(body.get_raw())),
@@ -219,7 +219,7 @@ impl<'a> Default for CowMail<'a, CowHeader<'a>> {
} }
} }
impl<'a, H: Header<'a>> CowMail<'a, H> { impl<'a, H: Header<'a> + 'a> CowMail<'a, H> {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
headers: Vec::new(), headers: Vec::new(),
@@ -246,7 +246,7 @@ impl<'a, H: Header<'a>> CowMail<'a, H> {
} }
pub fn get_content_type(&self) -> ContentType { pub fn get_content_type(&self) -> ContentType {
self.get_first_header("Content-Type") self.find_header("Content-Type")
.map(|h| ContentType::from(h.get_value_ref().as_ref())) .map(|h| ContentType::from(h.get_value_ref().as_ref()))
.unwrap_or_else(|| self.default_ctype.clone()) .unwrap_or_else(|| self.default_ctype.clone())
} }
@@ -282,7 +282,7 @@ impl<'a, H: Header<'a>> CowMail<'a, H> {
} }
pub fn get_content_disposition(&self) -> Option<ParsedContentDisposition> { pub fn get_content_disposition(&self) -> Option<ParsedContentDisposition> {
if let Some(header) = self.get_first_header("Content-Disposition") { if let Some(header) = self.find_header("Content-Disposition") {
let value = header.get_value(); let value = header.get_value();
return Some(parse_content_disposition(&value)); return Some(parse_content_disposition(&value));
} }
@@ -298,7 +298,7 @@ impl<'a, H: Header<'a>> CowMail<'a, H> {
pub fn get_content(&self) -> Result<String, CowMailError> { pub fn get_content(&self) -> Result<String, CowMailError> {
let ctype = self.get_content_type(); let ctype = self.get_content_type();
let transfer_encoding = self.headers.get_first_value("Content-Transfer-Encoding"); let transfer_encoding = self.headers.find_value("Content-Transfer-Encoding");
match self.get_content_encoded(&ctype, &transfer_encoding) { match self.get_content_encoded(&ctype, &transfer_encoding) {
Body::Base64(body) | Body::QuotedPrintable(body) => Ok(body.get_decoded_as_string()?), Body::Base64(body) | Body::QuotedPrintable(body) => Ok(body.get_decoded_as_string()?),
Body::SevenBit(body) | Body::EightBit(body) => Ok(body.get_as_string()?), Body::SevenBit(body) | Body::EightBit(body) => Ok(body.get_as_string()?),
@@ -308,7 +308,7 @@ impl<'a, H: Header<'a>> CowMail<'a, H> {
pub fn get_content_raw(&self) -> Result<Vec<u8>, CowMailError> { pub fn get_content_raw(&self) -> Result<Vec<u8>, CowMailError> {
let ctype = self.get_content_type(); let ctype = self.get_content_type();
let transfer_encoding = self.headers.get_first_value("Content-Transfer-Encoding"); let transfer_encoding = self.headers.find_value("Content-Transfer-Encoding");
match self.get_content_encoded(&ctype, &transfer_encoding) { match self.get_content_encoded(&ctype, &transfer_encoding) {
Body::Base64(body) | Body::QuotedPrintable(body) => Ok(body.get_decoded()?), Body::Base64(body) | Body::QuotedPrintable(body) => Ok(body.get_decoded()?),
Body::SevenBit(body) | Body::EightBit(body) => Ok(Vec::<u8>::from(body.get_raw())), Body::SevenBit(body) | Body::EightBit(body) => Ok(Vec::<u8>::from(body.get_raw())),
@@ -363,14 +363,14 @@ impl<'a, H: Header<'a>> CowMail<'a, H> {
pub fn get_boundary(&self) -> Option<String> { pub fn get_boundary(&self) -> Option<String> {
self.headers self.headers
.get_first_header("Content-Type") .find_header("Content-Type")
.and_then(|h| h.get_param("boundary")) .and_then(|h| h.get_param("boundary"))
} }
pub fn set_boundary<T: ToString>(&mut self, boundary: T) -> Result<(), CowMailError> { pub fn set_boundary<T: ToString>(&mut self, boundary: T) -> Result<(), CowMailError> {
let header = self let header = self
.headers .headers
.get_first_header_mut("Content-Type") .find_header_mut("Content-Type")
.ok_or(CowMailError::MissingContentTypeHeader)?; .ok_or(CowMailError::MissingContentTypeHeader)?;
header.set_param("boundary", boundary.to_string()); header.set_param("boundary", boundary.to_string());
Ok(()) Ok(())
@@ -392,7 +392,7 @@ impl<'a, H: Header<'a>> CowMail<'a, H> {
.and_then(|start| { .and_then(|start| {
self.subparts.iter().position(|part| { self.subparts.iter().position(|part| {
part.headers part.headers
.get_first_value("Content-ID") .find_value("Content-ID")
.map(|cid| cid.eq(start)) .map(|cid| cid.eq(start))
.unwrap_or(false) .unwrap_or(false)
}) })
@@ -476,7 +476,7 @@ impl<'a, H: Header<'a>> CowMail<'a, H> {
subpart.end_bytes = self.end_bytes; subpart.end_bytes = self.end_bytes;
self.end_bytes = b"\r\n"; self.end_bytes = b"\r\n";
if self.headers.get_first_header("MIME-Version").is_none() { if self.headers.find_header("MIME-Version").is_none() {
self.headers.add_header("MIME-Version", "1.0"); self.headers.add_header("MIME-Version", "1.0");
} }
@@ -524,7 +524,7 @@ impl<'a, H: Header<'a>> CowMail<'a, H> {
} }
pub fn add_related(&mut self, mut subpart: CowMail<'a, H>) -> Result<(), CowMailError> { pub fn add_related(&mut self, mut subpart: CowMail<'a, H>) -> Result<(), CowMailError> {
if subpart.headers.get_first_header("Content-Disposition").is_none() { if subpart.headers.find_header("Content-Disposition").is_none() {
subpart.headers.add_header("Content-Disposition", "inline"); subpart.headers.add_header("Content-Disposition", "inline");
} }
self.add_multipart(subpart, MediaType::new(MULTIPART, RELATED)) self.add_multipart(subpart, MediaType::new(MULTIPART, RELATED))
@@ -535,7 +535,7 @@ impl<'a, H: Header<'a>> CowMail<'a, H> {
} }
pub fn add_attachment(&mut self, mut subpart: CowMail<'a, H>) -> Result<(), CowMailError> { pub fn add_attachment(&mut self, mut subpart: CowMail<'a, H>) -> Result<(), CowMailError> {
if subpart.headers.get_first_header("Content-Disposition").is_none() { if subpart.headers.find_header("Content-Disposition").is_none() {
subpart.headers.add_header("Content-Disposition", "attachment"); subpart.headers.add_header("Content-Disposition", "attachment");
} }
self.add_multipart(subpart, MediaType::new(MULTIPART, MIXED)) self.add_multipart(subpart, MediaType::new(MULTIPART, MIXED))
@@ -581,21 +581,21 @@ impl<'a, H: Header<'a>> CowMail<'a, H> {
} }
} }
impl<'a, H: Header<'a>> HeaderMap<'a, H> for CowMail<'a, H> { impl<'a, H: Header<'a> + 'a> HeaderMap<'a, H> for CowMail<'a, H> {
fn get_all_headers(&self, key: &str) -> Vec<&H> { fn iter_headers(&'a self, key: &str) -> impl Iterator<Item = &'a H> {
self.headers.get_all_headers(key) self.headers.iter_headers(key)
} }
fn get_all_headers_mut(&mut self, key: &str) -> Vec<&mut H> { fn iter_headers_mut(&'a mut self, key: &str) -> impl Iterator<Item = &'a mut H> {
self.headers.get_all_headers_mut(key) self.headers.iter_headers_mut(key)
} }
fn get_first_header(&self, key: &str) -> Option<&H> { fn find_header(&self, key: &str) -> Option<&H> {
self.headers.get_first_header(key) self.headers.find_header(key)
} }
fn get_first_header_mut(&mut self, key: &str) -> Option<&mut H> { fn find_header_mut(&mut self, key: &str) -> Option<&mut H> {
self.headers.get_first_header_mut(key) self.headers.find_header_mut(key)
} }
/// Examples /// Examples
@@ -607,13 +607,13 @@ impl<'a, H: Header<'a>> HeaderMap<'a, H> for CowMail<'a, H> {
/// "\n", /// "\n",
/// "This is a test message").as_bytes()) /// "This is a test message").as_bytes())
/// .unwrap(); /// .unwrap();
/// let mut values = mail.get_all_values("Subject").into_iter(); /// let mut values = mail.iter_values("Subject");
/// assert_eq!(values.next().unwrap(), "Test"); /// assert_eq!(values.next().unwrap(), "Test");
/// assert_eq!(values.next().unwrap(), "Test1"); /// assert_eq!(values.next().unwrap(), "Test1");
/// assert!(values.next().is_none()); /// assert!(values.next().is_none());
/// ``` /// ```
fn get_all_values(&self, key: &str) -> Vec<String> { fn iter_values(&'a self, key: &str) -> impl Iterator<Item = String> {
self.headers.get_all_values(key) self.headers.iter_values(key)
} }
/// Examples /// Examples
@@ -625,10 +625,10 @@ impl<'a, H: Header<'a>> HeaderMap<'a, H> for CowMail<'a, H> {
/// "\n", /// "\n",
/// "This is a test message").as_bytes()) /// "This is a test message").as_bytes())
/// .unwrap(); /// .unwrap();
/// assert_eq!(mail.get_first_value("Subject").unwrap(), "Test"); /// assert_eq!(mail.find_value("Subject").unwrap(), "Test");
/// ``` /// ```
fn get_first_value(&self, key: &str) -> Option<String> { fn find_value(&self, key: &str) -> Option<String> {
self.headers.get_first_value(key) self.headers.find_value(key)
} }
/// Examples /// Examples
@@ -636,7 +636,7 @@ impl<'a, H: Header<'a>> HeaderMap<'a, H> for CowMail<'a, H> {
/// use email_message::{CowMail, HeaderMap}; /// use email_message::{CowMail, HeaderMap};
/// let mut mail = CowMail::default(); /// let mut mail = CowMail::default();
/// mail.add_header("Subject", "Test"); /// mail.add_header("Subject", "Test");
/// assert_eq!(mail.get_first_value("Subject").unwrap(), "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.headers.add_header(key, value) self.headers.add_header(key, value)
@@ -798,11 +798,11 @@ impl std::io::Read for CowMailReader<'_> {
} }
} }
pub fn parse_mail<'a, H: Header<'a>>(raw_data: &'a [u8]) -> Result<CowMail<H>, &'static str> { pub fn parse_mail<'a, H: Header<'a> + 'a>(raw_data: &'a [u8]) -> Result<CowMail<H>, &'static str> {
parse_mail_recursive(raw_data, false) parse_mail_recursive(raw_data, false)
} }
fn parse_mail_recursive<'a, H: Header<'a>>( fn parse_mail_recursive<'a, H: Header<'a> + 'a>(
raw_data: &'a [u8], raw_data: &'a [u8],
mut in_multipart_digest: bool, mut in_multipart_digest: bool,
) -> Result<CowMail<H>, &'static str> { ) -> Result<CowMail<H>, &'static str> {
@@ -811,7 +811,7 @@ fn parse_mail_recursive<'a, H: Header<'a>>(
let default_ctype = ContentType::default_conditional(in_multipart_digest); let default_ctype = ContentType::default_conditional(in_multipart_digest);
let ctype = headers let ctype = headers
.get_first_value("Content-Type") .find_value("Content-Type")
.map(|s| ContentType::from(s.as_str())) .map(|s| ContentType::from(s.as_str()))
.unwrap_or(default_ctype.clone()); .unwrap_or(default_ctype.clone());
@@ -922,11 +922,11 @@ mod tests {
} }
mail.insert_header("X-Test", "Test1", 1); mail.insert_header("X-Test", "Test1", 1);
assert_eq!(mail.get_all_values("X-Test"), ["Test", "Test1", "Test2", "Test3"]); assert_eq!(mail.iter_values("X-Test").collect::<Vec<_>>(), ["Test", "Test1", "Test2", "Test3"]);
mail.replace_all_headers("X-Test", "Test555"); mail.replace_all_headers("X-Test", "Test555");
assert_eq!( assert_eq!(
mail.get_all_values("X-Test"), mail.iter_values("X-Test").collect::<Vec<_>>(),
["Test555", "Test555", "Test555", "Test555"] ["Test555", "Test555", "Test555", "Test555"]
); );
@@ -934,10 +934,10 @@ mod tests {
mail.replace_header("X-Test", "Test1", 1); mail.replace_header("X-Test", "Test1", 1);
mail.replace_header("X-Test", "Test2", 2); mail.replace_header("X-Test", "Test2", 2);
mail.replace_header("X-Test", "Test3", 3); mail.replace_header("X-Test", "Test3", 3);
assert_eq!(mail.get_all_values("X-Test"), ["Test", "Test1", "Test2", "Test3"]); assert_eq!(mail.iter_values("X-Test").collect::<Vec<_>>(), ["Test", "Test1", "Test2", "Test3"]);
mail.remove_header("X-Test", 1); mail.remove_header("X-Test", 1);
assert_eq!(mail.get_all_values("X-Test"), ["Test", "Test2", "Test3"]); assert_eq!(mail.iter_values("X-Test").collect::<Vec<_>>(), ["Test", "Test2", "Test3"]);
mail.remove_all_headers("X-Test"); mail.remove_all_headers("X-Test");
assert_eq!(mail.headers.len(), 2); assert_eq!(mail.headers.len(), 2);
} }
@@ -954,7 +954,7 @@ mod tests {
assert_eq!(mail.get_content().unwrap(), "This is a test message"); assert_eq!(mail.get_content().unwrap(), "This is a test message");
mail.clear_content(); mail.clear_content();
assert!(mail.get_first_header("Content-Type").is_none()); assert!(mail.find_header("Content-Type").is_none());
assert_eq!(mail.get_content().unwrap(), ""); assert_eq!(mail.get_content().unwrap(), "");
mail.set_content("This is a new test message\n".to_string(), None); mail.set_content("This is a new test message\n".to_string(), None);
@@ -977,7 +977,7 @@ mod tests {
assert_eq!(ctype.mimetype, "multipart/alternative"); assert_eq!(ctype.mimetype, "multipart/alternative");
assert_eq!(ctype.params.get("boundary").unwrap(), &alt_boundary); assert_eq!(ctype.params.get("boundary").unwrap(), &alt_boundary);
assert_eq!( assert_eq!(
mail.get_first_header("Content-Type").unwrap().get_value(), mail.find_header("Content-Type").unwrap().get_value(),
format!("multipart/alternative; boundary=\"{}\"", alt_boundary) format!("multipart/alternative; boundary=\"{}\"", alt_boundary)
); );