pub mod quoted_characters {
use super::obsolete::obs_qp;
use abnf_core::streaming::{is_VCHAR, WSP};
use nom::{
branch::alt,
bytes::streaming::{tag, take_while_m_n},
combinator::recognize,
sequence::tuple,
IResult,
};
pub fn quoted_pair(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = alt((
recognize(tuple((
tag(b"\\"),
alt((take_while_m_n(1, 1, is_VCHAR), WSP)),
))),
obs_qp,
));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
}
pub mod folding_ws_and_comment {
use nom::IResult;
pub fn FWS(_input: &[u8]) -> IResult<&[u8], &[u8]> {
unimplemented!()
}
pub fn CFWS(_input: &[u8]) -> IResult<&[u8], &[u8]> {
unimplemented!()
}
}
pub mod atom {
use super::folding_ws_and_comment::CFWS;
use abnf_core::streaming::{is_ALPHA, is_DIGIT};
use nom::{
bytes::streaming::{tag, take_while1},
combinator::{opt, recognize},
multi::many0,
sequence::tuple,
IResult,
};
pub fn is_atext(byte: u8) -> bool {
let allowed = b"!#$%&'*+-/=?^_`{|}~";
is_ALPHA(byte) || is_DIGIT(byte) || allowed.contains(&byte)
}
pub fn atom(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((opt(CFWS), take_while1(is_atext), opt(CFWS)));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn dot_atom_text(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((
take_while1(is_atext),
many0(tuple((tag(b"."), take_while1(is_atext)))),
));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn dot_atom(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((opt(CFWS), dot_atom_text, opt(CFWS)));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
}
pub mod quoted_strings {
use super::{
folding_ws_and_comment::{CFWS, FWS},
obsolete::is_obs_qtext,
quoted_characters::quoted_pair,
};
use abnf_core::streaming::DQUOTE;
use nom::{
branch::alt,
bytes::streaming::take_while_m_n,
combinator::{opt, recognize},
multi::many0,
sequence::tuple,
IResult,
};
pub fn is_qtext(byte: u8) -> bool {
match byte {
33 | 35..=91 | 93..=126 => true,
_ if is_obs_qtext(byte) => true,
_ => false,
}
}
pub fn qcontent(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = alt((take_while_m_n(1, 1, is_qtext), quoted_pair));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn quoted_string(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((
opt(CFWS),
DQUOTE,
many0(tuple((opt(FWS), qcontent))),
opt(FWS),
DQUOTE,
opt(CFWS),
));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
}
pub mod miscellaneous {
use super::{atom::atom, quoted_strings::quoted_string};
use nom::{branch::alt, IResult};
pub fn word(input: &[u8]) -> IResult<&[u8], &[u8]> {
alt((atom, quoted_string))(input)
}
}
pub mod datetime {
use super::folding_ws_and_comment::{CFWS, FWS};
use abnf_core::streaming::is_DIGIT;
use nom::{
branch::alt,
bytes::streaming::{tag, tag_no_case, take_while_m_n},
combinator::{opt, recognize},
sequence::tuple,
IResult,
};
pub fn date_time(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((opt(tuple((day_of_week, tag(b",")))), date, time, opt(CFWS)));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn day_of_week(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((opt(FWS), day_name));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn day_name(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = alt((
tag_no_case(b"Mon"),
tag_no_case(b"Tue"),
tag_no_case(b"Wed"),
tag_no_case(b"Thu"),
tag_no_case(b"Fri"),
tag_no_case(b"Sat"),
tag_no_case(b"Sun"),
));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn date(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((day, month, year));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn day(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((opt(FWS), take_while_m_n(1, 2, is_DIGIT), FWS));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn month(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = alt((
tag_no_case(b"Jan"),
tag_no_case(b"Feb"),
tag_no_case(b"Mar"),
tag_no_case(b"Apr"),
tag_no_case(b"May"),
tag_no_case(b"Jun"),
tag_no_case(b"Jul"),
tag_no_case(b"Aug"),
tag_no_case(b"Sep"),
tag_no_case(b"Oct"),
tag_no_case(b"Nov"),
tag_no_case(b"Dec"),
));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn year(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((FWS, take_while_m_n(4, 8, is_DIGIT), FWS));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn time(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((time_of_day, zone));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn time_of_day(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((hour, tag(b":"), minute, opt(tuple((tag(b":"), second)))));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn hour(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = take_while_m_n(2, 2, is_DIGIT);
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn minute(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = take_while_m_n(2, 2, is_DIGIT);
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn second(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = take_while_m_n(2, 2, is_DIGIT);
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn zone(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((
FWS,
alt((tag(b"+"), tag(b"-"))),
take_while_m_n(4, 4, is_DIGIT),
));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
}
pub mod addr_spec {
use super::{
atom::dot_atom,
folding_ws_and_comment::{CFWS, FWS},
obsolete::{obs_domain, obs_dtext, obs_local_part},
quoted_strings::quoted_string,
};
use nom::{
branch::alt,
bytes::streaming::{tag, take_while_m_n},
combinator::{opt, recognize},
multi::many0,
sequence::tuple,
IResult,
};
pub fn local_part(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = alt((dot_atom, quoted_string, obs_local_part));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn domain(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = alt((dot_atom, domain_literal, obs_domain));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn domain_literal(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((
opt(CFWS),
tag(b"["),
many0(tuple((opt(FWS), dtext))),
opt(FWS),
tag(b"]"),
opt(CFWS),
));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn dtext(input: &[u8]) -> IResult<&[u8], &[u8]> {
fn is_a(byte: u8) -> bool {
matches!(byte, 33..=90)
}
fn is_b(byte: u8) -> bool {
matches!(byte, 94..=126)
}
let parser = alt((
take_while_m_n(1, 1, is_a),
take_while_m_n(1, 1, is_b),
obs_dtext,
));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
}
pub mod identification {
use super::{
addr_spec::dtext,
atom::dot_atom_text,
folding_ws_and_comment::CFWS,
obsolete::{obs_id_left, obs_id_right},
};
use nom::{
branch::alt,
bytes::streaming::tag,
combinator::{opt, recognize},
multi::many0,
sequence::{delimited, tuple},
IResult,
};
pub fn msg_id(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((
opt(CFWS),
tag(b"<"),
id_left,
tag(b"@"),
id_right,
tag(b">"),
opt(CFWS),
));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn id_left(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = alt((dot_atom_text, obs_id_left));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn id_right(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = alt((dot_atom_text, no_fold_literal, obs_id_right));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn no_fold_literal(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = delimited(tag(b"["), many0(dtext), tag(b"]"));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
}
pub mod obsolete {
use super::{
addr_spec::{domain, local_part},
atom::atom,
miscellaneous::word,
quoted_characters::quoted_pair,
};
use abnf_core::streaming::{CR, LF};
use nom::{
branch::alt,
bytes::streaming::{tag, take_while_m_n},
combinator::recognize,
multi::many0,
sequence::tuple,
IResult,
};
pub fn is_obs_NO_WS_CTL(byte: u8) -> bool {
matches!(byte, 1..=8 | 11 | 12 | 14..=31 | 127)
}
pub fn is_obs_qtext(byte: u8) -> bool {
is_obs_NO_WS_CTL(byte)
}
pub fn obs_qp(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((
tag(b"\\"),
alt((
take_while_m_n(1, 1, |x| x == 0x00),
take_while_m_n(1, 1, is_obs_NO_WS_CTL),
LF,
CR,
)),
));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn obs_local_part(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((word, many0(tuple((tag(b"."), word)))));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn obs_domain(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((atom, many0(tuple((tag(b"."), atom)))));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn obs_dtext(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = alt((take_while_m_n(1, 1, is_obs_NO_WS_CTL), quoted_pair));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn obs_id_left(input: &[u8]) -> IResult<&[u8], &[u8]> {
local_part(input)
}
pub fn obs_id_right(input: &[u8]) -> IResult<&[u8], &[u8]> {
domain(input)
}
}