use crate::{
Error,
dkim::{AtpsRecord, DkimReportRecord, DomainKey},
dmarc::DmarcRecord,
mta_sts::{MtaStsRecord, TlsRptRecord},
spf::{Macro, SpfRecord},
};
use std::sync::Arc;
#[derive(Clone)]
pub enum TxtRecord {
Spf(Arc<SpfRecord>),
SpfExplanation(Arc<Macro>),
DomainKey(Arc<DomainKey>),
DkimReport(Arc<DkimReportRecord>),
Dmarc(Arc<DmarcRecord>),
Atps(Arc<AtpsRecord>),
MtaSts(Arc<MtaStsRecord>),
TlsRpt(Arc<TlsRptRecord>),
Error(Error),
}
pub trait TxtRecordParser: Sized {
fn parse(record: &[u8]) -> crate::Result<Self>;
fn parse_records(records: &[Vec<u8>]) -> crate::Result<Self> {
let mut result = Err(Error::Dns(crate::DnsError::InvalidRecordType));
for record in records {
result = Self::parse(record);
if result.is_ok() {
break;
}
}
result
}
}
mod private {
pub trait Sealed {}
}
#[doc(hidden)]
pub trait UnwrapTxtRecord: private::Sealed + Sized {
fn unwrap_txt(txt: TxtRecord) -> crate::Result<Arc<Self>>;
}
impl<T: Into<TxtRecord>> From<crate::Result<T>> for TxtRecord {
fn from(v: crate::Result<T>) -> Self {
match v {
Ok(v) => v.into(),
Err(err) => TxtRecord::Error(err),
}
}
}
macro_rules! txt_record {
($($record:ty => $variant:ident),+ $(,)?) => {
$(
impl From<$record> for TxtRecord {
fn from(v: $record) -> Self {
TxtRecord::$variant(v.into())
}
}
impl private::Sealed for $record {}
impl UnwrapTxtRecord for $record {
fn unwrap_txt(txt: TxtRecord) -> crate::Result<Arc<Self>> {
match txt {
TxtRecord::$variant(a) => Ok(a),
TxtRecord::Error(err) => Err(err),
_ => Err(Error::Io("Invalid record type".to_string())),
}
}
}
)+
};
}
txt_record!(
DomainKey => DomainKey,
DkimReportRecord => DkimReport,
AtpsRecord => Atps,
SpfRecord => Spf,
Macro => SpfExplanation,
DmarcRecord => Dmarc,
MtaStsRecord => MtaSts,
TlsRptRecord => TlsRpt,
);