Refactor how headers are handled
This commit is contained in:
parent
7db49875ea
commit
07f969d109
8 changed files with 65 additions and 71 deletions
|
@ -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
|
||||
|
|
|
@ -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<CompField<'a, T>>);
|
||||
impl<'a, T> CompFieldList<'a, T> {
|
||||
pub fn known(self) -> Vec<T> {
|
||||
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<T>> {
|
||||
) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], (Vec::<T>, Vec::<Kv>, 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::<T>::new(), Vec::<Kv>::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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Field<'a>>);
|
||||
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<header::Kv<'a>>,
|
||||
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()
|
||||
}
|
||||
}}
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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<T: WithDefaultType> (self) -> AnyMIMEWithDefault<'a, T> {
|
||||
self.known().into_iter().collect::<AnyMIMEWithDefault<T>>()
|
||||
}
|
||||
}*/
|
||||
pub fn to_mime<'a, T: WithDefaultType>(list: Vec<Content<'a>>) -> AnyMIMEWithDefault<'a, T> {
|
||||
list.into_iter().collect::<AnyMIMEWithDefault<T>>()
|
||||
}
|
||||
|
||||
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![
|
||||
|
|
|
@ -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::<mime::WithDigestDefault>().into(),
|
||||
_ => fields.to_mime::<mime::WithGenericDefault>().into(),
|
||||
mime::r#type::MultipartSubtype::Digest => mime::field::to_mime::<mime::WithDigestDefault>(known).into(),
|
||||
_ => mime::field::to_mime::<mime::WithGenericDefault>(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<part::field::MixedField>) =
|
||||
let (input, (known, unknown, bad)): (_, (Vec::<MixedField>, Vec<header::Kv>, Vec<&[u8]>)) =
|
||||
header(part::field::mixed_field)(input)?;
|
||||
let (in_mime, imf) = fields.sections::<mime::WithGenericDefault>();
|
||||
let (in_mime, imf) = part::field::sections::<mime::WithGenericDefault>(known);
|
||||
|
||||
let part = part::to_anypart(in_mime, input);
|
||||
|
||||
|
|
|
@ -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<T: mime::WithDefaultType>(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::AnyMIMEWithDefault<T>>();
|
||||
let imf = v2
|
||||
.into_iter()
|
||||
.filter_map(|v| v.to_imf())
|
||||
.collect::<imf::Imf>();
|
||||
(mime.into(), imf)
|
||||
}
|
||||
|
||||
pub fn sections<'a, T: mime::WithDefaultType>(list: Vec<MixedField<'a>>) -> (mime::AnyMIME<'a>, imf::Imf<'a>) {
|
||||
let (v1, v2): (Vec<MixedField>, Vec<_>) = list.into_iter().partition(|v| v.mime().is_some());
|
||||
let mime = v1.into_iter().flat_map(MixedField::to_mime).collect::<mime::AnyMIMEWithDefault<T>>();
|
||||
let imf = v2.into_iter().flat_map(MixedField::to_imf).collect::<imf::Imf>();
|
||||
(mime.into(), imf)
|
||||
}
|
||||
|
||||
pub fn mixed_field(input: &[u8]) -> IResult<&[u8], MixedField> {
|
||||
alt((
|
||||
map(mime::field::content, MixedField::MIME),
|
||||
|
|
|
@ -16,7 +16,6 @@ use nom::{
|
|||
IResult,
|
||||
};
|
||||
|
||||
use crate::header::CompFieldList;
|
||||
use crate::mime;
|
||||
use crate::mime::AnyMIME;
|
||||
use crate::part::{
|
||||
|
|
Loading…
Reference in a new issue