#[cfg(unix)]
use std::fs;
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
#[cfg(unix)]
use std::path::Path;
use tokio::net::{TcpListener, TcpStream};
#[cfg(unix)]
use tokio::net::{UnixListener, UnixStream};
#[cfg(unix)]
use tokio_util::either::Either;
#[cfg(unix)]
pub type Stream = Either<TcpStream, UnixStream>;
#[cfg(windows)]
pub type Stream = TcpStream;
use crate::ProtAddr;
pub async fn connect(pa: &ProtAddr) -> Result<Stream, std::io::Error> {
let strm = match pa {
ProtAddr::Tcp(sa) => connect_tcp(sa).await?,
#[cfg(unix)]
ProtAddr::Uds(sa) => connect_uds(sa).await?
};
Ok(strm)
}
async fn connect_tcp(addr: &str) -> Result<Stream, std::io::Error> {
let stream = TcpStream::connect(addr).await?;
#[cfg(unix)]
return Ok(Either::Left(stream));
#[cfg(windows)]
return Ok(stream);
}
#[cfg(unix)]
async fn connect_uds(addr: &Path) -> Result<Stream, std::io::Error> {
let addr = match addr.to_str() {
Some(a) => a.to_string(),
None => unreachable!()
};
let stream = UnixStream::connect(addr).await?;
Ok(Either::Right(stream))
}
pub enum Listener {
#[cfg(unix)]
Unix(UnixListener),
Tcp(TcpListener)
}
#[derive(Debug)]
pub enum SockAddr {
Std(std::net::SocketAddr),
#[cfg(unix)]
TokioUnix(tokio::net::unix::SocketAddr)
}
impl Listener {
pub async fn accept(&self) -> Result<(Stream, SockAddr), tokio::io::Error> {
match self {
#[cfg(unix)]
Listener::Unix(u) => {
let (stream, sa) = u.accept().await?;
let sa = SockAddr::TokioUnix(sa);
return Ok((Either::Right(stream), sa));
}
Listener::Tcp(t) => {
let (stream, sa) = t.accept().await?;
let sa = SockAddr::Std(sa);
#[cfg(unix)]
return Ok((Either::Left(stream), sa));
#[cfg(windows)]
return Ok((stream, sa));
}
}
}
}
pub async fn bind(pa: &ProtAddr) -> Result<Listener, std::io::Error> {
let listener = match pa {
ProtAddr::Tcp(sa) => Listener::Tcp(TcpListener::bind(sa).await?),
#[cfg(unix)]
ProtAddr::Uds(sa) => Listener::Unix(UnixListener::bind(Path::new(sa))?)
};
Ok(listener)
}
pub async fn force_bind(pa: &ProtAddr) -> Result<Listener, std::io::Error> {
let listener = match pa {
ProtAddr::Tcp(_) => bind(pa).await?,
#[cfg(unix)]
ProtAddr::Uds(sa) => {
if sa.exists() {
let md = fs::metadata(sa)?;
let ft = md.file_type();
if !ft.is_socket() {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Not a socket"
));
}
fs::remove_file(sa)?;
}
Listener::Unix(UnixListener::bind(Path::new(sa))?)
}
};
Ok(listener)
}