add info about deductible fields
This commit is contained in:
parent
7b7d9de92d
commit
6e3b12c11a
10 changed files with 129 additions and 91 deletions
|
@ -18,7 +18,7 @@ Content-Type: text/plain; charset=us-ascii
|
||||||
This is the plain text body of the message. Note the blank line
|
This is the plain text body of the message. Note the blank line
|
||||||
between the header information and the body of the message."#;
|
between the header information and the body of the message."#;
|
||||||
|
|
||||||
let (_, email) = eml_codec::email(input).unwrap();
|
let (_, email) = eml_codec::parse_message(input).unwrap();
|
||||||
println!(
|
println!(
|
||||||
"{} just sent you an email with subject \"{}\"",
|
"{} just sent you an email with subject \"{}\"",
|
||||||
email.imf.from[0].to_string(),
|
email.imf.from[0].to_string(),
|
||||||
|
|
|
@ -10,7 +10,7 @@ This is the plain text body of the message. Note the blank line
|
||||||
between the header information and the body of the message."#;
|
between the header information and the body of the message."#;
|
||||||
|
|
||||||
// if you are only interested in email metadata/headers
|
// if you are only interested in email metadata/headers
|
||||||
let (_, imf) = eml_codec::imf(input).unwrap();
|
let (_, imf) = eml_codec::parse_imf(input).unwrap();
|
||||||
println!(
|
println!(
|
||||||
"{} just sent you an email with subject \"{}\"",
|
"{} just sent you an email with subject \"{}\"",
|
||||||
imf.from[0].to_string(),
|
imf.from[0].to_string(),
|
||||||
|
@ -18,7 +18,7 @@ between the header information and the body of the message."#;
|
||||||
);
|
);
|
||||||
|
|
||||||
// if you like to also parse the body/content
|
// if you like to also parse the body/content
|
||||||
let (_, email) = eml_codec::email(input).unwrap();
|
let (_, email) = eml_codec::parse_message(input).unwrap();
|
||||||
println!(
|
println!(
|
||||||
"{} raw message is:\n{}",
|
"{} raw message is:\n{}",
|
||||||
email.imf.from[0].to_string(),
|
email.imf.from[0].to_string(),
|
||||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -15,7 +15,7 @@ pub mod header;
|
||||||
/// Low-level email-specific text-based representation for data
|
/// Low-level email-specific text-based representation for data
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|
||||||
use nom::IResult;
|
use nom::{IResult, combinator::into};
|
||||||
|
|
||||||
/// Parse a whole email including its (MIME) body
|
/// Parse a whole email including its (MIME) body
|
||||||
///
|
///
|
||||||
|
@ -46,15 +46,15 @@ use nom::IResult;
|
||||||
/// This is the plain text body of the message. Note the blank line
|
/// This is the plain text body of the message. Note the blank line
|
||||||
/// between the header information and the body of the message."#;
|
/// between the header information and the body of the message."#;
|
||||||
///
|
///
|
||||||
/// let (_, email) = eml_codec::email(input).unwrap();
|
/// let (_, email) = eml_codec::parse_message(input).unwrap();
|
||||||
/// println!(
|
/// println!(
|
||||||
/// "{} raw message is:\n{}",
|
/// "{} raw message is:\n{}",
|
||||||
/// email.imf.from[0].to_string(),
|
/// email.imf.from[0].to_string(),
|
||||||
/// String::from_utf8_lossy(email.child.as_text().unwrap().body),
|
/// String::from_utf8_lossy(email.child.as_text().unwrap().body),
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub fn email(input: &[u8]) -> IResult<&[u8], part::composite::Message> {
|
pub fn parse_message(input: &[u8]) -> IResult<&[u8], part::composite::Message> {
|
||||||
part::composite::message(mime::MIME::<mime::r#type::Message>::default())(input)
|
into(part::composite::message(mime::MIME::<mime::r#type::DeductibleMessage>::default()))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Only extract the headers of the email that are part of the Internet Message Format spec
|
/// Only extract the headers of the email that are part of the Internet Message Format spec
|
||||||
|
@ -87,13 +87,13 @@ pub fn email(input: &[u8]) -> IResult<&[u8], part::composite::Message> {
|
||||||
/// This is the plain text body of the message. Note the blank line
|
/// This is the plain text body of the message. Note the blank line
|
||||||
/// between the header information and the body of the message."#;
|
/// between the header information and the body of the message."#;
|
||||||
///
|
///
|
||||||
/// let (_, imf) = eml_codec::imf(input).unwrap();
|
/// let (_, imf) = eml_codec::parse_imf(input).unwrap();
|
||||||
/// println!(
|
/// println!(
|
||||||
/// "{} just sent you an email with subject \"{}\"",
|
/// "{} just sent you an email with subject \"{}\"",
|
||||||
/// imf.from[0].to_string(),
|
/// imf.from[0].to_string(),
|
||||||
/// imf.subject.unwrap().to_string(),
|
/// imf.subject.unwrap().to_string(),
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub fn imf(input: &[u8]) -> IResult<&[u8], imf::Imf> {
|
pub fn parse_imf(input: &[u8]) -> IResult<&[u8], imf::Imf> {
|
||||||
imf::field::imf(input)
|
imf::field::imf(input)
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,10 +88,10 @@ mod tests {
|
||||||
if let Content::Type(nt) = content {
|
if let Content::Type(nt) = content {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
nt.to_type(),
|
nt.to_type(),
|
||||||
AnyType::Text(Text {
|
AnyType::Text(Deductible::Explicit(Text {
|
||||||
charset: EmailCharset::UTF_8,
|
charset: Deductible::Explicit(EmailCharset::UTF_8),
|
||||||
subtype: TextSubtype::Plain,
|
subtype: TextSubtype::Plain,
|
||||||
}),
|
})),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected Content::Type, got {:?}", content);
|
panic!("Expected Content::Type, got {:?}", content);
|
||||||
|
|
|
@ -21,22 +21,22 @@ use crate::text::misc_token::Unstructured; //Multipart, Message, Text, Binary};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct MIME<'a, T> {
|
pub struct MIME<'a, T> {
|
||||||
pub interpreted: T,
|
pub interpreted_type: T,
|
||||||
pub parsed: NaiveMIME<'a>
|
pub fields: NaiveMIME<'a>
|
||||||
}
|
}
|
||||||
impl<'a> Default for MIME<'a, r#type::Text> {
|
impl<'a> Default for MIME<'a, r#type::DeductibleText> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
interpreted: r#type::Text::default(),
|
interpreted_type: r#type::DeductibleText::default(),
|
||||||
parsed: NaiveMIME::default(),
|
fields: NaiveMIME::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> Default for MIME<'a, r#type::Message> {
|
impl<'a> Default for MIME<'a, r#type::DeductibleMessage> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
interpreted: r#type::Message::default(),
|
interpreted_type: r#type::DeductibleMessage::default(),
|
||||||
parsed: NaiveMIME::default(),
|
fields: NaiveMIME::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,8 +44,8 @@ impl<'a> Default for MIME<'a, r#type::Message> {
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum AnyMIME<'a> {
|
pub enum AnyMIME<'a> {
|
||||||
Mult(MIME<'a, r#type::Multipart>),
|
Mult(MIME<'a, r#type::Multipart>),
|
||||||
Msg(MIME<'a, r#type::Message>),
|
Msg(MIME<'a, r#type::DeductibleMessage>),
|
||||||
Txt(MIME<'a, r#type::Text>),
|
Txt(MIME<'a, r#type::DeductibleText>),
|
||||||
Bin(MIME<'a, r#type::Binary>),
|
Bin(MIME<'a, r#type::Binary>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,13 +103,13 @@ pub trait WithDefaultType {
|
||||||
pub struct WithGenericDefault {}
|
pub struct WithGenericDefault {}
|
||||||
impl WithDefaultType for WithGenericDefault {
|
impl WithDefaultType for WithGenericDefault {
|
||||||
fn default_type() -> AnyType {
|
fn default_type() -> AnyType {
|
||||||
AnyType::Text(r#type::Text::default())
|
AnyType::Text(r#type::DeductibleText::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub struct WithDigestDefault {}
|
pub struct WithDigestDefault {}
|
||||||
impl WithDefaultType for WithDigestDefault {
|
impl WithDefaultType for WithDigestDefault {
|
||||||
fn default_type() -> AnyType {
|
fn default_type() -> AnyType {
|
||||||
AnyType::Message(r#type::Message::default())
|
AnyType::Message(r#type::DeductibleMessage::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,10 +51,10 @@ pub fn parameter_list(input: &[u8]) -> IResult<&[u8], Vec<Parameter>> {
|
||||||
pub enum AnyType {
|
pub enum AnyType {
|
||||||
// Composite types
|
// Composite types
|
||||||
Multipart(Multipart),
|
Multipart(Multipart),
|
||||||
Message(Message),
|
Message(Deductible<Message>),
|
||||||
|
|
||||||
// Discrete types
|
// Discrete types
|
||||||
Text(Text),
|
Text(Deductible<Text>),
|
||||||
Binary(Binary),
|
Binary(Binary),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,25 +63,39 @@ impl<'a> From<&'a NaiveType<'a>> for AnyType {
|
||||||
match nt.main.to_ascii_lowercase().as_slice() {
|
match nt.main.to_ascii_lowercase().as_slice() {
|
||||||
b"multipart" => Multipart::try_from(nt)
|
b"multipart" => Multipart::try_from(nt)
|
||||||
.map(Self::Multipart)
|
.map(Self::Multipart)
|
||||||
.unwrap_or(Self::Text(Text::default())),
|
.unwrap_or(Self::Text(DeductibleText::default())),
|
||||||
b"message" => Self::Message(Message::from(nt)),
|
b"message" => Self::Message(DeductibleMessage::Explicit(Message::from(nt))),
|
||||||
b"text" => Self::Text(Text::from(nt)),
|
b"text" => Self::Text(DeductibleText::Explicit(Text::from(nt))),
|
||||||
_ => Self::Binary(Binary::default()),
|
_ => Self::Binary(Binary::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AnyType {
|
impl<'a> AnyType {
|
||||||
pub fn to_mime(self, parsed: NaiveMIME<'a>) -> AnyMIME<'a> {
|
pub fn to_mime(self, fields: NaiveMIME<'a>) -> AnyMIME<'a> {
|
||||||
match self {
|
match self {
|
||||||
Self::Multipart(interpreted) => AnyMIME::Mult(MIME::<Multipart> { interpreted, parsed }),
|
Self::Multipart(interpreted_type) => AnyMIME::Mult(MIME::<Multipart> { interpreted_type, fields }),
|
||||||
Self::Message(interpreted) => AnyMIME::Msg(MIME::<Message> { interpreted, parsed }),
|
Self::Message(interpreted_type) => AnyMIME::Msg(MIME::<DeductibleMessage> { interpreted_type, fields }),
|
||||||
Self::Text(interpreted) => AnyMIME::Txt(MIME::<Text> { interpreted, parsed }),
|
Self::Text(interpreted_type) => AnyMIME::Txt(MIME::<DeductibleText> { interpreted_type, fields }),
|
||||||
Self::Binary(interpreted) => AnyMIME::Bin(MIME::<Binary> { interpreted, parsed }),
|
Self::Binary(interpreted_type) => AnyMIME::Bin(MIME::<Binary> { interpreted_type, fields }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum Deductible<T: Default> {
|
||||||
|
Inferred(T),
|
||||||
|
Explicit(T),
|
||||||
|
}
|
||||||
|
impl<T: Default> Default for Deductible<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Inferred(T::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// REAL PARTS
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Multipart {
|
pub struct Multipart {
|
||||||
pub subtype: MultipartSubtype,
|
pub subtype: MultipartSubtype,
|
||||||
|
@ -124,29 +138,45 @@ impl<'a> From<&NaiveType<'a>> for MultipartSubtype {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Default, Clone)]
|
#[derive(Debug, PartialEq, Default, Clone)]
|
||||||
pub enum Message {
|
pub enum MessageSubtype {
|
||||||
#[default]
|
#[default]
|
||||||
RFC822,
|
RFC822,
|
||||||
Partial,
|
Partial,
|
||||||
External,
|
External,
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type DeductibleMessage = Deductible<Message>;
|
||||||
|
#[derive(Debug, PartialEq, Default, Clone)]
|
||||||
|
pub struct Message {
|
||||||
|
pub subtype: MessageSubtype,
|
||||||
|
}
|
||||||
impl<'a> From<&NaiveType<'a>> for Message {
|
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 { subtype: MessageSubtype::RFC822 },
|
||||||
b"partial" => Self::Partial,
|
b"partial" => Self { subtype: MessageSubtype::Partial },
|
||||||
b"external" => Self::External,
|
b"external" => Self { subtype: MessageSubtype::External },
|
||||||
_ => Self::Unknown,
|
_ => Self { subtype: MessageSubtype::Unknown },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Deductible<Message>> for Message {
|
||||||
|
fn from(d: Deductible<Message>) -> Self {
|
||||||
|
match d {
|
||||||
|
Deductible::Inferred(t) | Deductible::Explicit(t) => t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type DeductibleText = Deductible<Text>;
|
||||||
#[derive(Debug, PartialEq, Default, Clone)]
|
#[derive(Debug, PartialEq, Default, Clone)]
|
||||||
pub struct Text {
|
pub struct Text {
|
||||||
pub subtype: TextSubtype,
|
pub subtype: TextSubtype,
|
||||||
pub charset: EmailCharset,
|
pub charset: Deductible<EmailCharset>,
|
||||||
}
|
}
|
||||||
impl<'a> From<&NaiveType<'a>> for Text {
|
impl<'a> From<&NaiveType<'a>> for Text {
|
||||||
fn from(nt: &NaiveType<'a>) -> Self {
|
fn from(nt: &NaiveType<'a>) -> Self {
|
||||||
|
@ -156,8 +186,15 @@ impl<'a> From<&NaiveType<'a>> for Text {
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
.find(|x| x.name.to_ascii_lowercase().as_slice() == b"charset")
|
.find(|x| x.name.to_ascii_lowercase().as_slice() == b"charset")
|
||||||
.map(|x| EmailCharset::from(x.value.to_string().as_bytes()))
|
.map(|x| Deductible::Explicit(EmailCharset::from(x.value.to_string().as_bytes())))
|
||||||
.unwrap_or(EmailCharset::US_ASCII),
|
.unwrap_or(Deductible::Inferred(EmailCharset::US_ASCII)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Deductible<Text>> for Text {
|
||||||
|
fn from(d: Deductible<Text>) -> Self {
|
||||||
|
match d {
|
||||||
|
Deductible::Inferred(t) | Deductible::Explicit(t) => t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,6 +224,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::mime::charset::EmailCharset;
|
use crate::mime::charset::EmailCharset;
|
||||||
use crate::text::quoted::QuotedString;
|
use crate::text::quoted::QuotedString;
|
||||||
|
use crate::mime::r#type::Deductible;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parameter() {
|
fn test_parameter() {
|
||||||
|
@ -219,10 +257,10 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
nt.to_type(),
|
nt.to_type(),
|
||||||
AnyType::Text(Text {
|
AnyType::Text(Deductible::Explicit(Text {
|
||||||
charset: EmailCharset::UTF_8,
|
charset: Deductible::Explicit(EmailCharset::UTF_8),
|
||||||
subtype: TextSubtype::Plain,
|
subtype: TextSubtype::Plain,
|
||||||
})
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +282,7 @@ mod tests {
|
||||||
let (rest, nt) = naive_type(b"message/rfc822").unwrap();
|
let (rest, nt) = naive_type(b"message/rfc822").unwrap();
|
||||||
assert_eq!(rest, &[]);
|
assert_eq!(rest, &[]);
|
||||||
|
|
||||||
assert_eq!(nt.to_type(), AnyType::Message(Message::RFC822),);
|
assert_eq!(nt.to_type(), AnyType::Message(Deductible::Explicit(Message { subtype: MessageSubtype::RFC822 })));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -8,7 +8,7 @@ fn main() {
|
||||||
let mut rawmail = Vec::new();
|
let mut rawmail = Vec::new();
|
||||||
io::stdin().lock().read_to_end(&mut rawmail).unwrap();
|
io::stdin().lock().read_to_end(&mut rawmail).unwrap();
|
||||||
|
|
||||||
let (_, eml) = eml_codec::email(&rawmail).unwrap();
|
let (_, eml) = eml_codec::parse_message(&rawmail).unwrap();
|
||||||
println!("{:#?}", eml);
|
println!("{:#?}", eml);
|
||||||
assert!(eml.imf.date.is_some());
|
assert!(eml.imf.date.is_some());
|
||||||
assert!(!eml.imf.from.is_empty());
|
assert!(!eml.imf.from.is_empty());
|
||||||
|
|
|
@ -9,7 +9,7 @@ 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<'a, mime::r#type::Multipart>,
|
pub mime: mime::MIME<'a, mime::r#type::Multipart>,
|
||||||
pub children: Vec<AnyPart<'a>>,
|
pub children: Vec<AnyPart<'a>>,
|
||||||
pub preamble: &'a [u8],
|
pub preamble: &'a [u8],
|
||||||
pub epilogue: &'a [u8],
|
pub epilogue: &'a [u8],
|
||||||
|
@ -27,7 +27,7 @@ pub fn multipart<'a>(
|
||||||
let m = m.clone();
|
let m = m.clone();
|
||||||
|
|
||||||
move |input| {
|
move |input| {
|
||||||
let bound = m.interpreted.boundary.as_bytes();
|
let bound = m.interpreted_type.boundary.as_bytes();
|
||||||
let (mut input_loop, preamble) = part::part_raw(bound)(input)?;
|
let (mut input_loop, preamble) = part::part_raw(bound)(input)?;
|
||||||
let mut mparts: Vec<AnyPart> = vec![];
|
let mut mparts: Vec<AnyPart> = vec![];
|
||||||
loop {
|
loop {
|
||||||
|
@ -36,7 +36,7 @@ pub fn multipart<'a>(
|
||||||
return Ok((
|
return Ok((
|
||||||
input_loop,
|
input_loop,
|
||||||
Multipart {
|
Multipart {
|
||||||
interpreted: m.clone(),
|
mime: m.clone(),
|
||||||
children: mparts,
|
children: mparts,
|
||||||
preamble,
|
preamble,
|
||||||
epilogue: &[],
|
epilogue: &[],
|
||||||
|
@ -47,7 +47,7 @@ pub fn multipart<'a>(
|
||||||
return Ok((
|
return Ok((
|
||||||
inp,
|
inp,
|
||||||
Multipart {
|
Multipart {
|
||||||
interpreted: m.clone(),
|
mime: m.clone(),
|
||||||
children: mparts,
|
children: mparts,
|
||||||
preamble,
|
preamble,
|
||||||
epilogue: &[],
|
epilogue: &[],
|
||||||
|
@ -64,7 +64,7 @@ pub fn multipart<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
// interpret mime according to context
|
// interpret mime according to context
|
||||||
let mime = match m.interpreted.subtype {
|
let mime = match m.interpreted_type.subtype {
|
||||||
mime::r#type::MultipartSubtype::Digest => naive_mime.to_interpreted::<mime::WithDigestDefault>().into(),
|
mime::r#type::MultipartSubtype::Digest => naive_mime.to_interpreted::<mime::WithDigestDefault>().into(),
|
||||||
_ => naive_mime.to_interpreted::<mime::WithGenericDefault>().into(),
|
_ => naive_mime.to_interpreted::<mime::WithGenericDefault>().into(),
|
||||||
};
|
};
|
||||||
|
@ -85,7 +85,7 @@ pub fn multipart<'a>(
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Message<'a> {
|
pub struct Message<'a> {
|
||||||
pub interpreted: mime::MIME<'a, mime::r#type::Message>,
|
pub mime: mime::MIME<'a, mime::r#type::DeductibleMessage>,
|
||||||
pub imf: imf::Imf<'a>,
|
pub imf: imf::Imf<'a>,
|
||||||
pub child: Box<AnyPart<'a>>,
|
pub child: Box<AnyPart<'a>>,
|
||||||
pub epilogue: &'a [u8],
|
pub epilogue: &'a [u8],
|
||||||
|
@ -98,7 +98,7 @@ impl<'a> Message<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn message<'a>(
|
pub fn message<'a>(
|
||||||
m: mime::MIME<'a, mime::r#type::Message>,
|
m: mime::MIME<'a, mime::r#type::DeductibleMessage>,
|
||||||
) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Message<'a>> {
|
) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Message<'a>> {
|
||||||
move |input: &[u8]| {
|
move |input: &[u8]| {
|
||||||
// parse header fields
|
// parse header fields
|
||||||
|
@ -120,7 +120,7 @@ pub fn message<'a>(
|
||||||
Ok((
|
Ok((
|
||||||
&[],
|
&[],
|
||||||
Message {
|
Message {
|
||||||
interpreted: m.clone(),
|
mime: m.clone(),
|
||||||
imf,
|
imf,
|
||||||
child: Box::new(part),
|
child: Box::new(part),
|
||||||
epilogue: &[],
|
epilogue: &[],
|
||||||
|
@ -142,11 +142,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multipart() {
|
fn test_multipart() {
|
||||||
let base_mime = mime::MIME {
|
let base_mime = mime::MIME {
|
||||||
interpreted: mime::r#type::Multipart {
|
interpreted_type: 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(),
|
||||||
},
|
},
|
||||||
parsed: mime::NaiveMIME::default(),
|
fields: mime::NaiveMIME::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -170,27 +170,27 @@ This is the epilogue. It is also to be ignored.
|
||||||
"),
|
"),
|
||||||
Ok((&b"\nThis is the epilogue. It is also to be ignored.\n"[..],
|
Ok((&b"\nThis is the epilogue. It is also to be ignored.\n"[..],
|
||||||
Multipart {
|
Multipart {
|
||||||
interpreted: base_mime,
|
mime: base_mime,
|
||||||
preamble: &b"This is the preamble. It is to be ignored, though it\nis a handy place for composition agents to include an\nexplanatory note to non-MIME conformant readers.\n"[..],
|
preamble: &b"This is the preamble. It is to be ignored, though it\nis a handy place for composition agents to include an\nexplanatory note to non-MIME conformant readers.\n"[..],
|
||||||
epilogue: &b""[..],
|
epilogue: &b""[..],
|
||||||
children: vec![
|
children: vec![
|
||||||
AnyPart::Txt(Text {
|
AnyPart::Txt(Text {
|
||||||
interpreted: mime::MIME {
|
mime: mime::MIME {
|
||||||
interpreted: mime::r#type::Text {
|
interpreted_type: mime::r#type::Deductible::Inferred(mime::r#type::Text {
|
||||||
subtype: mime::r#type::TextSubtype::Plain,
|
subtype: mime::r#type::TextSubtype::Plain,
|
||||||
charset: mime::charset::EmailCharset::US_ASCII,
|
charset: mime::r#type::Deductible::Inferred(mime::charset::EmailCharset::US_ASCII),
|
||||||
},
|
}),
|
||||||
parsed: mime::NaiveMIME::default(),
|
fields: mime::NaiveMIME::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 {
|
mime: mime::MIME {
|
||||||
interpreted: mime::r#type::Text {
|
interpreted_type: mime::r#type::Deductible::Explicit(mime::r#type::Text {
|
||||||
subtype: mime::r#type::TextSubtype::Plain,
|
subtype: mime::r#type::TextSubtype::Plain,
|
||||||
charset: mime::charset::EmailCharset::US_ASCII,
|
charset: mime::r#type::Deductible::Explicit(mime::charset::EmailCharset::US_ASCII),
|
||||||
},
|
}),
|
||||||
parsed: mime::NaiveMIME {
|
fields: mime::NaiveMIME {
|
||||||
ctype: Some(mime::r#type::NaiveType {
|
ctype: Some(mime::r#type::NaiveType {
|
||||||
main: &b"text"[..],
|
main: &b"text"[..],
|
||||||
sub: &b"plain"[..],
|
sub: &b"plain"[..],
|
||||||
|
@ -259,13 +259,13 @@ OoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO<br />
|
||||||
"#
|
"#
|
||||||
.as_bytes();
|
.as_bytes();
|
||||||
|
|
||||||
let base_mime = mime::MIME::<mime::r#type::Message>::default();
|
let base_mime = mime::MIME::<mime::r#type::DeductibleMessage>::default();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
message(base_mime.clone())(fullmail),
|
message(base_mime.clone())(fullmail),
|
||||||
Ok((
|
Ok((
|
||||||
&[][..],
|
&[][..],
|
||||||
Message {
|
Message {
|
||||||
interpreted: base_mime,
|
mime: base_mime,
|
||||||
epilogue: &b""[..],
|
epilogue: &b""[..],
|
||||||
imf: imf::Imf {
|
imf: imf::Imf {
|
||||||
date: Some(FixedOffset::east_opt(2 * 3600)
|
date: Some(FixedOffset::east_opt(2 * 3600)
|
||||||
|
@ -342,12 +342,12 @@ OoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO<br />
|
||||||
..imf::Imf::default()
|
..imf::Imf::default()
|
||||||
},
|
},
|
||||||
child: Box::new(AnyPart::Mult(Multipart {
|
child: Box::new(AnyPart::Mult(Multipart {
|
||||||
interpreted: mime::MIME {
|
mime: mime::MIME {
|
||||||
interpreted: mime::r#type::Multipart {
|
interpreted_type: 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(),
|
||||||
},
|
},
|
||||||
parsed: mime::NaiveMIME {
|
fields: mime::NaiveMIME {
|
||||||
ctype: Some(mime::r#type::NaiveType {
|
ctype: Some(mime::r#type::NaiveType {
|
||||||
main: &b"multipart"[..],
|
main: &b"multipart"[..],
|
||||||
sub: &b"alternative"[..],
|
sub: &b"alternative"[..],
|
||||||
|
@ -365,12 +365,12 @@ OoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO<br />
|
||||||
epilogue: &b""[..],
|
epilogue: &b""[..],
|
||||||
children: vec![
|
children: vec![
|
||||||
AnyPart::Txt(Text {
|
AnyPart::Txt(Text {
|
||||||
interpreted: mime::MIME {
|
mime: mime::MIME {
|
||||||
interpreted: mime::r#type::Text {
|
interpreted_type: mime::r#type::Deductible::Explicit(mime::r#type::Text {
|
||||||
subtype: mime::r#type::TextSubtype::Plain,
|
subtype: mime::r#type::TextSubtype::Plain,
|
||||||
charset: mime::charset::EmailCharset::UTF_8,
|
charset: mime::r#type::Deductible::Explicit(mime::charset::EmailCharset::UTF_8),
|
||||||
},
|
}),
|
||||||
parsed: mime::NaiveMIME {
|
fields: mime::NaiveMIME {
|
||||||
ctype: Some(mime::r#type::NaiveType {
|
ctype: Some(mime::r#type::NaiveType {
|
||||||
main: &b"text"[..],
|
main: &b"text"[..],
|
||||||
sub: &b"plain"[..],
|
sub: &b"plain"[..],
|
||||||
|
@ -388,13 +388,13 @@ OoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO<br />
|
||||||
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 {
|
mime: mime::MIME {
|
||||||
interpreted: mime::r#type::Text {
|
interpreted_type: mime::r#type::Deductible::Explicit(mime::r#type::Text {
|
||||||
subtype: mime::r#type::TextSubtype::Html,
|
subtype: mime::r#type::TextSubtype::Html,
|
||||||
charset: mime::charset::EmailCharset::US_ASCII,
|
charset: mime::r#type::Deductible::Explicit(mime::charset::EmailCharset::US_ASCII),
|
||||||
},
|
}),
|
||||||
|
|
||||||
parsed: mime::NaiveMIME {
|
fields: mime::NaiveMIME {
|
||||||
ctype: Some(mime::r#type::NaiveType {
|
ctype: Some(mime::r#type::NaiveType {
|
||||||
main: &b"text"[..],
|
main: &b"text"[..],
|
||||||
sub: &b"html"[..],
|
sub: &b"html"[..],
|
||||||
|
|
|
@ -4,14 +4,14 @@ use crate::mime;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub struct Text<'a> {
|
pub struct Text<'a> {
|
||||||
pub interpreted: mime::MIME<'a, mime::r#type::Text>,
|
pub mime: mime::MIME<'a, mime::r#type::DeductibleText>,
|
||||||
pub body: &'a [u8],
|
pub body: &'a [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> fmt::Debug for Text<'a> {
|
impl<'a> fmt::Debug for Text<'a> {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt.debug_struct("part::Text")
|
fmt.debug_struct("part::Text")
|
||||||
.field("mime", &self.interpreted)
|
.field("mime", &self.mime)
|
||||||
.field(
|
.field(
|
||||||
"body",
|
"body",
|
||||||
&format_args!("\"{}\"", String::from_utf8_lossy(self.body)),
|
&format_args!("\"{}\"", String::from_utf8_lossy(self.body)),
|
||||||
|
@ -22,14 +22,14 @@ impl<'a> fmt::Debug for Text<'a> {
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub struct Binary<'a> {
|
pub struct Binary<'a> {
|
||||||
pub interpreted: mime::MIME<'a, mime::r#type::Binary>,
|
pub mime: mime::MIME<'a, mime::r#type::Binary>,
|
||||||
pub body: &'a [u8],
|
pub body: &'a [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> fmt::Debug for Binary<'a> {
|
impl<'a> fmt::Debug for Binary<'a> {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt.debug_struct("part::Binary")
|
fmt.debug_struct("part::Binary")
|
||||||
.field("mime", &self.interpreted)
|
.field("mime", &self.mime)
|
||||||
.field(
|
.field(
|
||||||
"body",
|
"body",
|
||||||
&format_args!("\"{}\"", String::from_utf8_lossy(self.body)),
|
&format_args!("\"{}\"", String::from_utf8_lossy(self.body)),
|
||||||
|
|
|
@ -65,21 +65,21 @@ pub fn to_anypart<'a>(m: AnyMIME<'a>, rpart: &'a [u8]) -> AnyPart<'a> {
|
||||||
AnyMIME::Mult(a) => multipart(a)(rpart)
|
AnyMIME::Mult(a) => multipart(a)(rpart)
|
||||||
.map(|(rest, multi)| AnyPart::Mult(multi.with_epilogue(rest)))
|
.map(|(rest, multi)| AnyPart::Mult(multi.with_epilogue(rest)))
|
||||||
.unwrap_or(AnyPart::Txt(Text {
|
.unwrap_or(AnyPart::Txt(Text {
|
||||||
interpreted: mime::MIME::<mime::r#type::Text>::default(),
|
mime: mime::MIME::<mime::r#type::DeductibleText>::default(),
|
||||||
body: rpart,
|
body: rpart,
|
||||||
})),
|
})),
|
||||||
AnyMIME::Msg(a) => message(a)(rpart)
|
AnyMIME::Msg(a) => message(a)(rpart)
|
||||||
.map(|(rest, msg)| AnyPart::Msg(msg.with_epilogue(rest)))
|
.map(|(rest, msg)| AnyPart::Msg(msg.with_epilogue(rest)))
|
||||||
.unwrap_or(AnyPart::Txt(Text {
|
.unwrap_or(AnyPart::Txt(Text {
|
||||||
interpreted: mime::MIME::<mime::r#type::Text>::default(),
|
mime: mime::MIME::<mime::r#type::DeductibleText>::default(),
|
||||||
body: rpart,
|
body: rpart,
|
||||||
})),
|
})),
|
||||||
AnyMIME::Txt(a) => AnyPart::Txt(Text {
|
AnyMIME::Txt(a) => AnyPart::Txt(Text {
|
||||||
interpreted: a,
|
mime: a,
|
||||||
body: rpart,
|
body: rpart,
|
||||||
}),
|
}),
|
||||||
AnyMIME::Bin(a) => AnyPart::Bin(Binary {
|
AnyMIME::Bin(a) => AnyPart::Bin(Binary {
|
||||||
interpreted: a,
|
mime: a,
|
||||||
body: rpart,
|
body: rpart,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue