mirror of
https://gitlab.com/mailcat-devs/mailcat.git
synced 2025-12-20 13:23:26 +01:00
Introduce MailMessage
This commit is contained in:
parent
58b3a1fe74
commit
bffb26c54a
2 changed files with 69 additions and 34 deletions
30
src/main.rs
30
src/main.rs
|
|
@ -2,9 +2,7 @@ mod parsing;
|
||||||
|
|
||||||
use parsing::ParsedMailExt;
|
use parsing::ParsedMailExt;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use clap::Clap;
|
use clap::Clap;
|
||||||
use mailparse::MailHeaderMap;
|
|
||||||
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
|
|
@ -17,7 +15,6 @@ struct Opts {
|
||||||
|
|
||||||
fn main() -> Result<(), anyhow::Error> {
|
fn main() -> Result<(), anyhow::Error> {
|
||||||
let opts = Opts::parse();
|
let opts = Opts::parse();
|
||||||
dbg!(&opts);
|
|
||||||
let input = match opts.filename {
|
let input = match opts.filename {
|
||||||
Some(filename) => std::fs::read(filename)?,
|
Some(filename) => std::fs::read(filename)?,
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -26,27 +23,12 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let message = mailparse::parse_mail(&input)?;
|
let message = mailparse::parse_mail(&input)?.parse()?;
|
||||||
println!(
|
println!("Subject: {}", message.subject.unwrap_or_default());
|
||||||
"Subject: {}",
|
if let Some(date) = message.date {
|
||||||
message
|
println!("Date: {}", date);
|
||||||
.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();
|
|
||||||
println!("---");
|
println!("---");
|
||||||
print!("{}", body);
|
print!("{}", message.body.unwrap_or_default());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,16 @@
|
||||||
use mailparse::{DispositionType, ParsedMail};
|
use mailparse::{DispositionType, MailHeaderMap, ParsedMail};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
pub struct MailMessage {
|
||||||
|
pub subject: 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 get_body_text(&self) -> Result<Option<String>, anyhow::Error>;
|
||||||
|
fn parse(&self) -> Result<MailMessage, anyhow::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ParsedMailExt for ParsedMail<'a> {
|
impl<'a> ParsedMailExt for ParsedMail<'a> {
|
||||||
|
|
@ -26,10 +33,29 @@ impl<'a> ParsedMailExt for ParsedMail<'a> {
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse(&self) -> Result<MailMessage, anyhow::Error> {
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use chrono::TimeZone;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const TEXT_PLAIN: &str = r#"From: Andrey Golovizin <ag@sologoc.com>
|
const TEXT_PLAIN: &str = r#"From: Andrey Golovizin <ag@sologoc.com>
|
||||||
|
|
@ -45,7 +71,7 @@ Prost=FD text.
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
const MULTIPART_ALTERNATIVE: &str = r#"From: Andrey Golovizin <ag@sologoc.com>
|
const MULTIPART_ALTERNATIVE: &str = r#"From: Andrey Golovizin <ag@sologoc.com>
|
||||||
Subject: Plain text with html
|
Subject: Plain text with HTML
|
||||||
Date: Sat, 17 Jul 2021 18:13:26 +0200
|
Date: Sat, 17 Jul 2021 18:13:26 +0200
|
||||||
Message-ID: <3609140.kQq0lBPeGt@sakuragaoka>
|
Message-ID: <3609140.kQq0lBPeGt@sakuragaoka>
|
||||||
MIME-Version: 1.0
|
MIME-Version: 1.0
|
||||||
|
|
@ -76,7 +102,7 @@ Content-Type: text/html; charset="UTF-8"
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
const MULTIPART_MIXED_ALTERNATIVE: &str = r#"From: Andrey Golovizin <ag@sologoc.com>
|
const MULTIPART_MIXED_ALTERNATIVE: &str = r#"From: Andrey Golovizin <ag@sologoc.com>
|
||||||
Subject: Plain text with html and attachment
|
Subject: Plain text with HTML and attachment
|
||||||
Date: Sat, 17 Jul 2021 21:39:08 +0200
|
Date: Sat, 17 Jul 2021 21:39:08 +0200
|
||||||
Message-ID: <3054314.5fSG56mABF@sakuragaoka>
|
Message-ID: <3054314.5fSG56mABF@sakuragaoka>
|
||||||
MIME-Version: 1.0
|
MIME-Version: 1.0
|
||||||
|
|
@ -125,22 +151,49 @@ 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())?;
|
let message = mailparse::parse_mail(TEXT_PLAIN.as_bytes())?.parse()?;
|
||||||
assert_eq!(message.get_body_text()?.unwrap_or_default(), "Prostý text.");
|
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(())
|
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())?;
|
let message = mailparse::parse_mail(MULTIPART_ALTERNATIVE.as_bytes())?.parse()?;
|
||||||
assert_eq!(message.get_body_text()?.unwrap_or_default(), "Prostý text.");
|
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(())
|
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())?;
|
let message = mailparse::parse_mail(MULTIPART_MIXED_ALTERNATIVE.as_bytes())?.parse()?;
|
||||||
assert_eq!(message.get_body_text()?.unwrap_or_default(), "Prostý text.");
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue