use aok::Result;
use crate::{Resolver, record_type};
#[derive(Debug, Clone)]
pub struct Mx {
pub priority: u16,
pub server: String,
pub ttl: u64,
}
pub trait MxLookup {
type VecMx<'a>: std::ops::Deref<Target = [Mx]> + 'a
where
Self: 'a;
fn mx<'a>(
&'a self,
domain: impl AsRef<str> + Send + 'a,
) -> impl std::future::Future<Output = Result<Option<Self::VecMx<'a>>>> + Send + 'a;
}
impl<T: Resolver + Sync> MxLookup for T {
type VecMx<'a>
= Vec<Mx>
where
Self: 'a;
async fn mx<'a>(
&'a self,
domain: impl AsRef<str> + Send + 'a,
) -> Result<Option<Self::VecMx<'a>>> {
self
.resolve(domain, "MX", |li| {
let mut r = Vec::with_capacity(li.len());
for i in li {
if i.r#type == record_type::MX {
if let Some((priority_str, server)) = i.data.split_once(' ')
&& let Ok(priority) = priority_str.parse::<u16>()
{
r.push(Mx {
priority,
server: server.trim_end_matches('.').to_string(),
ttl: i.ttl,
});
}
}
}
r.sort_unstable_by_key(|i| i.priority);
Ok(Some(r))
})
.await
}
}
#[cfg(feature = "cache")]
pub mod cache {
use aok::Result;
use dashmap::DashMap;
use expire_cache::Expire;
use super::Mx;
use crate::Resolve;
pub struct MxRef<'a>(expire_cache::map::RefVal<'a, String, Option<Vec<Mx>>>);
impl<'a> std::ops::Deref for MxRef<'a> {
type Target = [Mx];
fn deref(&self) -> &Self::Target {
match &*self.0 {
None => &[],
Some(li) => li,
}
}
}
#[static_init::dynamic]
pub static CACHE: Expire<DashMap<String, Option<Vec<Mx>>>> = Expire::new(600);
pub struct Cache;
impl super::MxLookup for Cache {
type VecMx<'a> = MxRef<'a>;
async fn mx<'a>(&'a self, domain: impl AsRef<str> + Send + 'a) -> Result<Option<MxRef<'a>>> {
let domain = domain.as_ref();
let r = CACHE
.get_or_init_async(domain.to_owned(), move |domain| {
Box::pin(Resolve.mx(domain.clone()))
})
.await?;
if r.is_some() {
return Ok(Some(MxRef(r)));
}
Ok(None)
}
}
}