Use trait methods instead of MailMessage

This commit is contained in:
Andrey Golovizin 2021-07-18 16:05:10 +02:00
parent bffb26c54a
commit 202736e0bd
2 changed files with 76 additions and 59 deletions

View file

@ -23,12 +23,15 @@ fn main() -> Result<(), anyhow::Error> {
result result
} }
}; };
let message = mailparse::parse_mail(&input)?.parse()?; let message = mailparse::parse_mail(&input)?;
println!("Subject: {}", message.subject.unwrap_or_default()); println!("Subject: {}", message.subject().unwrap_or_default());
if let Some(date) = message.date { if let Some(date) = message.date()? {
println!("Date: {}", date); println!("Date: {}", date);
} }
println!("---"); println!("---");
print!("{}", message.body.unwrap_or_default()); print!(
"{}",
message.body()?.unwrap_or_default().text.unwrap_or_default()
);
Ok(()) Ok(())
} }

View file

@ -1,16 +1,15 @@
use mailparse::{DispositionType, MailHeaderMap, ParsedMail}; use mailparse::{DispositionType, MailHeaderMap, ParsedMail};
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug, Default)]
pub struct MailMessage { pub struct Body {
pub subject: Option<String>, pub text: Option<String>,
pub date: Option<chrono::DateTime<chrono::FixedOffset>>,
pub body: Option<String>,
} }
pub trait ParsedMailExt { pub trait ParsedMailExt {
fn is_attachment(&self) -> bool; fn is_attachment(&self) -> bool;
fn get_body_text(&self) -> Result<Option<String>, anyhow::Error>; fn subject(&self) -> Option<String>;
fn parse(&self) -> Result<MailMessage, anyhow::Error>; fn date(&self) -> Result<Option<chrono::DateTime<chrono::FixedOffset>>, anyhow::Error>;
fn body(&self) -> Result<Option<Body>, anyhow::Error>;
} }
impl<'a> ParsedMailExt for ParsedMail<'a> { impl<'a> ParsedMailExt for ParsedMail<'a> {
@ -18,37 +17,34 @@ impl<'a> ParsedMailExt for ParsedMail<'a> {
self.get_content_disposition().disposition == DispositionType::Attachment self.get_content_disposition().disposition == DispositionType::Attachment
} }
fn get_body_text(&self) -> Result<Option<String>, anyhow::Error> { fn subject(&self) -> Option<String> {
let mimetype: mime::Mime = self.ctype.mimetype.parse()?; self.headers.get_first_value("Subject")
if self.is_attachment() {
return Ok(None);
}
if mimetype == mime::TEXT_PLAIN {
return Ok(Some(self.get_body()?));
}
for subpart in &self.subparts {
if let Some(body) = subpart.get_body_text()? {
return Ok(Some(body));
}
}
Ok(None)
} }
fn date(&self) -> Result<Option<chrono::DateTime<chrono::FixedOffset>>, anyhow::Error> {
fn parse(&self) -> Result<MailMessage, anyhow::Error> {
let subject = self.headers.get_first_value("Subject");
let date = self let date = self
.headers .headers
.get_first_value("Date") .get_first_value("Date")
.map(|s| chrono::DateTime::parse_from_rfc2822(&s)) .map(|s| chrono::DateTime::parse_from_rfc2822(&s))
.transpose()?; .transpose()?;
Ok(date)
}
let body = self.get_body_text()?; fn body(&self) -> Result<Option<Body>, anyhow::Error> {
let message = MailMessage { let mimetype: mime::Mime = self.ctype.mimetype.parse()?;
subject, if self.is_attachment() {
date, return Ok(None);
body, }
}; if mimetype == mime::TEXT_PLAIN {
Ok(message) return Ok(Some(Body {
text: Some(self.get_body()?),
}));
}
for subpart in &self.subparts {
if let Some(body) = subpart.body()? {
return Ok(Some(body));
}
}
Ok(None)
} }
} }
@ -151,49 +147,67 @@ TsSbamFrw6EgcMWZw61sb2hhLgo=
#[test] #[test]
fn test_plain_text_body() -> Result<(), anyhow::Error> { fn test_plain_text_body() -> Result<(), anyhow::Error> {
let message = mailparse::parse_mail(TEXT_PLAIN.as_bytes())?.parse()?; let message = mailparse::parse_mail(TEXT_PLAIN.as_bytes())?;
let expected_message = MailMessage { assert_eq!(message.subject(), Some("Plain text body".to_string()));
subject: Some("Plain text body".to_string()), assert_eq!(
date: Some( message.date()?,
Some(
chrono::FixedOffset::east(3600 * 2) chrono::FixedOffset::east(3600 * 2)
.ymd(2021, 7, 17) .ymd(2021, 7, 17)
.and_hms(18, 4, 10), .and_hms(18, 4, 10),
), )
body: Some("Prostý text.".to_string()), );
}; assert_eq!(
assert_eq!(message, expected_message); message.body()?,
Some(Body {
text: Some("Prostý text.".to_string())
}),
);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_multipart_alternative_text_body() -> Result<(), anyhow::Error> { fn test_multipart_alternative_text_body() -> Result<(), anyhow::Error> {
let message = mailparse::parse_mail(MULTIPART_ALTERNATIVE.as_bytes())?.parse()?; let message = mailparse::parse_mail(MULTIPART_ALTERNATIVE.as_bytes())?;
let expected_message = MailMessage { assert_eq!(message.subject(), Some("Plain text with HTML".to_string()));
subject: Some("Plain text with HTML".to_string()), assert_eq!(
date: Some( message.date()?,
Some(
chrono::FixedOffset::east(3600 * 2) chrono::FixedOffset::east(3600 * 2)
.ymd(2021, 7, 17) .ymd(2021, 7, 17)
.and_hms(18, 13, 26), .and_hms(18, 13, 26),
), )
body: Some("Prostý text.".to_string()), );
}; assert_eq!(
assert_eq!(message, expected_message); message.body()?,
Some(Body {
text: Some("Prostý text.".to_string())
}),
);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_multipart_mixed_alternative_text_body() -> Result<(), anyhow::Error> { fn test_multipart_mixed_alternative_text_body() -> Result<(), anyhow::Error> {
let message = mailparse::parse_mail(MULTIPART_MIXED_ALTERNATIVE.as_bytes())?.parse()?; let message = mailparse::parse_mail(MULTIPART_MIXED_ALTERNATIVE.as_bytes())?;
let expected_message = MailMessage { assert_eq!(
subject: Some("Plain text with HTML and attachment".to_string()), message.subject(),
date: Some( Some("Plain text with HTML and attachment".to_string())
);
assert_eq!(
message.date()?,
Some(
chrono::FixedOffset::east(3600 * 2) chrono::FixedOffset::east(3600 * 2)
.ymd(2021, 7, 17) .ymd(2021, 7, 17)
.and_hms(21, 39, 8), .and_hms(21, 39, 8),
), )
body: Some("Prostý text.".to_string()), );
}; assert_eq!(
assert_eq!(message, expected_message); message.body()?,
Some(Body {
text: Some("Prostý text.".to_string())
}),
);
Ok(()) Ok(())
} }
} }