From 07f969d109577df5b7a76b3770bfe3fe54456077 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Mon, 24 Jul 2023 19:19:49 +0200 Subject: [PATCH] Refactor how headers are handled --- examples/simple.rs | 4 ++-- src/header.rs | 46 +++++++++++++++++++++---------------------- src/imf/field.rs | 29 +++++++++++++++------------ src/lib.rs | 6 +++--- src/mime/field.rs | 11 +++++++---- src/part/composite.rs | 16 +++++++-------- src/part/field.rs | 23 +++++++--------------- src/part/mod.rs | 1 - 8 files changed, 65 insertions(+), 71 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index 38c7b78..44bdc69 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -13,8 +13,8 @@ between the header information and the body of the message."#; let (_, header) = eml_codec::imf(input).unwrap(); println!( "{} just sent you an email with subject \"{}\"", - header.from[0].to_string(), - header.subject.unwrap().to_string(), + header.imf.from[0].to_string(), + header.imf.subject.unwrap().to_string(), ); // if you like to also parse the body/content diff --git a/src/header.rs b/src/header.rs index 637ef9d..861103e 100644 --- a/src/header.rs +++ b/src/header.rs @@ -6,7 +6,7 @@ use nom::{ bytes::complete::{tag, tag_no_case, take_while1}, character::complete::space0, combinator::map, - multi::many0, + multi::{fold_many0}, sequence::{pair, terminated, tuple}, IResult, }; @@ -14,39 +14,37 @@ use nom::{ #[derive(Debug, PartialEq)] pub enum CompField<'a, T> { Known(T), - Unknown(&'a [u8], Unstructured<'a>), + Unknown(Kv<'a>), Bad(&'a [u8]), } #[derive(Debug, PartialEq)] -pub struct CompFieldList<'a, T>(pub Vec>); -impl<'a, T> CompFieldList<'a, T> { - pub fn known(self) -> Vec { - self.0 - .into_iter() - .filter_map(|v| match v { - CompField::Known(f) => Some(f), - _ => None, - }) - .collect() - } -} +pub struct Kv<'a>(&'a [u8], Unstructured<'a>); + pub fn header<'a, T>( fx: impl Fn(&'a [u8]) -> IResult<&'a [u8], T> + Copy, -) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], CompFieldList> { +) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], (Vec::, Vec::, Vec<&'a [u8]>)> { move |input| { - map( terminated( - many0(alt(( - map(fx, CompField::Known), - map(opt_field, |(k, v)| CompField::Unknown(k, v)), - map(foldable_line, CompField::Bad), - ))), + fold_many0( + alt(( + map(fx, CompField::Known), + map(opt_field, |(k, v)| CompField::Unknown(Kv(k, v))), + map(foldable_line, CompField::Bad), + )), + || (Vec::::new(), Vec::::new(), Vec::<&'a [u8]>::new()), + |(mut known, mut unknown, mut bad), item| { + match item { + CompField::Known(v) => known.push(v), + CompField::Unknown(v) => unknown.push(v), + CompField::Bad(v) => bad.push(v), + }; + (known, unknown, bad) + } + ), obs_crlf, - ), - CompFieldList, - )(input) + )(input) } } diff --git a/src/imf/field.rs b/src/imf/field.rs index 9ec50c6..61873bc 100644 --- a/src/imf/field.rs +++ b/src/imf/field.rs @@ -6,7 +6,7 @@ use nom::{ IResult, }; -use crate::header::{field_name, header}; +use crate::header::{field_name, header, self}; use crate::imf::address::{address_list, mailbox_list, nullable_address_list, AddressList}; use crate::imf::datetime::section as date; use crate::imf::identification::{msg_id, msg_list, MessageID, MessageIDList}; @@ -50,14 +50,6 @@ pub enum Field<'a> { MIMEVersion(Version), } -#[derive(Debug, PartialEq)] -pub struct FieldList<'a>(pub Vec>); -impl<'a> FieldList<'a> { - pub fn imf(self) -> Imf<'a> { - Imf::from_iter(self.0) - } -} - pub fn field(input: &[u8]) -> IResult<&[u8], Field> { terminated( alt(( @@ -88,8 +80,19 @@ pub fn field(input: &[u8]) -> IResult<&[u8], Field> { )(input) } -pub fn imf(input: &[u8]) -> IResult<&[u8], Imf> { - map(header(field), |v| FieldList(v.known()).imf())(input) +#[derive(Debug, PartialEq)] +pub struct Header<'a> { + pub imf: Imf<'a>, + pub ext: Vec>, + pub bad: Vec<&'a [u8]>, +} + +pub fn imf(input: &[u8]) -> IResult<&[u8], Header> { + map(header(field), |(known, unknown, bad)| Header { + imf: Imf::from_iter(known), + ext: unknown, + bad + })(input) } #[cfg(test)] @@ -114,7 +117,7 @@ between the header information and the body of the message."; imf(fullmail), Ok(( &b"This is the plain text body of the message. Note the blank line\nbetween the header information and the body of the message."[..], - Imf { + Header { bad: vec![], ext: vec![], imf: Imf { date: Some(FixedOffset::east_opt(2 * 3600).unwrap().with_ymd_and_hms(2023, 3, 7, 8, 0, 0).unwrap()), from: vec![MailboxRef { name: None, @@ -138,7 +141,7 @@ between the header information and the body of the message."; UnstrToken::Plain(&b"message"[..]), ])), ..Imf::default() - } + }} )), ) } diff --git a/src/lib.rs b/src/lib.rs index 41cf312..118086d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,10 +90,10 @@ pub fn email(input: &[u8]) -> IResult<&[u8], part::composite::Message> { /// let (_, header) = eml_codec::imf(input).unwrap(); /// println!( /// "{} just sent you an email with subject \"{}\"", -/// header.from[0].to_string(), -/// header.subject.unwrap().to_string(), +/// header.imf.from[0].to_string(), +/// header.imf.subject.unwrap().to_string(), /// ); /// ``` -pub fn imf(input: &[u8]) -> IResult<&[u8], imf::Imf> { +pub fn imf(input: &[u8]) -> IResult<&[u8], imf::field::Header> { imf::field::imf(input) } diff --git a/src/mime/field.rs b/src/mime/field.rs index a9d52e5..b672618 100644 --- a/src/mime/field.rs +++ b/src/mime/field.rs @@ -5,7 +5,7 @@ use nom::{ IResult, }; -use crate::header::{field_name, CompFieldList}; +use crate::header::{field_name}; use crate::imf::identification::{msg_id, MessageID}; use crate::mime::mechanism::{mechanism, Mechanism}; use crate::mime::{AnyMIMEWithDefault, WithDefaultType}; @@ -48,10 +48,13 @@ impl<'a> Content<'a> { } } -impl<'a> CompFieldList<'a, Content<'a>> { +/*impl<'a> CompFieldList<'a, Content<'a>> { pub fn to_mime (self) -> AnyMIMEWithDefault<'a, T> { self.known().into_iter().collect::>() } +}*/ +pub fn to_mime<'a, T: WithDefaultType>(list: Vec>) -> AnyMIMEWithDefault<'a, T> { + list.into_iter().collect::>() } pub fn content(input: &[u8]) -> IResult<&[u8], Content> { @@ -75,7 +78,7 @@ pub fn content(input: &[u8]) -> IResult<&[u8], Content> { #[cfg(test)] mod tests { use super::*; - use crate::header::{header, CompFieldList}; + use crate::header::{header}; use crate::mime::charset::EmailCharset; use crate::mime::r#type::*; use crate::text::misc_token::MIMEWord; @@ -118,7 +121,7 @@ This is a multipart message. .as_bytes(); assert_eq!( - map(header(content), CompFieldList::known)(fullmail), + map(header(content), |(k, _, _)| k)(fullmail), Ok(( &b"This is a multipart message.\n\n"[..], vec![ diff --git a/src/part/composite.rs b/src/part/composite.rs index eb38bcb..f6a2cfd 100644 --- a/src/part/composite.rs +++ b/src/part/composite.rs @@ -1,9 +1,9 @@ use nom::IResult; -use crate::header::{header, CompFieldList}; -use crate::imf::{self as imf}; +use crate::header::{header, self}; +use crate::imf; use crate::mime; -use crate::part::{self, AnyPart}; +use crate::part::{self, AnyPart, field::MixedField}; use crate::text::boundary::{boundary, Delimiter}; //--- Multipart @@ -58,10 +58,10 @@ pub fn multipart<'a>( }; // parse mime headers - let (input, fields) = header(mime::field::content)(input)?; + let (input, (known, unknown, bad)) = header(mime::field::content)(input)?; let mime = match m.0.subtype { - mime::r#type::MultipartSubtype::Digest => fields.to_mime::().into(), - _ => fields.to_mime::().into(), + mime::r#type::MultipartSubtype::Digest => mime::field::to_mime::(known).into(), + _ => mime::field::to_mime::(known).into(), }; // parse raw part @@ -95,9 +95,9 @@ pub fn message<'a>( m: mime::Message<'a>, ) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Message<'a>> { move |input: &[u8]| { - let (input, fields): (_, CompFieldList) = + let (input, (known, unknown, bad)): (_, (Vec::, Vec, Vec<&[u8]>)) = header(part::field::mixed_field)(input)?; - let (in_mime, imf) = fields.sections::(); + let (in_mime, imf) = part::field::sections::(known); let part = part::to_anypart(in_mime, input); diff --git a/src/part/field.rs b/src/part/field.rs index 542390d..0046085 100644 --- a/src/part/field.rs +++ b/src/part/field.rs @@ -2,7 +2,6 @@ use nom::{branch::alt, combinator::map, IResult}; use crate::imf; use crate::mime; -use crate::part::CompFieldList; pub enum MixedField<'a> { MIME(mime::field::Content<'a>), @@ -35,22 +34,14 @@ impl<'a> MixedField<'a> { } } } -impl<'a> CompFieldList<'a, MixedField<'a>> { - pub fn sections(self) -> (mime::AnyMIME<'a>, imf::Imf<'a>) { - let k = self.known(); - let (v1, v2): (Vec, Vec) = - k.into_iter().partition(|v| v.mime().is_some()); - let mime = v1 - .into_iter() - .filter_map(|v| v.to_mime()) - .collect::>(); - let imf = v2 - .into_iter() - .filter_map(|v| v.to_imf()) - .collect::(); - (mime.into(), imf) - } + +pub fn sections<'a, T: mime::WithDefaultType>(list: Vec>) -> (mime::AnyMIME<'a>, imf::Imf<'a>) { + let (v1, v2): (Vec, Vec<_>) = list.into_iter().partition(|v| v.mime().is_some()); + let mime = v1.into_iter().flat_map(MixedField::to_mime).collect::>(); + let imf = v2.into_iter().flat_map(MixedField::to_imf).collect::(); + (mime.into(), imf) } + pub fn mixed_field(input: &[u8]) -> IResult<&[u8], MixedField> { alt(( map(mime::field::content, MixedField::MIME), diff --git a/src/part/mod.rs b/src/part/mod.rs index 32d55f2..977b39b 100644 --- a/src/part/mod.rs +++ b/src/part/mod.rs @@ -16,7 +16,6 @@ use nom::{ IResult, }; -use crate::header::CompFieldList; use crate::mime; use crate::mime::AnyMIME; use crate::part::{