#![allow(non_snake_case)]
use std::{borrow::Cow, str::from_utf8};
use abnf_core::streaming::{is_ALPHA, is_DIGIT, DQUOTE};
use nom::{
branch::alt,
bytes::streaming::{tag, take_while, take_while1, take_while_m_n},
character::streaming::digit1,
combinator::{map, map_res, opt, recognize},
multi::{many0, separated_list1},
sequence::{delimited, tuple},
IResult,
};
use crate::{parse::imf::atom::is_atext, types::AtomOrQuoted, utils::unescape_quoted};
pub mod address;
pub mod command;
pub mod imf;
pub mod response;
pub mod trace;
pub mod utils;
pub fn base64(input: &[u8]) -> IResult<&[u8], &str> {
let mut parser = map_res(
recognize(tuple((
take_while(is_base64_char),
opt(alt((tag("=="), tag("=")))),
))),
from_utf8,
);
let (remaining, base64) = parser(input)?;
Ok((remaining, base64))
}
fn is_base64_char(i: u8) -> bool {
is_ALPHA(i) || is_DIGIT(i) || i == b'+' || i == b'/'
}
pub fn number(input: &[u8]) -> IResult<&[u8], u32> {
map_res(map_res(digit1, from_utf8), str::parse::<u32>)(input) }
pub fn String(input: &[u8]) -> IResult<&[u8], AtomOrQuoted> {
alt((
map(Atom, |atom| AtomOrQuoted::Atom(atom.into())),
map(Quoted_string, |quoted| AtomOrQuoted::Quoted(quoted.into())),
))(input)
}
pub fn Atom(input: &[u8]) -> IResult<&[u8], &str> {
map_res(take_while1(is_atext), std::str::from_utf8)(input)
}
pub fn Quoted_string(input: &[u8]) -> IResult<&[u8], Cow<'_, str>> {
map(
delimited(
DQUOTE,
map_res(recognize(many0(QcontentSMTP)), std::str::from_utf8),
DQUOTE,
),
|s| unescape_quoted(s),
)(input)
}
pub fn QcontentSMTP(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = alt((take_while_m_n(1, 1, is_qtextSMTP), quoted_pairSMTP));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn is_qtextSMTP(byte: u8) -> bool {
matches!(byte, 32..=33 | 35..=91 | 93..=126)
}
pub fn quoted_pairSMTP(input: &[u8]) -> IResult<&[u8], &[u8]> {
fn is_value(byte: u8) -> bool {
byte == b'\\' || byte == b'\"'
}
let parser = tuple((tag("\\"), take_while_m_n(1, 1, is_value)));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn Domain(input: &[u8]) -> IResult<&[u8], &str> {
let parser = separated_list1(tag(b"."), sub_domain);
let (remaining, parsed) = map_res(recognize(parser), std::str::from_utf8)(input)?;
Ok((remaining, parsed))
}
pub fn sub_domain(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((take_while_m_n(1, 1, is_Let_dig), opt(Ldh_str)));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
pub fn is_Let_dig(byte: u8) -> bool {
is_ALPHA(byte) || is_DIGIT(byte)
}
pub fn Ldh_str(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = many0(alt((
take_while_m_n(1, 1, is_ALPHA),
take_while_m_n(1, 1, is_DIGIT),
recognize(tuple((tag(b"-"), take_while_m_n(1, 1, is_Let_dig)))),
)));
let (remaining, parsed) = recognize(parser)(input)?;
Ok((remaining, parsed))
}
#[cfg(test)]
pub mod test {
use super::sub_domain;
#[test]
fn test_subdomain() {
let (rem, parsed) = sub_domain(b"example???").unwrap();
assert_eq!(parsed, b"example");
assert_eq!(rem, b"???");
}
}