simplify content type structs
This commit is contained in:
parent
a8ae9c3714
commit
5df9b253ec
10 changed files with 26 additions and 692 deletions
|
@ -43,41 +43,41 @@ pub fn parameter_list(input: &[u8]) -> IResult<&[u8], Vec<Parameter>> {
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
// Composite types
|
// Composite types
|
||||||
Multipart(MultipartDesc),
|
Multipart(Multipart),
|
||||||
Message(MessageSubtype),
|
Message(Message),
|
||||||
|
|
||||||
// Discrete types
|
// Discrete types
|
||||||
Text(TextDesc),
|
Text(Text),
|
||||||
Binary,
|
Binary,
|
||||||
}
|
}
|
||||||
impl Default for Type {
|
impl Default for Type {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Text(TextDesc::default())
|
Self::Text(Text::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> From<&'a NaiveType<'a>> for Type {
|
impl<'a> From<&'a NaiveType<'a>> for Type {
|
||||||
fn from(nt: &'a NaiveType<'a>) -> Self {
|
fn from(nt: &'a NaiveType<'a>) -> Self {
|
||||||
match nt.main.to_ascii_lowercase().as_slice() {
|
match nt.main.to_ascii_lowercase().as_slice() {
|
||||||
b"multipart" => MultipartDesc::try_from(nt).map(Self::Multipart).unwrap_or(Self::default()),
|
b"multipart" => Multipart::try_from(nt).map(Self::Multipart).unwrap_or(Self::default()),
|
||||||
b"message" => Self::Message(MessageSubtype::from(nt)),
|
b"message" => Self::Message(Message::from(nt)),
|
||||||
b"text" => Self::Text(TextDesc::from(nt)),
|
b"text" => Self::Text(Text::from(nt)),
|
||||||
_ => Self::Binary,
|
_ => Self::Binary,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct MultipartDesc {
|
pub struct Multipart {
|
||||||
pub subtype: MultipartSubtype,
|
pub subtype: MultipartSubtype,
|
||||||
pub boundary: String,
|
pub boundary: String,
|
||||||
}
|
}
|
||||||
impl<'a> TryFrom<&'a NaiveType<'a>> for MultipartDesc {
|
impl<'a> TryFrom<&'a NaiveType<'a>> for Multipart {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn try_from(nt: &'a NaiveType<'a>) -> Result<Self, Self::Error> {
|
fn try_from(nt: &'a NaiveType<'a>) -> Result<Self, Self::Error> {
|
||||||
nt.params.iter()
|
nt.params.iter()
|
||||||
.find(|x| x.name.to_ascii_lowercase().as_slice() == b"boundary")
|
.find(|x| x.name.to_ascii_lowercase().as_slice() == b"boundary")
|
||||||
.map(|boundary| MultipartDesc {
|
.map(|boundary| Multipart {
|
||||||
subtype: MultipartSubtype::from(nt),
|
subtype: MultipartSubtype::from(nt),
|
||||||
boundary: boundary.value.to_string(),
|
boundary: boundary.value.to_string(),
|
||||||
})
|
})
|
||||||
|
@ -108,13 +108,13 @@ impl<'a> From<&NaiveType<'a>> for MultipartSubtype {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum MessageSubtype {
|
pub enum Message {
|
||||||
RFC822,
|
RFC822,
|
||||||
Partial,
|
Partial,
|
||||||
External,
|
External,
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
impl<'a> From<&NaiveType<'a>> for MessageSubtype {
|
impl<'a> From<&NaiveType<'a>> for Message {
|
||||||
fn from(nt: &NaiveType<'a>) -> Self {
|
fn from(nt: &NaiveType<'a>) -> Self {
|
||||||
match nt.sub.to_ascii_lowercase().as_slice() {
|
match nt.sub.to_ascii_lowercase().as_slice() {
|
||||||
b"rfc822" => Self::RFC822,
|
b"rfc822" => Self::RFC822,
|
||||||
|
@ -126,13 +126,13 @@ impl<'a> From<&NaiveType<'a>> for MessageSubtype {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Default)]
|
#[derive(Debug, PartialEq, Default)]
|
||||||
pub struct TextDesc {
|
pub struct Text {
|
||||||
pub subtype: TextSubtype,
|
pub subtype: TextSubtype,
|
||||||
pub charset: EmailCharset,
|
pub charset: EmailCharset,
|
||||||
}
|
}
|
||||||
impl<'a> From<&NaiveType<'a>> for TextDesc {
|
impl<'a> From<&NaiveType<'a>> for Text {
|
||||||
fn from(nt: &NaiveType<'a>) -> Self {
|
fn from(nt: &NaiveType<'a>) -> Self {
|
||||||
TextDesc {
|
Self {
|
||||||
subtype: TextSubtype::from(nt),
|
subtype: TextSubtype::from(nt),
|
||||||
charset: nt.params.iter()
|
charset: nt.params.iter()
|
||||||
.find(|x| x.name.to_ascii_lowercase().as_slice() == b"charset")
|
.find(|x| x.name.to_ascii_lowercase().as_slice() == b"charset")
|
||||||
|
@ -189,7 +189,7 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
nt.to_type(),
|
nt.to_type(),
|
||||||
Type::Text(TextDesc {
|
Type::Text(Text {
|
||||||
charset: EmailCharset::UTF_8,
|
charset: EmailCharset::UTF_8,
|
||||||
subtype: TextSubtype::Plain,
|
subtype: TextSubtype::Plain,
|
||||||
})
|
})
|
||||||
|
@ -203,7 +203,7 @@ mod tests {
|
||||||
assert_eq!(rest, &[]);
|
assert_eq!(rest, &[]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
nt.to_type(),
|
nt.to_type(),
|
||||||
Type::Multipart(MultipartDesc {
|
Type::Multipart(Multipart {
|
||||||
subtype: MultipartSubtype::Mixed,
|
subtype: MultipartSubtype::Mixed,
|
||||||
boundary: "--==_mimepart_64a3f2c69114f_2a13d020975fe".into(),
|
boundary: "--==_mimepart_64a3f2c69114f_2a13d020975fe".into(),
|
||||||
})
|
})
|
||||||
|
@ -217,7 +217,7 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
nt.to_type(),
|
nt.to_type(),
|
||||||
Type::Message(MessageSubtype::RFC822),
|
Type::Message(Message::RFC822),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
use crate::fragments::part;
|
|
||||||
use crate::fragments::section::Section;
|
|
||||||
use crate::multipass::header_section;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub struct Parsed<'a> {
|
|
||||||
pub fields: Section<'a>,
|
|
||||||
pub body: part::PartNode<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new<'a>(p: &'a header_section::Parsed<'a>) -> Parsed<'a> {
|
|
||||||
todo!();
|
|
||||||
/*Parsed {
|
|
||||||
fields: p.fields,
|
|
||||||
body: p.body,
|
|
||||||
}*/
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
use nom::{
|
|
||||||
bytes::complete::is_not,
|
|
||||||
character::complete::space1,
|
|
||||||
combinator::{all_consuming, recognize},
|
|
||||||
multi::{many0, many1},
|
|
||||||
sequence::{pair, tuple},
|
|
||||||
IResult,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::error::IMFError;
|
|
||||||
use crate::fragments::fields;
|
|
||||||
use crate::multipass::field_lazy;
|
|
||||||
use crate::multipass::guess_charset;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub struct Parsed<'a> {
|
|
||||||
pub fields: Vec<&'a str>,
|
|
||||||
pub body: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new<'a>(gcha: &'a guess_charset::Parsed<'a>) -> Result<Parsed<'a>, IMFError<'a>> {
|
|
||||||
fields(&gcha.header)
|
|
||||||
.map_err(|e| IMFError::ExtractFields(e))
|
|
||||||
.map(|(_, fields)| Parsed {
|
|
||||||
fields,
|
|
||||||
body: gcha.body,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Parsed<'a> {
|
|
||||||
pub fn names(&'a self) -> field_lazy::Parsed<'a> {
|
|
||||||
field_lazy::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_extract() {
|
|
||||||
assert_eq!(
|
|
||||||
new(&guess_charset::Parsed {
|
|
||||||
header: "From: hello@world.com,\r\n\talice@wonderlands.com\r\nDate: 12 Mar 1997 07:33:25 Z\r\n".into(),
|
|
||||||
encoding: encoding_rs::UTF_8,
|
|
||||||
malformed: false,
|
|
||||||
body: b"Hello world!",
|
|
||||||
}),
|
|
||||||
Ok(Parsed {
|
|
||||||
fields: vec![
|
|
||||||
"From: hello@world.com,\r\n\talice@wonderlands.com\r\n",
|
|
||||||
"Date: 12 Mar 1997 07:33:25 Z\r\n",
|
|
||||||
],
|
|
||||||
body: b"Hello world!",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,336 +0,0 @@
|
||||||
use crate::fragments::eager;
|
|
||||||
use crate::multipass::field_lazy;
|
|
||||||
use crate::multipass::header_section;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub struct Parsed<'a> {
|
|
||||||
pub fields: Vec<eager::Field<'a>>,
|
|
||||||
pub body: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new<'a>(p: &'a field_lazy::Parsed<'a>) -> Parsed<'a> {
|
|
||||||
Parsed {
|
|
||||||
fields: p
|
|
||||||
.fields
|
|
||||||
.iter()
|
|
||||||
.filter_map(|entry| entry.try_into().ok())
|
|
||||||
.collect(),
|
|
||||||
body: p.body,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Parsed<'a> {
|
|
||||||
pub fn section(&'a self) -> header_section::Parsed<'a> {
|
|
||||||
header_section::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::fragments::lazy;
|
|
||||||
use crate::fragments::model;
|
|
||||||
use chrono::{FixedOffset, TimeZone};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_field_body() {
|
|
||||||
assert_eq!(
|
|
||||||
new(&field_lazy::Parsed {
|
|
||||||
fields: vec![
|
|
||||||
lazy::Field::From(lazy::MailboxList(
|
|
||||||
"hello@world.com,\r\n\talice@wonderlands.com\r\n"
|
|
||||||
)),
|
|
||||||
lazy::Field::Date(lazy::DateTime("12 Mar 1997 07:33:25 Z\r\n")),
|
|
||||||
],
|
|
||||||
body: b"Hello world!",
|
|
||||||
}),
|
|
||||||
Parsed {
|
|
||||||
fields: vec![
|
|
||||||
eager::Field::From(vec![
|
|
||||||
model::MailboxRef {
|
|
||||||
name: None,
|
|
||||||
addrspec: model::AddrSpec {
|
|
||||||
local_part: "hello".into(),
|
|
||||||
domain: "world.com".into()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
model::MailboxRef {
|
|
||||||
name: None,
|
|
||||||
addrspec: model::AddrSpec {
|
|
||||||
local_part: "alice".into(),
|
|
||||||
domain: "wonderlands.com".into()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
eager::Field::Date(
|
|
||||||
FixedOffset::east_opt(0)
|
|
||||||
.unwrap()
|
|
||||||
.with_ymd_and_hms(1997, 03, 12, 7, 33, 25)
|
|
||||||
.unwrap()
|
|
||||||
),
|
|
||||||
],
|
|
||||||
body: b"Hello world!",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
use crate::fragments::misc_token;
|
|
||||||
use crate::multipass::extract_fields;
|
|
||||||
fn lazy_eager<F>(input: &str, func: F)
|
|
||||||
where
|
|
||||||
F: Fn(&eager::Field),
|
|
||||||
{
|
|
||||||
let field = extract_fields::Parsed {
|
|
||||||
fields: vec![input],
|
|
||||||
body: b"",
|
|
||||||
};
|
|
||||||
let lazy = field_lazy::new(&field);
|
|
||||||
let eager = new(&lazy);
|
|
||||||
func(eager.fields.first().unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from() {
|
|
||||||
lazy_eager(
|
|
||||||
"From: \"Joe Q. Public\" <john.q.public@example.com>\r\n",
|
|
||||||
|from| {
|
|
||||||
assert_eq!(
|
|
||||||
from,
|
|
||||||
&eager::Field::From(vec![model::MailboxRef {
|
|
||||||
name: Some("Joe Q. Public".into()),
|
|
||||||
addrspec: model::AddrSpec {
|
|
||||||
local_part: "john.q.public".into(),
|
|
||||||
domain: "example.com".into(),
|
|
||||||
}
|
|
||||||
}])
|
|
||||||
)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_sender() {
|
|
||||||
lazy_eager(
|
|
||||||
"Sender: Michael Jones <mjones@machine.example>\r\n",
|
|
||||||
|sender| {
|
|
||||||
assert_eq!(
|
|
||||||
sender,
|
|
||||||
&eager::Field::Sender(model::MailboxRef {
|
|
||||||
name: Some("Michael Jones".into()),
|
|
||||||
addrspec: model::AddrSpec {
|
|
||||||
local_part: "mjones".into(),
|
|
||||||
domain: "machine.example".into(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_reply_to() {
|
|
||||||
lazy_eager(
|
|
||||||
"Reply-To: \"Mary Smith: Personal Account\" <smith@home.example>\r\n",
|
|
||||||
|reply_to| {
|
|
||||||
assert_eq!(
|
|
||||||
reply_to,
|
|
||||||
&eager::Field::ReplyTo(vec![model::AddressRef::Single(model::MailboxRef {
|
|
||||||
name: Some("Mary Smith: Personal Account".into()),
|
|
||||||
addrspec: model::AddrSpec {
|
|
||||||
local_part: "smith".into(),
|
|
||||||
domain: "home.example".into(),
|
|
||||||
},
|
|
||||||
})])
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_to() {
|
|
||||||
lazy_eager(
|
|
||||||
"To: A Group:Ed Jones <c@a.test>,joe@where.test,John <jdoe@one.test>;\r\n",
|
|
||||||
|to| {
|
|
||||||
assert_eq!(
|
|
||||||
to,
|
|
||||||
&eager::Field::To(vec![model::AddressRef::Many(model::GroupRef {
|
|
||||||
name: "A Group".into(),
|
|
||||||
participants: vec![
|
|
||||||
model::MailboxRef {
|
|
||||||
name: Some("Ed Jones".into()),
|
|
||||||
addrspec: model::AddrSpec {
|
|
||||||
local_part: "c".into(),
|
|
||||||
domain: "a.test".into()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
model::MailboxRef {
|
|
||||||
name: None,
|
|
||||||
addrspec: model::AddrSpec {
|
|
||||||
local_part: "joe".into(),
|
|
||||||
domain: "where.test".into()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
model::MailboxRef {
|
|
||||||
name: Some("John".into()),
|
|
||||||
addrspec: model::AddrSpec {
|
|
||||||
local_part: "jdoe".into(),
|
|
||||||
domain: "one.test".into()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
})])
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cc() {
|
|
||||||
lazy_eager("Cc: Undisclosed recipients:;\r\n", |cc| {
|
|
||||||
assert_eq!(
|
|
||||||
cc,
|
|
||||||
&eager::Field::Cc(vec![model::AddressRef::Many(model::GroupRef {
|
|
||||||
name: "Undisclosed recipients".into(),
|
|
||||||
participants: vec![],
|
|
||||||
})]),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_bcc() {
|
|
||||||
lazy_eager("Bcc: (empty)\r\n", |bcc| {
|
|
||||||
assert_eq!(bcc, &eager::Field::Bcc(vec![]),)
|
|
||||||
});
|
|
||||||
|
|
||||||
lazy_eager("Bcc: \r\n", |bcc| {
|
|
||||||
assert_eq!(bcc, &eager::Field::Bcc(vec![]),)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_message_id() {
|
|
||||||
lazy_eager("Message-ID: <310@[127.0.0.1]>\r\n", |msg_id| {
|
|
||||||
assert_eq!(
|
|
||||||
msg_id,
|
|
||||||
&eager::Field::MessageID(model::MessageId {
|
|
||||||
left: "310",
|
|
||||||
right: "127.0.0.1"
|
|
||||||
},)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_in_reply_to() {
|
|
||||||
lazy_eager("In-Reply-To: <a@b> <c@example.com>\r\n", |irt| {
|
|
||||||
assert_eq!(
|
|
||||||
irt,
|
|
||||||
&eager::Field::InReplyTo(vec![
|
|
||||||
model::MessageId {
|
|
||||||
left: "a",
|
|
||||||
right: "b"
|
|
||||||
},
|
|
||||||
model::MessageId {
|
|
||||||
left: "c",
|
|
||||||
right: "example.com"
|
|
||||||
},
|
|
||||||
])
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_references() {
|
|
||||||
lazy_eager(
|
|
||||||
"References: <1234@local.machine.example> <3456@example.net>\r\n",
|
|
||||||
|refer| {
|
|
||||||
assert_eq!(
|
|
||||||
refer,
|
|
||||||
&eager::Field::References(vec![
|
|
||||||
model::MessageId {
|
|
||||||
left: "1234",
|
|
||||||
right: "local.machine.example"
|
|
||||||
},
|
|
||||||
model::MessageId {
|
|
||||||
left: "3456",
|
|
||||||
right: "example.net"
|
|
||||||
},
|
|
||||||
])
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_subject() {
|
|
||||||
lazy_eager("Subject: Aérogramme\r\n", |subject| {
|
|
||||||
assert_eq!(
|
|
||||||
subject,
|
|
||||||
&eager::Field::Subject(misc_token::Unstructured("Aérogramme".into()))
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_comments() {
|
|
||||||
lazy_eager("Comments: 😛 easter egg!\r\n", |comments| {
|
|
||||||
assert_eq!(
|
|
||||||
comments,
|
|
||||||
&eager::Field::Comments(misc_token::Unstructured("😛 easter egg!".into())),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_keywords() {
|
|
||||||
lazy_eager(
|
|
||||||
"Keywords: fantasque, farfelu, fanfreluche\r\n",
|
|
||||||
|keywords| {
|
|
||||||
assert_eq!(
|
|
||||||
keywords,
|
|
||||||
&eager::Field::Keywords(misc_token::PhraseList(vec![
|
|
||||||
"fantasque".into(),
|
|
||||||
"farfelu".into(),
|
|
||||||
"fanfreluche".into()
|
|
||||||
]))
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
//@FIXME non ported tests:
|
|
||||||
|
|
||||||
/*
|
|
||||||
#[test]
|
|
||||||
fn test_invalid_field_name() {
|
|
||||||
assert!(known_field("Unknown: unknown\r\n").is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rescue_field() {
|
|
||||||
assert_eq!(
|
|
||||||
rescue_field("Héron: élan\r\n\tnoël: test\r\nFrom: ..."),
|
|
||||||
Ok(("From: ...", Field::Rescue("Héron: élan\r\n\tnoël: test"))),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_wrong_fields() {
|
|
||||||
let fullmail = r#"Return-Path: xoxo
|
|
||||||
From: !!!!
|
|
||||||
|
|
||||||
Hello world"#;
|
|
||||||
assert_eq!(
|
|
||||||
section(fullmail),
|
|
||||||
Ok(("Hello world", HeaderSection {
|
|
||||||
bad_fields: vec![
|
|
||||||
Field::ReturnPath(FieldBody::Failed("xoxo")),
|
|
||||||
Field::From(FieldBody::Failed("!!!!")),
|
|
||||||
],
|
|
||||||
..Default::default()
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
use crate::fragments::lazy;
|
|
||||||
use crate::multipass::extract_fields;
|
|
||||||
use crate::multipass::field_eager;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub struct Parsed<'a> {
|
|
||||||
pub fields: Vec<lazy::Field<'a>>,
|
|
||||||
pub body: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new<'a>(ef: &'a extract_fields::Parsed<'a>) -> Parsed<'a> {
|
|
||||||
Parsed {
|
|
||||||
fields: ef.fields.iter().map(|e| (*e).into()).collect(),
|
|
||||||
body: ef.body,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Parsed<'a> {
|
|
||||||
pub fn body(&'a self) -> field_eager::Parsed<'a> {
|
|
||||||
field_eager::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_field_name() {
|
|
||||||
assert_eq!(
|
|
||||||
new(&extract_fields::Parsed {
|
|
||||||
fields: vec![
|
|
||||||
"From: hello@world.com,\r\n\talice@wonderlands.com\r\n",
|
|
||||||
"Date: 12 Mar 1997 07:33:25 Z\r\n",
|
|
||||||
],
|
|
||||||
body: b"Hello world!",
|
|
||||||
}),
|
|
||||||
Parsed {
|
|
||||||
fields: vec![
|
|
||||||
lazy::Field::From(lazy::MailboxList(
|
|
||||||
"hello@world.com,\r\n\talice@wonderlands.com\r\n"
|
|
||||||
)),
|
|
||||||
lazy::Field::Date(lazy::DateTime("12 Mar 1997 07:33:25 Z\r\n")),
|
|
||||||
],
|
|
||||||
body: b"Hello world!",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mime_fields() {
|
|
||||||
assert_eq!(
|
|
||||||
new(&extract_fields::Parsed {
|
|
||||||
fields: vec![
|
|
||||||
"MIME-Version: 1.0 \r\n",
|
|
||||||
"Content-Type: multipart/alternative; boundary=\"bound\"\r\n",
|
|
||||||
"Content-Transfer-Encoding: 7bit\r\n",
|
|
||||||
"Content-ID: <foo4*foo1@bar.net>\r\n",
|
|
||||||
"Content-Description: hello world\r\n",
|
|
||||||
],
|
|
||||||
body: b"Hello world!",
|
|
||||||
}),
|
|
||||||
Parsed {
|
|
||||||
fields: vec![
|
|
||||||
lazy::Field::MIMEVersion(lazy::Version("1.0 \r\n")),
|
|
||||||
lazy::Field::MIME(lazy::MIMEField::ContentType(lazy::Type("multipart/alternative; boundary=\"bound\"\r\n"))),
|
|
||||||
lazy::Field::MIME(lazy::MIMEField::ContentTransferEncoding(lazy::Mechanism("7bit\r\n"))),
|
|
||||||
lazy::Field::MIME(lazy::MIMEField::ContentID(lazy::Identifier("<foo4*foo1@bar.net>\r\n"))),
|
|
||||||
lazy::Field::MIME(lazy::MIMEField::ContentDescription(lazy::Unstructured("hello world\r\n"))),
|
|
||||||
],
|
|
||||||
body: b"Hello world!",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use crate::error::IMFError;
|
|
||||||
use crate::fragments::encoding;
|
|
||||||
use crate::multipass::extract_fields;
|
|
||||||
use crate::multipass::segment;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub struct Parsed<'a> {
|
|
||||||
pub header: Cow<'a, str>,
|
|
||||||
pub body: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new<'a>(seg: &'a segment::Parsed<'a>) -> Parsed<'a> {
|
|
||||||
Parsed {
|
|
||||||
header: encoding::header_decode(&seg.header),
|
|
||||||
body: seg.body,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Parsed<'a> {
|
|
||||||
pub fn fields(&'a self) -> Result<extract_fields::Parsed<'a>, IMFError<'a>> {
|
|
||||||
extract_fields::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_charset() {
|
|
||||||
assert_eq!(
|
|
||||||
new(&segment::Parsed {
|
|
||||||
body: b"Hello world!",
|
|
||||||
header: b"From: hello@world.com\r\nDate: 12 Mar 1997 07:33:25 Z\r\n",
|
|
||||||
}),
|
|
||||||
Parsed {
|
|
||||||
header: "From: hello@world.com\r\nDate: 12 Mar 1997 07:33:25 Z\r\n".into(),
|
|
||||||
encoding: encoding_rs::UTF_8,
|
|
||||||
malformed: false,
|
|
||||||
body: b"Hello world!",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
use crate::fragments::section::Section;
|
|
||||||
use crate::multipass::{field_eager, body_structure};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub struct Parsed<'a> {
|
|
||||||
pub fields: Section<'a>,
|
|
||||||
pub body: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new<'a>(p: &'a field_eager::Parsed<'a>) -> Parsed<'a> {
|
|
||||||
Parsed {
|
|
||||||
fields: Section::from_iter(p.fields.iter()),
|
|
||||||
body: p.body,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Parsed<'a> {
|
|
||||||
pub fn body_structure(&self) -> body_structure::Parsed<'a> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::fragments::eager;
|
|
||||||
use crate::fragments::model;
|
|
||||||
use chrono::{FixedOffset, TimeZone};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_section() {
|
|
||||||
assert_eq!(
|
|
||||||
new(&field_eager::Parsed {
|
|
||||||
fields: vec![
|
|
||||||
eager::Field::From(vec![
|
|
||||||
model::MailboxRef {
|
|
||||||
name: None,
|
|
||||||
addrspec: model::AddrSpec {
|
|
||||||
local_part: "hello".into(),
|
|
||||||
domain: "world.com".into()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
model::MailboxRef {
|
|
||||||
name: None,
|
|
||||||
addrspec: model::AddrSpec {
|
|
||||||
local_part: "alice".into(),
|
|
||||||
domain: "wonderlands.com".into()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
eager::Field::Date(
|
|
||||||
FixedOffset::east_opt(0)
|
|
||||||
.unwrap()
|
|
||||||
.with_ymd_and_hms(1997, 03, 12, 7, 33, 25)
|
|
||||||
.unwrap()
|
|
||||||
),
|
|
||||||
],
|
|
||||||
body: b"Hello world!",
|
|
||||||
}),
|
|
||||||
Parsed {
|
|
||||||
fields: Section {
|
|
||||||
from: vec![
|
|
||||||
&model::MailboxRef {
|
|
||||||
name: None,
|
|
||||||
addrspec: model::AddrSpec {
|
|
||||||
local_part: "hello".into(),
|
|
||||||
domain: "world.com".into()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
&model::MailboxRef {
|
|
||||||
name: None,
|
|
||||||
addrspec: model::AddrSpec {
|
|
||||||
local_part: "alice".into(),
|
|
||||||
domain: "wonderlands.com".into()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
date: Some(
|
|
||||||
&FixedOffset::east_opt(0)
|
|
||||||
.unwrap()
|
|
||||||
.with_ymd_and_hms(1997, 03, 12, 7, 33, 25)
|
|
||||||
.unwrap()
|
|
||||||
),
|
|
||||||
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
body: b"Hello world!",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
pub mod extract_fields;
|
|
||||||
pub mod field_eager;
|
|
||||||
pub mod field_lazy;
|
|
||||||
pub mod guess_charset;
|
|
||||||
pub mod header_section;
|
|
||||||
pub mod segment;
|
|
||||||
pub mod body_structure;
|
|
|
@ -1,37 +0,0 @@
|
||||||
use crate::error::IMFError;
|
|
||||||
use crate::multipass::guess_charset;
|
|
||||||
use crate::fragments::whitespace::headers;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub struct Parsed<'a> {
|
|
||||||
pub header: &'a [u8],
|
|
||||||
pub body: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new<'a>(buffer: &'a [u8]) -> Result<Parsed<'a>, IMFError<'a>> {
|
|
||||||
headers(buffer)
|
|
||||||
.map_err(|e| IMFError::Segment(e))
|
|
||||||
.map(|(body, header)| Parsed { header, body })
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Parsed<'a> {
|
|
||||||
pub fn charset(&'a self) -> guess_charset::Parsed<'a> {
|
|
||||||
guess_charset::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_segment() {
|
|
||||||
assert_eq!(
|
|
||||||
new(&b"From: hello@world.com\r\nDate: 12 Mar 1997 07:33:25 Z\r\n\r\nHello world!"[..]),
|
|
||||||
Ok(Parsed {
|
|
||||||
header: b"From: hello@world.com\r\nDate: 12 Mar 1997 07:33:25 Z\r\n",
|
|
||||||
body: b"Hello world!",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,12 +7,14 @@ use nom::{
|
||||||
combinator::{not, opt, recognize},
|
combinator::{not, opt, recognize},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::fragments::mime::{Mechanism, Type};
|
|
||||||
use crate::fragments::model::MessageId;
|
pub struct Part<'a, T> {
|
||||||
use crate::fragments::misc_token::Unstructured;
|
|
||||||
use crate::fragments::whitespace::{CRLF, headers, line, obs_crlf};
|
}
|
||||||
use crate::fragments::{eager,lazy};
|
|
||||||
use crate::fragments::section::MIMESection;
|
impl<'a> Part<'a, r#type::Text<'a>> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue