use std::io;
use alloc::vec::Vec;
use domain::new::{
base::{
Record,
name::{NameBuf, RevNameBuf},
},
rdata::Mx,
};
use thiserror::Error;
use url::Url;
use crate::{
autoconfig::{
isp::{DiscoveryIsp, DiscoveryIspError},
mailconf::{DiscoveryMailconf, DiscoveryMailconfError},
mx::{DiscoveryDnsMx, DiscoveryDnsMxError},
types::Autoconfig,
},
coroutine::{DiscoveryCoroutine, DiscoveryCoroutineState, DiscoveryYield},
shared::pool::{Stream, StreamPool},
};
const READ_BUFFER_SIZE: usize = 8 * 1024;
#[derive(Debug, Error)]
pub enum DiscoveryAutoconfigClientStdError {
#[error(transparent)]
Isp(#[from] DiscoveryIspError),
#[error(transparent)]
DnsMx(#[from] DiscoveryDnsMxError),
#[error(transparent)]
Mailconf(#[from] DiscoveryMailconfError),
#[error(transparent)]
UrlParse(#[from] url::ParseError),
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
Pool(#[from] anyhow::Error),
}
pub struct DiscoveryAutoconfigClientStd {
dns: Url,
pool: StreamPool,
}
impl DiscoveryAutoconfigClientStd {
pub fn new(dns: Url) -> Self {
Self {
dns,
pool: StreamPool::new(),
}
}
pub fn with_factory<F, S>(mut self, scheme: &'static str, factory: F) -> Self
where
F: FnMut(&Url) -> anyhow::Result<S> + 'static,
S: Stream + 'static,
{
self.pool = self.pool.with_factory(scheme, factory);
self
}
#[cfg(feature = "stream")]
pub fn with_tls(mut self, tls: pimalaya_stream::tls::Tls) -> Self {
self.pool = self.pool.with_http_factories(tls);
self
}
pub fn isp(
&mut self,
local_part: &str,
domain: &str,
secure: bool,
) -> Result<Autoconfig, DiscoveryAutoconfigClientStdError> {
let url = DiscoveryIsp::main_url(local_part, domain, secure)?;
self.run(DiscoveryIsp::new(url))
}
pub fn isp_fallback(
&mut self,
domain: &str,
secure: bool,
) -> Result<Autoconfig, DiscoveryAutoconfigClientStdError> {
let url = DiscoveryIsp::fallback_url(domain, secure)?;
self.run(DiscoveryIsp::new(url))
}
pub fn ispdb(
&mut self,
domain: &str,
secure: bool,
) -> Result<Autoconfig, DiscoveryAutoconfigClientStdError> {
let url = DiscoveryIsp::db_url(domain, secure)?;
self.run(DiscoveryIsp::new(url))
}
pub fn mx(
&mut self,
domain: &str,
) -> Result<Vec<Record<RevNameBuf, Mx<NameBuf>>>, DiscoveryAutoconfigClientStdError> {
self.run(DiscoveryDnsMx::new(domain, self.dns.clone()))
}
pub fn mailconf(&mut self, domain: &str) -> Result<Url, DiscoveryAutoconfigClientStdError> {
self.run(DiscoveryMailconf::new(domain, self.dns.clone()))
}
fn run<C, T, E>(&mut self, mut coroutine: C) -> Result<T, DiscoveryAutoconfigClientStdError>
where
C: DiscoveryCoroutine<Yield = DiscoveryYield, Return = Result<T, E>>,
DiscoveryAutoconfigClientStdError: From<E>,
{
let mut buf = [0u8; READ_BUFFER_SIZE];
let mut arg: Option<&[u8]> = None;
loop {
match coroutine.resume(arg.take()) {
DiscoveryCoroutineState::Complete(Ok(out)) => return Ok(out),
DiscoveryCoroutineState::Complete(Err(err)) => return Err(err.into()),
DiscoveryCoroutineState::Yielded(DiscoveryYield::WantsRead { url }) => {
let stream = self.pool.get(&url)?;
let n = stream.read(&mut buf)?;
arg = Some(&buf[..n]);
}
DiscoveryCoroutineState::Yielded(DiscoveryYield::WantsWrite { url, bytes }) => {
let stream = self.pool.get(&url)?;
stream.write_all(&bytes)?;
}
}
}
}
}