diff --git a/src/main.rs b/src/main.rs index 87e7290..6472fde 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,9 +2,7 @@ mod parsing; use parsing::ParsedMailExt; -use anyhow::anyhow; use clap::Clap; -use mailparse::MailHeaderMap; use std::io::Read; @@ -17,7 +15,6 @@ struct Opts { fn main() -> Result<(), anyhow::Error> { let opts = Opts::parse(); - dbg!(&opts); let input = match opts.filename { Some(filename) => std::fs::read(filename)?, None => { @@ -26,27 +23,12 @@ fn main() -> Result<(), anyhow::Error> { result } }; - let message = mailparse::parse_mail(&input)?; - println!( - "Subject: {}", - message - .headers - .get_first_value("Subject") - .unwrap_or_default() - ); - - let date_str = &message - .headers - .get_first_value("Date") - .ok_or_else(|| anyhow!("No date"))?; - dbg!(&date_str); - let date = chrono::DateTime::parse_from_rfc2822(&date_str)?; - println!("Date: {}", date); - dbg!(&message.ctype); - dbg!(&message.get_content_disposition()); - // - let body = message.get_body_text()?.unwrap_or_default(); + let message = mailparse::parse_mail(&input)?.parse()?; + println!("Subject: {}", message.subject.unwrap_or_default()); + if let Some(date) = message.date { + println!("Date: {}", date); + } println!("---"); - print!("{}", body); + print!("{}", message.body.unwrap_or_default()); Ok(()) } diff --git a/src/parsing.rs b/src/parsing.rs index dfec2f7..e21abb7 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -1,9 +1,16 @@ -use mailparse::{DispositionType, ParsedMail}; +use mailparse::{DispositionType, MailHeaderMap, ParsedMail}; + +#[derive(PartialEq, Eq, Debug)] +pub struct MailMessage { + pub subject: Option, + pub date: Option>, + pub body: Option, +} pub trait ParsedMailExt { fn is_attachment(&self) -> bool; - fn get_body_text(&self) -> Result, anyhow::Error>; + fn parse(&self) -> Result; } impl<'a> ParsedMailExt for ParsedMail<'a> { @@ -26,10 +33,29 @@ impl<'a> ParsedMailExt for ParsedMail<'a> { } Ok(None) } + + fn parse(&self) -> Result { + let subject = self.headers.get_first_value("Subject"); + let date = self + .headers + .get_first_value("Date") + .map(|s| chrono::DateTime::parse_from_rfc2822(&s)) + .transpose()?; + + let body = self.get_body_text()?; + let message = MailMessage { + subject, + date, + body, + }; + Ok(message) + } } #[cfg(test)] mod test { + use chrono::TimeZone; + use super::*; const TEXT_PLAIN: &str = r#"From: Andrey Golovizin @@ -45,7 +71,7 @@ Prost=FD text. "#; const MULTIPART_ALTERNATIVE: &str = r#"From: Andrey Golovizin -Subject: Plain text with html +Subject: Plain text with HTML Date: Sat, 17 Jul 2021 18:13:26 +0200 Message-ID: <3609140.kQq0lBPeGt@sakuragaoka> MIME-Version: 1.0 @@ -76,7 +102,7 @@ Content-Type: text/html; charset="UTF-8" "#; const MULTIPART_MIXED_ALTERNATIVE: &str = r#"From: Andrey Golovizin -Subject: Plain text with html and attachment +Subject: Plain text with HTML and attachment Date: Sat, 17 Jul 2021 21:39:08 +0200 Message-ID: <3054314.5fSG56mABF@sakuragaoka> MIME-Version: 1.0 @@ -125,22 +151,49 @@ TsSbamFrw6EgcMWZw61sb2hhLgo= #[test] fn test_plain_text_body() -> Result<(), anyhow::Error> { - let message = mailparse::parse_mail(TEXT_PLAIN.as_bytes())?; - assert_eq!(message.get_body_text()?.unwrap_or_default(), "Prostý text."); + let message = mailparse::parse_mail(TEXT_PLAIN.as_bytes())?.parse()?; + let expected_message = MailMessage { + subject: Some("Plain text body".to_string()), + date: Some( + chrono::FixedOffset::east(3600 * 2) + .ymd(2021, 7, 17) + .and_hms(18, 4, 10), + ), + body: Some("Prostý text.".to_string()), + }; + assert_eq!(message, expected_message); Ok(()) } #[test] fn test_multipart_alternative_text_body() -> Result<(), anyhow::Error> { - let message = mailparse::parse_mail(MULTIPART_ALTERNATIVE.as_bytes())?; - assert_eq!(message.get_body_text()?.unwrap_or_default(), "Prostý text."); + let message = mailparse::parse_mail(MULTIPART_ALTERNATIVE.as_bytes())?.parse()?; + let expected_message = MailMessage { + subject: Some("Plain text with HTML".to_string()), + date: Some( + chrono::FixedOffset::east(3600 * 2) + .ymd(2021, 7, 17) + .and_hms(18, 13, 26), + ), + body: Some("Prostý text.".to_string()), + }; + assert_eq!(message, expected_message); Ok(()) } #[test] fn test_multipart_mixed_alternative_text_body() -> Result<(), anyhow::Error> { - let message = mailparse::parse_mail(MULTIPART_MIXED_ALTERNATIVE.as_bytes())?; - assert_eq!(message.get_body_text()?.unwrap_or_default(), "Prostý text."); + let message = mailparse::parse_mail(MULTIPART_MIXED_ALTERNATIVE.as_bytes())?.parse()?; + let expected_message = MailMessage { + subject: Some("Plain text with HTML and attachment".to_string()), + date: Some( + chrono::FixedOffset::east(3600 * 2) + .ymd(2021, 7, 17) + .and_hms(21, 39, 8), + ), + body: Some("Prostý text.".to_string()), + }; + assert_eq!(message, expected_message); Ok(()) } }