add info about deductible fields

This commit is contained in:
Quentin 2023-07-25 14:00:01 +02:00
parent 7b7d9de92d
commit 6e3b12c11a
Signed by: quentin
GPG key ID: E9602264D639FF68
10 changed files with 129 additions and 91 deletions

View file

@ -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(),

View file

@ -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(),

View file

@ -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)
} }

View file

@ -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);

View file

@ -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())
} }
} }

View file

@ -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]

View file

@ -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());

View file

@ -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"[..],

View file

@ -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)),

View file

@ -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,
}), }),
} }