use nom::error::Error as NomError;
use nom::Err;
mod bimi;
mod dmarc;
mod domain_verification;
mod mta_sts;
mod spf;
pub(crate) mod tag_value;
mod tls_rpt;
use crate::Error;
use crate::Result;
pub use bimi::Bimi;
pub use dmarc::Dmarc;
pub use domain_verification::DomainVerification;
pub use mta_sts::MtaSts;
pub use spf::{Mechanism, Modifier, Qualifier, Spf, Word};
pub use tls_rpt::TlsRpt;
#[derive(Debug)]
pub enum ParsedTxt<'a> {
Bimi(Bimi<'a>),
Dmarc(Dmarc<'a>),
DomainVerification(DomainVerification<'a>),
MtaSts(MtaSts<'a>),
Spf(Spf<'a>),
TlsRpt(TlsRpt<'a>),
}
#[allow(clippy::should_implement_trait)]
impl<'a> ParsedTxt<'a> {
pub fn from_str(txt: &'a str) -> Result<ParsedTxt<'a>> {
match parser::parsed_txt(txt) {
Ok((_, result)) => Ok(result),
Err(Err::Incomplete(_)) => Err(Error::ParserError {
what: txt.to_string(),
to: "ParsedTxt",
why: "input is incompletely parsed".to_string(),
}),
Err(Err::Error(NomError { input: what, code: why }))
| Err(Err::Failure(NomError { input: what, code: why })) => Err(Error::ParserError {
what: what.to_string(),
to: "ParsedTxt",
why: format!("{:?}", why),
}),
}
}
}
mod parser {
use crate::resources::rdata::parsed_txt::{bimi, dmarc, domain_verification, mta_sts, spf, tls_rpt, ParsedTxt};
use nom::branch::alt;
use nom::IResult;
pub fn parsed_txt(input: &str) -> IResult<&str, ParsedTxt<'_>> {
let (input, parsed_txt) = alt((dmarc, mta_sts, tls_rpt, bimi, domain_verification, spf))(input)?;
Ok((input, parsed_txt))
}
pub fn dmarc(input: &str) -> IResult<&str, ParsedTxt<'_>> {
let (input, dmarc) = dmarc::parser::dmarc(input)?;
Ok((input, ParsedTxt::Dmarc(dmarc)))
}
pub fn mta_sts(input: &str) -> IResult<&str, ParsedTxt<'_>> {
let (input, mta_sts) = mta_sts::parser::mta_sts(input)?;
Ok((input, ParsedTxt::MtaSts(mta_sts)))
}
pub fn tls_rpt(input: &str) -> IResult<&str, ParsedTxt<'_>> {
let (input, tls_rpt) = tls_rpt::parser::tls_rpt(input)?;
Ok((input, ParsedTxt::TlsRpt(tls_rpt)))
}
pub fn bimi(input: &str) -> IResult<&str, ParsedTxt<'_>> {
let (input, bimi) = bimi::parser::bimi(input)?;
Ok((input, ParsedTxt::Bimi(bimi)))
}
pub fn spf(input: &str) -> IResult<&str, ParsedTxt<'_>> {
let (input, spf) = spf::parser::spf(input)?;
Ok((input, ParsedTxt::Spf(spf)))
}
pub fn domain_verification(input: &str) -> IResult<&str, ParsedTxt<'_>> {
let (input, domain_verification) = domain_verification::parser::domain_verification(input)?;
Ok((input, ParsedTxt::DomainVerification(domain_verification)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dispatch_spf() {
let result = ParsedTxt::from_str("v=spf1 -all").unwrap();
assert!(matches!(result, ParsedTxt::Spf(_)));
}
#[test]
fn dispatch_dmarc() {
let result = ParsedTxt::from_str("v=DMARC1; p=none").unwrap();
assert!(matches!(result, ParsedTxt::Dmarc(_)));
}
#[test]
fn dispatch_mta_sts() {
let result = ParsedTxt::from_str("v=STSv1; id=20190429T010101").unwrap();
assert!(matches!(result, ParsedTxt::MtaSts(_)));
}
#[test]
fn dispatch_tls_rpt() {
let result = ParsedTxt::from_str("v=TLSRPTv1; rua=mailto:tlsrpt@example.com").unwrap();
assert!(matches!(result, ParsedTxt::TlsRpt(_)));
}
#[test]
fn dispatch_bimi() {
let result = ParsedTxt::from_str("v=BIMI1; l=https://example.com/logo.svg").unwrap();
assert!(matches!(result, ParsedTxt::Bimi(_)));
}
#[test]
fn dispatch_domain_verification() {
let result = ParsedTxt::from_str("google-site-verification=abc123").unwrap();
assert!(matches!(result, ParsedTxt::DomainVerification(_)));
}
#[test]
fn dispatch_ms_office_365() {
let result = ParsedTxt::from_str("MS=ms86874996").unwrap();
assert!(matches!(result, ParsedTxt::DomainVerification(_)));
}
#[test]
fn dispatch_unparseable() {
let result = ParsedTxt::from_str("random garbage text");
assert!(result.is_err());
}
#[test]
fn dmarc_before_domain_verification() {
let result = ParsedTxt::from_str("v=DMARC1; p=reject").unwrap();
assert!(matches!(result, ParsedTxt::Dmarc(_)));
}
}