refactor mime

This commit is contained in:
Quentin 2023-07-24 13:53:59 +02:00
parent 16006781d0
commit 40ab7e33b6
Signed by: quentin
GPG key ID: E9602264D639FF68
9 changed files with 96 additions and 98 deletions

View file

@ -101,7 +101,7 @@ IANA
## State of the art / alternatives ## State of the art / alternatives
*The following review is not an objective, neutral, impartial review. Instead, it's a temptative *The following review is not an objective, neutral, impartial review. Instead, it's a temptative
to explain why I wrote this to explain why I wrote this library. If you find something outdated or objectively wrong, feel free to open a PR or an issue to fix it.*
`stalwartlab/mail_parser` `stalwartlab/mail_parser`
@ -115,7 +115,6 @@ to explain why I wrote this
## License ## License
```
eml-codec eml-codec
Copyright (C) The eml-codec Contributors Copyright (C) The eml-codec Contributors
@ -131,4 +130,3 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
```

View file

@ -1,14 +1,14 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
mod error; pub mod error;
mod header; mod header;
mod imf; pub mod imf;
mod mime; pub mod mime;
mod part; pub mod part;
mod text; pub mod text;
pub fn email(input: &[u8]) -> Result<part::composite::Message, error::EMLError> { pub fn email(input: &[u8]) -> Result<part::composite::Message, error::EMLError> {
part::composite::message(mime::mime::Message::default())(input) part::composite::message(mime::Message::default())(input)
.map(|(_, v)| v) .map(|(_, v)| v)
.map_err(error::EMLError::ParseError) .map_err(error::EMLError::ParseError)
} }

View file

@ -8,7 +8,7 @@ use nom::{
use crate::header::{field_name, CompFieldList}; use crate::header::{field_name, CompFieldList};
use crate::imf::identification::{msg_id, MessageID}; use crate::imf::identification::{msg_id, MessageID};
use crate::mime::mechanism::{mechanism, Mechanism}; use crate::mime::mechanism::{mechanism, Mechanism};
use crate::mime::mime::AnyMIME; use crate::mime::AnyMIME;
use crate::mime::r#type::{naive_type, NaiveType}; use crate::mime::r#type::{naive_type, NaiveType};
use crate::text::misc_token::{unstructured, Unstructured}; use crate::text::misc_token::{unstructured, Unstructured};
use crate::text::whitespace::obs_crlf; use crate::text::whitespace::obs_crlf;

View file

@ -1,62 +0,0 @@
use crate::imf::identification::MessageID;
use crate::mime::field::Content;
use crate::mime::mechanism::Mechanism;
use crate::mime::r#type::{self as ctype, AnyType};
use crate::text::misc_token::Unstructured; //Multipart, Message, Text, Binary};
#[derive(Debug, PartialEq, Clone)]
pub struct Multipart<'a>(pub ctype::Multipart, pub Generic<'a>);
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Message<'a>(pub ctype::Message, pub Generic<'a>);
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Text<'a>(pub ctype::Text, pub Generic<'a>);
#[derive(Debug, PartialEq, Clone)]
pub struct Binary<'a>(pub ctype::Binary, pub Generic<'a>);
#[derive(Debug, PartialEq, Clone)]
pub enum AnyMIME<'a> {
Mult(Multipart<'a>),
Msg(Message<'a>),
Txt(Text<'a>),
Bin(Binary<'a>),
}
impl<'a> AnyMIME<'a> {
pub fn from_pair(at: AnyType, gen: Generic<'a>) -> Self {
match at {
AnyType::Multipart(m) => AnyMIME::Mult(Multipart(m, gen)),
AnyType::Message(m) => AnyMIME::Msg(Message(m, gen)),
AnyType::Text(m) => AnyMIME::Txt(Text(m, gen)),
AnyType::Binary(m) => AnyMIME::Bin(Binary(m, gen)),
}
}
}
impl<'a> FromIterator<Content<'a>> for AnyMIME<'a> {
fn from_iter<I: IntoIterator<Item = Content<'a>>>(it: I) -> Self {
let (at, gen) = it.into_iter().fold(
(AnyType::default(), Generic::default()),
|(mut at, mut section), field| {
match field {
Content::Type(v) => at = v.to_type(),
Content::TransferEncoding(v) => section.transfer_encoding = v,
Content::ID(v) => section.id = Some(v),
Content::Description(v) => section.description = Some(v),
};
(at, section)
},
);
Self::from_pair(at, gen)
}
}
#[derive(Debug, PartialEq, Default, Clone)]
pub struct Generic<'a> {
pub transfer_encoding: Mechanism<'a>,
pub id: Option<MessageID<'a>>,
pub description: Option<Unstructured<'a>>,
}

View file

@ -1,5 +1,67 @@
pub mod charset; pub mod charset;
pub mod field; pub mod field;
pub mod mechanism; pub mod mechanism;
pub mod mime;
pub mod r#type; pub mod r#type;
use crate::imf::identification::MessageID;
use crate::mime::field::Content;
use crate::mime::mechanism::Mechanism;
use crate::mime::r#type::{self as ctype, AnyType};
use crate::text::misc_token::Unstructured; //Multipart, Message, Text, Binary};
#[derive(Debug, PartialEq, Clone)]
pub struct Multipart<'a>(pub ctype::Multipart, pub Generic<'a>);
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Message<'a>(pub ctype::Message, pub Generic<'a>);
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Text<'a>(pub ctype::Text, pub Generic<'a>);
#[derive(Debug, PartialEq, Clone)]
pub struct Binary<'a>(pub ctype::Binary, pub Generic<'a>);
#[derive(Debug, PartialEq, Clone)]
pub enum AnyMIME<'a> {
Mult(Multipart<'a>),
Msg(Message<'a>),
Txt(Text<'a>),
Bin(Binary<'a>),
}
impl<'a> AnyMIME<'a> {
pub fn from_pair(at: AnyType, gen: Generic<'a>) -> Self {
match at {
AnyType::Multipart(m) => AnyMIME::Mult(Multipart(m, gen)),
AnyType::Message(m) => AnyMIME::Msg(Message(m, gen)),
AnyType::Text(m) => AnyMIME::Txt(Text(m, gen)),
AnyType::Binary(m) => AnyMIME::Bin(Binary(m, gen)),
}
}
}
impl<'a> FromIterator<Content<'a>> for AnyMIME<'a> {
fn from_iter<I: IntoIterator<Item = Content<'a>>>(it: I) -> Self {
let (at, gen) = it.into_iter().fold(
(AnyType::default(), Generic::default()),
|(mut at, mut section), field| {
match field {
Content::Type(v) => at = v.to_type(),
Content::TransferEncoding(v) => section.transfer_encoding = v,
Content::ID(v) => section.id = Some(v),
Content::Description(v) => section.description = Some(v),
};
(at, section)
},
);
Self::from_pair(at, gen)
}
}
#[derive(Debug, PartialEq, Default, Clone)]
pub struct Generic<'a> {
pub transfer_encoding: Mechanism<'a>,
pub id: Option<MessageID<'a>>,
pub description: Option<Unstructured<'a>>,
}

View file

@ -9,12 +9,12 @@ use crate::text::boundary::{boundary, Delimiter};
//--- Multipart //--- Multipart
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Multipart<'a> { pub struct Multipart<'a> {
pub interpreted: mime::mime::Multipart<'a>, pub interpreted: mime::Multipart<'a>,
pub children: Vec<AnyPart<'a>>, pub children: Vec<AnyPart<'a>>,
} }
pub fn multipart<'a>( pub fn multipart<'a>(
m: mime::mime::Multipart<'a>, m: mime::Multipart<'a>,
) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Multipart<'a>> { ) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Multipart<'a>> {
let m = m.clone(); let m = m.clone();
@ -64,13 +64,13 @@ pub fn multipart<'a>(
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Message<'a> { pub struct Message<'a> {
pub interpreted: mime::mime::Message<'a>, pub interpreted: mime::Message<'a>,
pub imf: imf::Imf<'a>, pub imf: imf::Imf<'a>,
pub child: Box<AnyPart<'a>>, pub child: Box<AnyPart<'a>>,
} }
pub fn message<'a>( pub fn message<'a>(
m: mime::mime::Message<'a>, m: mime::Message<'a>,
) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Message<'a>> { ) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Message<'a>> {
move |input: &[u8]| { move |input: &[u8]| {
let (input, fields): (_, CompFieldList<part::field::MixedField>) = let (input, fields): (_, CompFieldList<part::field::MixedField>) =
@ -101,12 +101,12 @@ mod tests {
#[test] #[test]
fn test_multipart() { fn test_multipart() {
let base_mime = mime::mime::Multipart( let base_mime = mime::Multipart(
mime::r#type::Multipart { mime::r#type::Multipart {
subtype: mime::r#type::MultipartSubtype::Alternative, subtype: mime::r#type::MultipartSubtype::Alternative,
boundary: "simple boundary".to_string(), boundary: "simple boundary".to_string(),
}, },
mime::mime::Generic::default(), mime::Generic::default(),
); );
assert_eq!( assert_eq!(
@ -133,22 +133,22 @@ This is the epilogue. It is also to be ignored.
interpreted: base_mime, interpreted: base_mime,
children: vec![ children: vec![
AnyPart::Txt(Text { AnyPart::Txt(Text {
interpreted: mime::mime::Text( interpreted: mime::Text(
mime::r#type::Text { mime::r#type::Text {
subtype: mime::r#type::TextSubtype::Plain, subtype: mime::r#type::TextSubtype::Plain,
charset: mime::charset::EmailCharset::US_ASCII, charset: mime::charset::EmailCharset::US_ASCII,
}, },
mime::mime::Generic::default(), mime::Generic::default(),
), ),
body: &b"This is implicitly typed plain US-ASCII text.\nIt does NOT end with a linebreak."[..], body: &b"This is implicitly typed plain US-ASCII text.\nIt does NOT end with a linebreak."[..],
}), }),
AnyPart::Txt(Text { AnyPart::Txt(Text {
interpreted: mime::mime::Text( interpreted: mime::Text(
mime::r#type::Text { mime::r#type::Text {
subtype: mime::r#type::TextSubtype::Plain, subtype: mime::r#type::TextSubtype::Plain,
charset: mime::charset::EmailCharset::US_ASCII, charset: mime::charset::EmailCharset::US_ASCII,
}, },
mime::mime::Generic::default(), mime::Generic::default(),
), ),
body: &b"This is explicitly typed plain US-ASCII text.\nIt DOES end with a linebreak.\n"[..], body: &b"This is explicitly typed plain US-ASCII text.\nIt DOES end with a linebreak.\n"[..],
}), }),
@ -205,7 +205,7 @@ OoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO<br />
"# "#
.as_bytes(); .as_bytes();
let base_mime = mime::mime::Message::default(); let base_mime = mime::Message::default();
assert_eq!( assert_eq!(
message(base_mime.clone())(fullmail), message(base_mime.clone())(fullmail),
Ok(( Ok((
@ -278,34 +278,34 @@ OoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO<br />
..imf::Imf::default() ..imf::Imf::default()
}, },
child: Box::new(AnyPart::Mult(Multipart { child: Box::new(AnyPart::Mult(Multipart {
interpreted: mime::mime::Multipart( interpreted: mime::Multipart(
mime::r#type::Multipart { mime::r#type::Multipart {
subtype: mime::r#type::MultipartSubtype::Alternative, subtype: mime::r#type::MultipartSubtype::Alternative,
boundary: "b1_e376dc71bafc953c0b0fdeb9983a9956".to_string(), boundary: "b1_e376dc71bafc953c0b0fdeb9983a9956".to_string(),
}, },
mime::mime::Generic::default(), mime::Generic::default(),
), ),
children: vec![ children: vec![
AnyPart::Txt(Text { AnyPart::Txt(Text {
interpreted: mime::mime::Text( interpreted: mime::Text(
mime::r#type::Text { mime::r#type::Text {
subtype: mime::r#type::TextSubtype::Plain, subtype: mime::r#type::TextSubtype::Plain,
charset: mime::charset::EmailCharset::UTF_8, charset: mime::charset::EmailCharset::UTF_8,
}, },
mime::mime::Generic { mime::Generic {
transfer_encoding: mime::mechanism::Mechanism::QuotedPrintable, transfer_encoding: mime::mechanism::Mechanism::QuotedPrintable,
..mime::mime::Generic::default() ..mime::Generic::default()
} }
), ),
body: &b"GZ\nOoOoO\noOoOoOoOo\noOoOoOoOoOoOoOoOo\noOoOoOoOoOoOoOoOoOoOoOo\noOoOoOoOoOoOoOoOoOoOoOoOoOoOo\nOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO\n"[..], body: &b"GZ\nOoOoO\noOoOoOoOo\noOoOoOoOoOoOoOoOo\noOoOoOoOoOoOoOoOoOoOoOo\noOoOoOoOoOoOoOoOoOoOoOoOoOoOo\nOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO\n"[..],
}), }),
AnyPart::Txt(Text { AnyPart::Txt(Text {
interpreted: mime::mime::Text( interpreted: mime::Text(
mime::r#type::Text { mime::r#type::Text {
subtype: mime::r#type::TextSubtype::Html, subtype: mime::r#type::TextSubtype::Html,
charset: mime::charset::EmailCharset::US_ASCII, charset: mime::charset::EmailCharset::US_ASCII,
}, },
mime::mime::Generic::default(), mime::Generic::default(),
), ),
body: &br#"<div style="text-align: center;"><strong>GZ</strong><br /> body: &br#"<div style="text-align: center;"><strong>GZ</strong><br />
OoOoO<br /> OoOoO<br />

View file

@ -4,7 +4,7 @@ use crate::mime;
#[derive(PartialEq)] #[derive(PartialEq)]
pub struct Text<'a> { pub struct Text<'a> {
pub interpreted: mime::mime::Text<'a>, pub interpreted: mime::Text<'a>,
pub body: &'a [u8], pub body: &'a [u8],
} }
@ -22,7 +22,7 @@ impl<'a> fmt::Debug for Text<'a> {
#[derive(PartialEq)] #[derive(PartialEq)]
pub struct Binary<'a> { pub struct Binary<'a> {
pub interpreted: mime::mime::Binary<'a>, pub interpreted: mime::Binary<'a>,
pub body: &'a [u8], pub body: &'a [u8],
} }

View file

@ -36,14 +36,14 @@ impl<'a> MixedField<'a> {
} }
} }
impl<'a> CompFieldList<'a, MixedField<'a>> { impl<'a> CompFieldList<'a, MixedField<'a>> {
pub fn sections(self) -> (mime::mime::AnyMIME<'a>, imf::Imf<'a>) { pub fn sections(self) -> (mime::AnyMIME<'a>, imf::Imf<'a>) {
let k = self.known(); let k = self.known();
let (v1, v2): (Vec<MixedField>, Vec<MixedField>) = let (v1, v2): (Vec<MixedField>, Vec<MixedField>) =
k.into_iter().partition(|v| v.mime().is_some()); k.into_iter().partition(|v| v.mime().is_some());
let mime = v1 let mime = v1
.into_iter() .into_iter()
.filter_map(|v| v.to_mime()) .filter_map(|v| v.to_mime())
.collect::<mime::mime::AnyMIME>(); .collect::<mime::AnyMIME>();
let imf = v2 let imf = v2
.into_iter() .into_iter()
.filter_map(|v| v.to_imf()) .filter_map(|v| v.to_imf())

View file

@ -13,7 +13,7 @@ use nom::{
use crate::header::CompFieldList; use crate::header::CompFieldList;
use crate::mime; use crate::mime;
use crate::mime::mime::AnyMIME; use crate::mime::AnyMIME;
use crate::part::{ use crate::part::{
composite::{message, multipart, Message, Multipart}, composite::{message, multipart, Message, Multipart},
discrete::{Binary, Text}, discrete::{Binary, Text},
@ -35,14 +35,14 @@ pub fn to_anypart<'a>(m: AnyMIME<'a>, rpart: &'a [u8]) -> AnyPart<'a> {
AnyMIME::Mult(a) => map(multipart(a), AnyPart::Mult)(rpart) AnyMIME::Mult(a) => map(multipart(a), AnyPart::Mult)(rpart)
.map(|v| v.1) .map(|v| v.1)
.unwrap_or(AnyPart::Txt(Text { .unwrap_or(AnyPart::Txt(Text {
interpreted: mime::mime::Text::default(), interpreted: mime::Text::default(),
body: rpart, body: rpart,
})), })),
AnyMIME::Msg(a) => { AnyMIME::Msg(a) => {
map(message(a), AnyPart::Msg)(rpart) map(message(a), AnyPart::Msg)(rpart)
.map(|v| v.1) .map(|v| v.1)
.unwrap_or(AnyPart::Txt(Text { .unwrap_or(AnyPart::Txt(Text {
interpreted: mime::mime::Text::default(), interpreted: mime::Text::default(),
body: rpart, body: rpart,
})) }))
} }