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
*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`
@ -115,7 +115,6 @@ to explain why I wrote this
## License
```
eml-codec
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
along with this program. If not, see <http://www.gnu.org/licenses/>.
```

View file

@ -1,14 +1,14 @@
#![doc = include_str!("../README.md")]
mod error;
pub mod error;
mod header;
mod imf;
mod mime;
mod part;
mod text;
pub mod imf;
pub mod mime;
pub mod part;
pub mod text;
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_err(error::EMLError::ParseError)
}

View file

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

View file

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

View file

@ -36,14 +36,14 @@ impl<'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 (v1, v2): (Vec<MixedField>, Vec<MixedField>) =
k.into_iter().partition(|v| v.mime().is_some());
let mime = v1
.into_iter()
.filter_map(|v| v.to_mime())
.collect::<mime::mime::AnyMIME>();
.collect::<mime::AnyMIME>();
let imf = v2
.into_iter()
.filter_map(|v| v.to_imf())

View file

@ -13,7 +13,7 @@ use nom::{
use crate::header::CompFieldList;
use crate::mime;
use crate::mime::mime::AnyMIME;
use crate::mime::AnyMIME;
use crate::part::{
composite::{message, multipart, Message, Multipart},
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)
.map(|v| v.1)
.unwrap_or(AnyPart::Txt(Text {
interpreted: mime::mime::Text::default(),
interpreted: mime::Text::default(),
body: rpart,
})),
AnyMIME::Msg(a) => {
map(message(a), AnyPart::Msg)(rpart)
.map(|v| v.1)
.unwrap_or(AnyPart::Txt(Text {
interpreted: mime::mime::Text::default(),
interpreted: mime::Text::default(),
body: rpart,
}))
}