use std::io;
use std::net::{SocketAddr, ToSocketAddrs};
use std::sync::OnceLock;
use crate::blocking::{Abandoned, BlockingPool};
pub(super) const RESOLVER_WORKERS: usize = 4;
pub(super) const RESOLVER_QUEUE_DEPTH: usize = RESOLVER_WORKERS;
pub(super) fn pool() -> &'static BlockingPool {
static POOL: OnceLock<BlockingPool> = OnceLock::new();
POOL.get_or_init(|| BlockingPool::new("moirai-resolve", RESOLVER_WORKERS, RESOLVER_QUEUE_DEPTH))
}
pub(super) struct ResolvedAddrs {
first: SocketAddr,
rest: Vec<SocketAddr>,
}
impl ResolvedAddrs {
pub(super) fn first(&self) -> SocketAddr {
self.first
}
pub(super) fn into_parts(self) -> (SocketAddr, Vec<SocketAddr>) {
(self.first, self.rest)
}
}
pub(super) async fn resolve(addr: &str) -> io::Result<ResolvedAddrs> {
if let Ok(first) = addr.parse::<SocketAddr>() {
return Ok(ResolvedAddrs {
first,
rest: Vec::new(),
});
}
let query = addr.to_owned();
let mut addrs = pool()
.run(Abandoned::Skip, move || {
query
.to_socket_addrs()
.map(Iterator::collect::<Vec<SocketAddr>>)
})
.await??
.into_iter();
let first = addrs.next().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
format!("{addr:?} resolved to no socket address"),
)
})?;
Ok(ResolvedAddrs {
first,
rest: addrs.collect(),
})
}