eml-codec/src/header.rs

114 lines
3.1 KiB
Rust
Raw Normal View History

2023-07-24 12:41:21 +00:00
2023-07-23 14:37:47 +00:00
use crate::text::misc_token::{unstructured, Unstructured};
use crate::text::whitespace::{foldable_line, obs_crlf};
2023-07-20 14:26:59 +00:00
use nom::{
2023-07-22 11:51:19 +00:00
branch::alt,
2023-07-23 14:37:47 +00:00
bytes::complete::{tag, tag_no_case, take_while1},
2023-07-20 14:26:59 +00:00
character::complete::space0,
2023-08-30 09:35:29 +00:00
combinator::{into, map},
multi::{fold_many0, many0},
2023-07-23 07:46:57 +00:00
sequence::{pair, terminated, tuple},
2023-07-23 14:37:47 +00:00
IResult,
2023-07-20 14:26:59 +00:00
};
#[derive(Debug, PartialEq)]
pub enum CompField<'a, T> {
Known(T),
2023-07-24 17:19:49 +00:00
Unknown(Kv<'a>),
2023-07-20 14:26:59 +00:00
Bad(&'a [u8]),
}
2023-07-24 20:08:13 +00:00
#[derive(Debug, PartialEq, Clone)]
pub struct Kv<'a>(pub &'a [u8], pub Unstructured<'a>);
2023-08-30 09:35:29 +00:00
impl<'a> From<(&'a [u8], Unstructured<'a>)> for Kv<'a> {
fn from(pair: (&'a [u8], Unstructured<'a>)) -> Self {
Self(pair.0, pair.1)
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Field<'a> {
Good(Kv<'a>),
Bad(&'a [u8]),
}
impl<'a> From<Kv<'a>> for Field<'a> {
fn from(kv: Kv<'a>) -> Self {
Self::Good(kv)
}
}
impl<'a> From<&'a [u8]> for Field<'a> {
fn from(bad: &'a [u8]) -> Self {
Self::Bad(bad)
}
}
/// Parse headers as key/values
pub fn header_kv(input: &[u8]) -> IResult<&[u8], Vec<Field>> {
terminated(
many0(
alt((
into(opt_field),
into(foldable_line),
))
),
obs_crlf
)(input)
}
2023-07-24 17:19:49 +00:00
2023-07-20 14:26:59 +00:00
2023-07-23 14:37:47 +00:00
pub fn header<'a, T>(
fx: impl Fn(&'a [u8]) -> IResult<&'a [u8], T> + Copy,
2023-07-24 17:19:49 +00:00
) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], (Vec::<T>, Vec::<Kv>, Vec<&'a [u8]>)> {
2023-07-23 14:37:47 +00:00
move |input| {
terminated(
2023-07-24 17:19:49 +00:00
fold_many0(
alt((
map(fx, CompField::Known),
2023-08-30 09:35:29 +00:00
map(opt_field, CompField::Unknown),
2023-07-24 17:19:49 +00:00
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)
}
),
2023-07-23 14:37:47 +00:00
obs_crlf,
2023-07-24 17:19:49 +00:00
)(input)
2023-07-23 14:37:47 +00:00
}
2023-07-22 11:51:19 +00:00
}
2023-07-20 14:26:59 +00:00
pub fn field_name<'a>(name: &'static [u8]) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], &'a [u8]> {
2023-07-23 14:37:47 +00:00
move |input| terminated(tag_no_case(name), tuple((space0, tag(b":"), space0)))(input)
2023-07-20 14:26:59 +00:00
}
2023-08-30 09:35:29 +00:00
pub fn field_any(input: &[u8]) -> IResult<&[u8], &[u8]> {
terminated(
take_while1(|c| (0x21..=0x7E).contains(&c) && c != 0x3A),
tuple((space0, tag(b":"), space0)),
)(input)
}
2023-07-20 14:26:59 +00:00
/// Optional field
///
/// ```abnf
/// field = field-name ":" unstructured CRLF
/// field-name = 1*ftext
/// ftext = %d33-57 / ; Printable US-ASCII
/// %d59-126 ; characters not including
/// ; ":".
/// ```
2023-08-30 09:35:29 +00:00
pub fn opt_field(input: &[u8]) -> IResult<&[u8], Kv> {
2023-07-22 11:51:19 +00:00
terminated(
2023-08-30 09:35:29 +00:00
into(pair(
field_any,
2023-07-22 11:51:19 +00:00
unstructured,
2023-08-30 09:35:29 +00:00
)),
2023-07-23 14:37:47 +00:00
obs_crlf,
)(input)
2023-07-20 14:26:59 +00:00
}