refactor mime
This commit is contained in:
parent
16006781d0
commit
40ab7e33b6
9 changed files with 96 additions and 98 deletions
|
@ -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/>.
|
||||
```
|
||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>>,
|
||||
}
|
|
@ -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>>,
|
||||
}
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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],
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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,
|
||||
}))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue