use std::future::Future;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::time::Instant;
use crate::{ClientError, ClientResult, ErrorKind};
pub trait ByteTransport: AsyncRead + AsyncWrite + Unpin + Send + 'static {}
pub type BoxTransport = Box<dyn ByteTransport>;
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait Connector: Send + Sync {
fn connect(&self, deadline: Instant) -> BoxFuture<'_, ClientResult<BoxTransport>>;
}
#[derive(Debug, Clone)]
pub struct LocalConnector {
path: PathBuf,
}
impl LocalConnector {
pub fn new(path: impl AsRef<Path>) -> Self {
Self {
path: path.as_ref().to_owned(),
}
}
pub fn path(&self) -> &Path {
&self.path
}
}
impl<T: AsyncRead + AsyncWrite + Unpin + Send + 'static> ByteTransport for T {}
impl Connector for LocalConnector {
fn connect(&self, deadline: Instant) -> BoxFuture<'_, ClientResult<BoxTransport>> {
Box::pin(async move {
tokio::time::timeout_at(deadline, async {
#[cfg(all(unix, feature = "uds"))]
{
let stream = tokio::net::UnixStream::connect(&self.path).await?;
Ok(Box::new(stream) as BoxTransport)
}
#[cfg(all(windows, feature = "named-pipe"))]
{
loop {
match tokio::net::windows::named_pipe::ClientOptions::new().open(&self.path)
{
Ok(stream) => return Ok(Box::new(stream) as BoxTransport),
Err(error)
if error.kind() == std::io::ErrorKind::NotFound
|| error.raw_os_error() == Some(231) =>
{
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
}
Err(error) => return Err(ClientError::from(error)),
}
}
}
#[cfg(not(any(all(unix, feature = "uds"), all(windows, feature = "named-pipe"))))]
Err(ClientError::new(ErrorKind::UnsupportedOperation))
})
.await
.map_err(|_| ClientError::new(ErrorKind::Timeout))?
})
}
}