format code

This commit is contained in:
Quentin 2023-08-30 19:49:04 +02:00
parent 2529b0145e
commit d9285c9ddf
Signed by: quentin
GPG key ID: E9602264D639FF68
12 changed files with 167 additions and 121 deletions

View file

@ -1,4 +1,3 @@
use std::fmt;
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::complete::{tag, take_while1}, bytes::complete::{tag, take_while1},
@ -8,9 +7,10 @@ use nom::{
sequence::{pair, terminated, tuple}, sequence::{pair, terminated, tuple},
IResult, IResult,
}; };
use std::fmt;
use crate::text::whitespace::{foldable_line, obs_crlf};
use crate::text::misc_token::unstructured; use crate::text::misc_token::unstructured;
use crate::text::whitespace::{foldable_line, obs_crlf};
#[derive(PartialEq, Clone)] #[derive(PartialEq, Clone)]
pub struct Kv2<'a>(pub &'a [u8], pub &'a [u8]); pub struct Kv2<'a>(pub &'a [u8], pub &'a [u8]);
@ -47,13 +47,8 @@ impl<'a> From<&'a [u8]> for Field<'a> {
/// Parse headers as key/values /// Parse headers as key/values
pub fn header_kv(input: &[u8]) -> IResult<&[u8], Vec<Field>> { pub fn header_kv(input: &[u8]) -> IResult<&[u8], Vec<Field>> {
terminated( terminated(
many0( many0(alt((into(correct_field), into(foldable_line)))),
alt(( obs_crlf,
into(correct_field),
into(foldable_line),
))
),
obs_crlf
)(input) )(input)
} }
@ -74,11 +69,5 @@ pub fn field_any(input: &[u8]) -> IResult<&[u8], &[u8]> {
/// ; ":". /// ; ":".
/// ``` /// ```
pub fn correct_field(input: &[u8]) -> IResult<&[u8], Kv2> { pub fn correct_field(input: &[u8]) -> IResult<&[u8], Kv2> {
terminated( terminated(into(pair(field_any, recognize(unstructured))), obs_crlf)(input)
into(pair(
field_any,
recognize(unstructured),
)),
obs_crlf,
)(input)
} }

View file

@ -46,7 +46,8 @@ impl<'a> TryFrom<&header::Field<'a>> for Field<'a> {
type Error = (); type Error = ();
fn try_from(f: &header::Field<'a>) -> Result<Self, Self::Error> { fn try_from(f: &header::Field<'a>) -> Result<Self, Self::Error> {
let content = match f { let content = match f {
header::Field::Good(header::Kv2(key, value)) => match key.to_ascii_lowercase().as_slice() { header::Field::Good(header::Kv2(key, value)) => {
match key.to_ascii_lowercase().as_slice() {
b"date" => map(date, Field::Date)(value), b"date" => map(date, Field::Date)(value),
b"from" => map(mailbox_list, Field::From)(value), b"from" => map(mailbox_list, Field::From)(value),
b"sender" => map(mailbox, Field::Sender)(value), b"sender" => map(mailbox, Field::Sender)(value),
@ -64,7 +65,8 @@ impl<'a> TryFrom<&header::Field<'a>> for Field<'a> {
b"received" => map(received_log, Field::Received)(value), b"received" => map(received_log, Field::Received)(value),
b"mime-version" => map(version, Field::MIMEVersion)(value), b"mime-version" => map(version, Field::MIMEVersion)(value),
_ => return Err(()), _ => return Err(()),
}, }
}
_ => return Err(()), _ => return Err(()),
}; };

View file

@ -1,5 +1,4 @@
/// Parse and represent IMF (Internet Message Format) headers (RFC822, RFC5322) /// Parse and represent IMF (Internet Message Format) headers (RFC822, RFC5322)
pub mod address; pub mod address;
pub mod datetime; pub mod datetime;
pub mod field; pub mod field;
@ -8,10 +7,7 @@ pub mod mailbox;
pub mod mime; pub mod mime;
pub mod trace; pub mod trace;
use nom::{ use nom::{combinator::map, IResult};
combinator::map,
IResult,
};
use crate::header; use crate::header;
use crate::imf::address::AddressRef; use crate::imf::address::AddressRef;
@ -61,7 +57,8 @@ pub struct Imf<'a> {
} }
impl<'a> Imf<'a> { impl<'a> Imf<'a> {
pub fn with_kv(mut self, v: Vec<header::Field<'a>>) -> Self { pub fn with_kv(mut self, v: Vec<header::Field<'a>>) -> Self {
self.kv = v; self self.kv = v;
self
} }
} }
@ -95,11 +92,14 @@ impl<'a> FromIterator<Field<'a>> for Imf<'a> {
pub fn imf(input: &[u8]) -> IResult<&[u8], Imf> { pub fn imf(input: &[u8]) -> IResult<&[u8], Imf> {
map(header::header_kv, |fields| { map(header::header_kv, |fields| {
fields.iter().flat_map(Field::try_from).into_iter().collect::<Imf>() fields
.iter()
.flat_map(Field::try_from)
.into_iter()
.collect::<Imf>()
})(input) })(input)
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -18,7 +18,7 @@ pub mod text;
/// Manipulate buffer of bytes /// Manipulate buffer of bytes
mod pointers; mod pointers;
use nom::{IResult, combinator::into}; use nom::{combinator::into, IResult};
/// Parse a whole email including its (MIME) body /// Parse a whole email including its (MIME) body
/// ///
@ -57,7 +57,9 @@ use nom::{IResult, combinator::into};
/// ); /// );
/// ``` /// ```
pub fn parse_message(input: &[u8]) -> IResult<&[u8], part::composite::Message> { pub fn parse_message(input: &[u8]) -> IResult<&[u8], part::composite::Message> {
into(part::composite::message(mime::MIME::<mime::r#type::DeductibleMessage>::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

View file

@ -45,7 +45,10 @@ impl<'a> TryFrom<&header::Field<'a>> for Content<'a> {
type Error = (); type Error = ();
fn try_from(f: &header::Field<'a>) -> Result<Self, Self::Error> { fn try_from(f: &header::Field<'a>) -> Result<Self, Self::Error> {
let content = match f { let content = match f {
header::Field::Good(header::Kv2(key, value)) => match key.to_ascii_lowercase().as_slice() { header::Field::Good(header::Kv2(key, value)) => match key
.to_ascii_lowercase()
.as_slice()
{
b"content-type" => map(naive_type, Content::Type)(value), b"content-type" => map(naive_type, Content::Type)(value),
b"content-transfer-encoding" => map(mechanism, Content::TransferEncoding)(value), b"content-transfer-encoding" => map(mechanism, Content::TransferEncoding)(value),
b"content-id" => map(msg_id, Content::ID)(value), b"content-id" => map(msg_id, Content::ID)(value),
@ -107,7 +110,10 @@ This is a multipart message.
.as_bytes(); .as_bytes();
assert_eq!( assert_eq!(
map(header::header_kv, |k| k.iter().flat_map(Content::try_from).collect())(fullmail), map(header::header_kv, |k| k
.iter()
.flat_map(Content::try_from)
.collect())(fullmail),
Ok(( Ok((
&b"This is a multipart message.\n\n"[..], &b"This is a multipart message.\n\n"[..],
vec![ vec![

View file

@ -13,17 +13,17 @@ pub mod r#type;
use std::fmt; use std::fmt;
use std::marker::PhantomData; use std::marker::PhantomData;
use crate::header;
use crate::imf::identification::MessageID; use crate::imf::identification::MessageID;
use crate::mime::field::Content; use crate::mime::field::Content;
use crate::mime::mechanism::Mechanism; use crate::mime::mechanism::Mechanism;
use crate::mime::r#type::{AnyType, NaiveType}; use crate::mime::r#type::{AnyType, NaiveType};
use crate::header;
use crate::text::misc_token::Unstructured; //Multipart, Message, Text, Binary}; 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_type: T, pub interpreted_type: T,
pub fields: NaiveMIME<'a> pub fields: NaiveMIME<'a>,
} }
impl<'a> Default for MIME<'a, r#type::DeductibleText> { impl<'a> Default for MIME<'a, r#type::DeductibleText> {
fn default() -> Self { fn default() -> Self {
@ -80,9 +80,8 @@ impl<'a> fmt::Debug for NaiveMIME<'a> {
impl<'a> FromIterator<Content<'a>> for NaiveMIME<'a> { impl<'a> FromIterator<Content<'a>> for NaiveMIME<'a> {
fn from_iter<I: IntoIterator<Item = Content<'a>>>(it: I) -> Self { fn from_iter<I: IntoIterator<Item = Content<'a>>>(it: I) -> Self {
it.into_iter().fold( it.into_iter()
NaiveMIME::default(), .fold(NaiveMIME::default(), |mut section, field| {
|mut section, field| {
match field { match field {
Content::Type(v) => section.ctype = Some(v), Content::Type(v) => section.ctype = Some(v),
Content::TransferEncoding(v) => section.transfer_encoding = v, Content::TransferEncoding(v) => section.transfer_encoding = v,
@ -90,25 +89,29 @@ impl<'a> FromIterator<Content<'a>> for NaiveMIME<'a> {
Content::Description(v) => section.description = Some(v), Content::Description(v) => section.description = Some(v),
}; };
section section
}, })
)
} }
} }
impl<'a> NaiveMIME<'a> { impl<'a> NaiveMIME<'a> {
pub fn with_kv(mut self, fields: Vec<header::Field<'a>>) -> Self { pub fn with_kv(mut self, fields: Vec<header::Field<'a>>) -> Self {
self.kv = fields; self self.kv = fields;
self
} }
pub fn with_raw(mut self, raw: &'a [u8]) -> Self { pub fn with_raw(mut self, raw: &'a [u8]) -> Self {
self.raw = raw; self self.raw = raw;
self
} }
pub fn to_interpreted<T: WithDefaultType>(self) -> AnyMIME<'a> { pub fn to_interpreted<T: WithDefaultType>(self) -> AnyMIME<'a> {
self.ctype.as_ref().map(|c| c.to_type()).unwrap_or(T::default_type()).to_mime(self).into() self.ctype
.as_ref()
.map(|c| c.to_type())
.unwrap_or(T::default_type())
.to_mime(self)
.into()
} }
} }
pub trait WithDefaultType { pub trait WithDefaultType {
fn default_type() -> AnyType; fn default_type() -> AnyType;
} }

View file

@ -1,4 +1,3 @@
use std::fmt;
use nom::{ use nom::{
bytes::complete::tag, bytes::complete::tag,
combinator::{map, opt}, combinator::{map, opt},
@ -6,11 +5,12 @@ use nom::{
sequence::{preceded, terminated, tuple}, sequence::{preceded, terminated, tuple},
IResult, IResult,
}; };
use std::fmt;
use crate::mime::charset::EmailCharset; use crate::mime::charset::EmailCharset;
use crate::mime::{AnyMIME, NaiveMIME, MIME};
use crate::text::misc_token::{mime_word, MIMEWord}; use crate::text::misc_token::{mime_word, MIMEWord};
use crate::text::words::mime_atom; use crate::text::words::mime_atom;
use crate::mime::{AnyMIME, MIME, NaiveMIME};
// --------- NAIVE TYPE // --------- NAIVE TYPE
#[derive(PartialEq, Clone)] #[derive(PartialEq, Clone)]
@ -93,15 +93,26 @@ impl<'a> From<&'a NaiveType<'a>> for AnyType {
impl<'a> AnyType { impl<'a> AnyType {
pub fn to_mime(self, fields: NaiveMIME<'a>) -> AnyMIME<'a> { pub fn to_mime(self, fields: NaiveMIME<'a>) -> AnyMIME<'a> {
match self { match self {
Self::Multipart(interpreted_type) => AnyMIME::Mult(MIME::<Multipart> { interpreted_type, fields }), Self::Multipart(interpreted_type) => AnyMIME::Mult(MIME::<Multipart> {
Self::Message(interpreted_type) => AnyMIME::Msg(MIME::<DeductibleMessage> { interpreted_type, fields }), interpreted_type,
Self::Text(interpreted_type) => AnyMIME::Txt(MIME::<DeductibleText> { interpreted_type, fields }), fields,
Self::Binary(interpreted_type) => AnyMIME::Bin(MIME::<Binary> { interpreted_type, fields }), }),
Self::Message(interpreted_type) => AnyMIME::Msg(MIME::<DeductibleMessage> {
interpreted_type,
fields,
}),
Self::Text(interpreted_type) => AnyMIME::Txt(MIME::<DeductibleText> {
interpreted_type,
fields,
}),
Self::Binary(interpreted_type) => AnyMIME::Bin(MIME::<Binary> {
interpreted_type,
fields,
}),
} }
} }
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum Deductible<T: Default> { pub enum Deductible<T: Default> {
Inferred(T), Inferred(T),
@ -158,7 +169,8 @@ impl ToString for MultipartSubtype {
Self::Parallel => "parallel", Self::Parallel => "parallel",
Self::Report => "report", Self::Report => "report",
Self::Unknown => "mixed", Self::Unknown => "mixed",
}.into() }
.into()
} }
} }
impl<'a> From<&NaiveType<'a>> for MultipartSubtype { impl<'a> From<&NaiveType<'a>> for MultipartSubtype {
@ -174,8 +186,6 @@ impl<'a> From<&NaiveType<'a>> for MultipartSubtype {
} }
} }
#[derive(Debug, PartialEq, Default, Clone)] #[derive(Debug, PartialEq, Default, Clone)]
pub enum MessageSubtype { pub enum MessageSubtype {
#[default] #[default]
@ -191,7 +201,8 @@ impl ToString for MessageSubtype {
Self::Partial => "partial", Self::Partial => "partial",
Self::External => "external", Self::External => "external",
Self::Unknown => "rfc822", Self::Unknown => "rfc822",
}.into() }
.into()
} }
} }
@ -203,17 +214,25 @@ pub struct Message {
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 { subtype: MessageSubtype::RFC822 }, b"rfc822" => Self {
b"partial" => Self { subtype: MessageSubtype::Partial }, subtype: MessageSubtype::RFC822,
b"external" => Self { subtype: MessageSubtype::External }, },
_ => Self { subtype: MessageSubtype::Unknown }, b"partial" => Self {
subtype: MessageSubtype::Partial,
},
b"external" => Self {
subtype: MessageSubtype::External,
},
_ => Self {
subtype: MessageSubtype::Unknown,
},
} }
} }
} }
impl From<Deductible<Message>> for Message { impl From<Deductible<Message>> for Message {
fn from(d: Deductible<Message>) -> Self { fn from(d: Deductible<Message>) -> Self {
match d { match d {
Deductible::Inferred(t) | Deductible::Explicit(t) => t Deductible::Inferred(t) | Deductible::Explicit(t) => t,
} }
} }
} }
@ -240,7 +259,7 @@ impl<'a> From<&NaiveType<'a>> for Text {
impl From<Deductible<Text>> for Text { impl From<Deductible<Text>> for Text {
fn from(d: Deductible<Text>) -> Self { fn from(d: Deductible<Text>) -> Self {
match d { match d {
Deductible::Inferred(t) | Deductible::Explicit(t) => t Deductible::Inferred(t) | Deductible::Explicit(t) => t,
} }
} }
} }
@ -257,7 +276,8 @@ impl ToString for TextSubtype {
match self { match self {
Self::Plain | Self::Unknown => "plain", Self::Plain | Self::Unknown => "plain",
Self::Html => "html", Self::Html => "html",
}.into() }
.into()
} }
} }
impl<'a> From<&NaiveType<'a>> for TextSubtype { impl<'a> From<&NaiveType<'a>> for TextSubtype {
@ -277,8 +297,8 @@ pub struct Binary {}
mod tests { mod tests {
use super::*; use super::*;
use crate::mime::charset::EmailCharset; use crate::mime::charset::EmailCharset;
use crate::text::quoted::QuotedString;
use crate::mime::r#type::Deductible; use crate::mime::r#type::Deductible;
use crate::text::quoted::QuotedString;
#[test] #[test]
fn test_parameter() { fn test_parameter() {
@ -336,7 +356,12 @@ 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(Deductible::Explicit(Message { subtype: MessageSubtype::RFC822 }))); assert_eq!(
nt.to_type(),
AnyType::Message(Deductible::Explicit(Message {
subtype: MessageSubtype::RFC822
}))
);
} }
#[test] #[test]

View file

@ -1,12 +1,12 @@
use std::fmt;
use nom::IResult; use nom::IResult;
use std::fmt;
use crate::header; use crate::header;
use crate::imf; use crate::imf;
use crate::mime; use crate::mime;
use crate::part::{self, AnyPart}; use crate::part::{self, AnyPart};
use crate::text::boundary::{boundary, Delimiter};
use crate::pointers; use crate::pointers;
use crate::text::boundary::{boundary, Delimiter};
//--- Multipart //--- Multipart
#[derive(PartialEq)] #[derive(PartialEq)]
@ -21,8 +21,14 @@ impl<'a> fmt::Debug for Multipart<'a> {
fmt.debug_struct("part::Multipart") fmt.debug_struct("part::Multipart")
.field("mime", &self.mime) .field("mime", &self.mime)
.field("children", &self.children) .field("children", &self.children)
.field("raw_part_inner", &String::from_utf8_lossy(self.raw_part_inner)) .field(
.field("raw_part_outer", &String::from_utf8_lossy(self.raw_part_outer)) "raw_part_inner",
&String::from_utf8_lossy(self.raw_part_inner),
)
.field(
"raw_part_outer",
&String::from_utf8_lossy(self.raw_part_outer),
)
.finish() .finish()
} }
} }
@ -76,7 +82,10 @@ pub fn multipart<'a>(
mime: m.clone(), mime: m.clone(),
children: mparts, children: mparts,
raw_part_inner: pointers::parsed(inner_orig, inp), raw_part_inner: pointers::parsed(inner_orig, inp),
raw_part_outer: pointers::parsed(outer_orig, &outer_orig[outer_orig.len()..]), raw_part_outer: pointers::parsed(
outer_orig,
&outer_orig[outer_orig.len()..],
),
}, },
)) ))
} }
@ -93,19 +102,21 @@ pub fn multipart<'a>(
.into_iter() .into_iter()
.collect::<mime::NaiveMIME>(); .collect::<mime::NaiveMIME>();
let mime = mime let mime = mime.with_kv(fields).with_raw(raw_hdrs);
.with_kv(fields)
.with_raw(raw_hdrs);
(input_eom, mime) (input_eom, mime)
}, }
Err(_) => (input, mime::NaiveMIME::default()), Err(_) => (input, mime::NaiveMIME::default()),
}; };
// interpret mime according to context // interpret mime according to context
let mime = match m.interpreted_type.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
_ => naive_mime.to_interpreted::<mime::WithGenericDefault>().into(), .to_interpreted::<mime::WithDigestDefault>()
.into(),
_ => naive_mime
.to_interpreted::<mime::WithGenericDefault>()
.into(),
}; };
// parse raw part // parse raw part
@ -168,7 +179,10 @@ pub fn message<'a>(
let imf = imf.with_kv(headers); let imf = imf.with_kv(headers);
// interpret headers to choose a mime type // interpret headers to choose a mime type
let in_mime = naive_mime.with_raw(raw_headers).to_interpreted::<mime::WithGenericDefault>().into(); let in_mime = naive_mime
.with_raw(raw_headers)
.to_interpreted::<mime::WithGenericDefault>()
.into();
//--------------- //---------------
// parse a part following this mime specification // parse a part following this mime specification
@ -183,7 +197,9 @@ pub fn message<'a>(
Message { Message {
mime: m.clone(), mime: m.clone(),
imf, imf,
raw_part, raw_headers, raw_body, raw_part,
raw_headers,
raw_body,
child: Box::new(part), child: Box::new(part),
}, },
)) ))
@ -196,7 +212,7 @@ mod tests {
use crate::part::discrete::Text; use crate::part::discrete::Text;
use crate::part::AnyPart; use crate::part::AnyPart;
use crate::text::encoding::{Base64Word, EncodedWord, QuotedChunk, QuotedWord}; use crate::text::encoding::{Base64Word, EncodedWord, QuotedChunk, QuotedWord};
use crate::text::misc_token::{Phrase, UnstrToken, Unstructured, Word, MIMEWord}; use crate::text::misc_token::{MIMEWord, Phrase, UnstrToken, Unstructured, Word};
use crate::text::quoted::QuotedString; use crate::text::quoted::QuotedString;
use chrono::{FixedOffset, TimeZone}; use chrono::{FixedOffset, TimeZone};

View file

@ -12,10 +12,7 @@ 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.mime) .field("mime", &self.mime)
.field( .field("body", &String::from_utf8_lossy(self.body))
"body",
&String::from_utf8_lossy(self.body),
)
.finish() .finish()
} }
} }
@ -30,10 +27,7 @@ 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.mime) .field("mime", &self.mime)
.field( .field("body", &String::from_utf8_lossy(self.body))
"body",
&String::from_utf8_lossy(self.body),
)
.finish() .finish()
} }
} }

View file

@ -4,7 +4,10 @@ use crate::mime;
pub fn split_and_build<'a>(v: &Vec<header::Field<'a>>) -> (mime::NaiveMIME<'a>, imf::Imf<'a>) { pub fn split_and_build<'a>(v: &Vec<header::Field<'a>>) -> (mime::NaiveMIME<'a>, imf::Imf<'a>) {
let (mimev, imfv) = v.iter().fold( let (mimev, imfv) = v.iter().fold(
(Vec::<mime::field::Content>::new(), Vec::<imf::field::Field>::new()), (
Vec::<mime::field::Content>::new(),
Vec::<imf::field::Field>::new(),
),
|(mut mime, mut imf), f| { |(mut mime, mut imf), f| {
if let Ok(m) = mime::field::Content::try_from(f) { if let Ok(m) = mime::field::Content::try_from(f) {
mime.push(m); mime.push(m);
@ -12,7 +15,7 @@ pub fn split_and_build<'a>(v: &Vec<header::Field<'a>>) -> (mime::NaiveMIME<'a>,
imf.push(i); imf.push(i);
} }
(mime, imf) (mime, imf)
} },
); );
let fmime = mimev.into_iter().collect::<mime::NaiveMIME>(); let fmime = mimev.into_iter().collect::<mime::NaiveMIME>();

View file

@ -80,18 +80,19 @@ pub fn anypart<'a>(m: AnyMIME<'a>) -> impl FnOnce(&'a [u8]) -> IResult<&'a [u8],
move |input| { move |input| {
let part = match m { let part = match m {
AnyMIME::Mult(a) => multipart(a)(input) AnyMIME::Mult(a) => multipart(a)(input)
.map(|(_, multi)| .map(|(_, multi)| multi.into())
multi.into())
.unwrap_or(AnyPart::Txt(Text { .unwrap_or(AnyPart::Txt(Text {
mime: mime::MIME::<mime::r#type::DeductibleText>::default(), mime: mime::MIME::<mime::r#type::DeductibleText>::default(),
body: input, body: input,
})), })),
AnyMIME::Msg(a) => message(a)(input) AnyMIME::Msg(a) => {
message(a)(input)
.map(|(_, msg)| msg.into()) .map(|(_, msg)| msg.into())
.unwrap_or(AnyPart::Txt(Text { .unwrap_or(AnyPart::Txt(Text {
mime: mime::MIME::<mime::r#type::DeductibleText>::default(), mime: mime::MIME::<mime::r#type::DeductibleText>::default(),
body: input, body: input,
})), }))
}
AnyMIME::Txt(a) => AnyPart::Txt(Text { AnyMIME::Txt(a) => AnyPart::Txt(Text {
mime: a, mime: a,
body: input, body: input,

View file

@ -22,7 +22,12 @@ use nom::{
/// \r or \n is allowed nowhere else, so we also add this support. /// \r or \n is allowed nowhere else, so we also add this support.
pub fn obs_crlf(input: &[u8]) -> IResult<&[u8], &[u8]> { pub fn obs_crlf(input: &[u8]) -> IResult<&[u8], &[u8]> {
alt((tag(ascii::CRLF), tag(ascii::CRCRLF), tag(&[ascii::CR]), tag(&[ascii::LF])))(input) alt((
tag(ascii::CRLF),
tag(ascii::CRCRLF),
tag(&[ascii::CR]),
tag(&[ascii::LF]),
))(input)
} }
/// ```abnf /// ```abnf