use alloc::{borrow::Cow, vec::Vec};
use core::fmt;
use bounded_static_derive::ToStatic;
use chumsky::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, ToStatic)]
pub struct Domain<'a>(pub Cow<'a, str>);
impl Domain<'_> {
pub fn parse<'a>(bytes: &'a [u8]) -> Result<Domain<'a>, Vec<Rich<'a, u8>>> {
parsers::domain()
.then_ignore(end())
.parse(bytes)
.into_result()
}
}
impl fmt::Display for Domain<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<'a> From<Domain<'a>> for Cow<'a, str> {
fn from(domain: Domain<'a>) -> Self {
domain.0
}
}
impl AsRef<str> for Domain<'_> {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
pub(crate) mod parsers {
use core::str::from_utf8;
use alloc::borrow::Cow;
use chumsky::prelude::*;
use crate::{rfc5321::types::domain::Domain, utils::parsers::Extra};
pub(crate) fn domain<'a>() -> impl Parser<'a, &'a [u8], Domain<'a>, Extra<'a>> + Clone {
let sub_domain = any()
.filter(|b: &u8| b.is_ascii_alphanumeric())
.then(
any()
.filter(|b: &u8| b.is_ascii_alphanumeric() || *b == b'-')
.repeated()
.to_slice(),
)
.to_slice();
sub_domain
.then(
just(b'.')
.then(any().filter(|b: &u8| b.is_ascii_alphanumeric()))
.then(
any()
.filter(|b: &u8| b.is_ascii_alphanumeric() || *b == b'-')
.repeated()
.to_slice(),
)
.to_slice()
.repeated()
.to_slice(),
)
.to_slice()
.try_map(|bytes: &[u8], span| {
from_utf8(bytes)
.map_err(|_| Rich::custom(span, "invalid UTF-8 in domain"))
.map(Cow::from)
.map(Domain)
})
.labelled("domain")
}
}