use std::fmt;
use std::io;
use std::ops::Deref;
use std::ops::DerefMut;
use std::path::Path;
use std::path::PathBuf;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite};
pub use tokio::net::UnixListener;
#[cfg(feature = "client")]
use crate::client::pool::PoolableStream;
use crate::info::HasConnectionInfo;
use crate::info::HasTlsConnectionInfo;
#[cfg(feature = "server")]
use crate::server::Accept;
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub struct UnixAddr {
path: Option<PathBuf>,
}
impl UnixAddr {
pub fn is_named(&self) -> bool {
self.path.is_some()
}
pub fn path(&self) -> Option<&Path> {
self.path.as_deref()
}
pub fn from_pathbuf(path: PathBuf) -> Self {
Self { path: Some(path) }
}
pub fn unnamed() -> Self {
Self { path: None }
}
}
impl fmt::Display for UnixAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(path) = self.path() {
write!(f, "unix://{path}", path = path.display())
} else {
write!(f, "unix://")
}
}
}
impl TryFrom<std::os::unix::net::SocketAddr> for UnixAddr {
type Error = io::Error;
fn try_from(addr: std::os::unix::net::SocketAddr) -> Result<Self, Self::Error> {
Ok(Self {
path: addr.as_pathname().map(|path| path.to_owned()),
})
}
}
impl TryFrom<tokio::net::unix::SocketAddr> for UnixAddr {
type Error = io::Error;
fn try_from(addr: tokio::net::unix::SocketAddr) -> Result<Self, Self::Error> {
Ok(Self {
path: addr.as_pathname().map(|path| path.to_owned()),
})
}
}
#[pin_project::pin_project]
pub struct UnixStream {
#[pin]
stream: tokio::net::UnixStream,
remote: Option<UnixAddr>,
}
impl fmt::Debug for UnixStream {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.stream.fmt(f)
}
}
impl UnixStream {
pub async fn connect<P: AsRef<Path>>(path: P) -> io::Result<Self> {
let path = path.as_ref();
let stream = tokio::net::UnixStream::connect(path).await?;
Ok(Self::new(
stream,
Some(UnixAddr::from_pathbuf(path.to_path_buf())),
))
}
pub fn pair() -> io::Result<(Self, Self)> {
let (a, b) = tokio::net::UnixStream::pair()?;
Ok((
Self::new(a, Some(UnixAddr::unnamed())),
Self::new(b, Some(UnixAddr::unnamed())),
))
}
pub fn new(inner: tokio::net::UnixStream, remote: Option<UnixAddr>) -> Self {
Self {
stream: inner,
remote,
}
}
pub fn local_addr(&self) -> io::Result<UnixAddr> {
self.stream.local_addr().and_then(UnixAddr::try_from)
}
pub fn peer_addr(&self) -> io::Result<UnixAddr> {
match &self.remote {
Some(addr) => Ok(addr.clone()),
None => self.stream.peer_addr().and_then(UnixAddr::try_from),
}
}
pub fn into_inner(self) -> tokio::net::UnixStream {
self.stream
}
}
impl Deref for UnixStream {
type Target = tokio::net::UnixStream;
fn deref(&self) -> &Self::Target {
&self.stream
}
}
impl DerefMut for UnixStream {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.stream
}
}
impl HasConnectionInfo for UnixStream {
type Addr = UnixAddr;
fn info(&self) -> crate::info::ConnectionInfo<Self::Addr> {
let remote_addr = self
.peer_addr()
.expect("peer_addr is available for unix stream");
let local_addr = self
.local_addr()
.expect("local_addr is available for unix stream");
crate::info::ConnectionInfo {
local_addr,
remote_addr,
}
}
}
impl HasTlsConnectionInfo for UnixStream {
fn tls_info(&self) -> Option<&crate::info::TlsConnectionInfo> {
None
}
}
#[cfg(feature = "client")]
impl PoolableStream for UnixStream {
fn can_share(&self) -> bool {
false
}
}
impl AsyncRead for UnixStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<io::Result<()>> {
self.project().stream.poll_read(cx, buf)
}
}
impl AsyncWrite for UnixStream {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, io::Error>> {
self.project().stream.poll_write(cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
self.project().stream.poll_flush(cx)
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
self.project().stream.poll_shutdown(cx)
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[io::IoSlice<'_>],
) -> Poll<Result<usize, io::Error>> {
self.project().stream.poll_write_vectored(cx, bufs)
}
fn is_write_vectored(&self) -> bool {
self.stream.is_write_vectored()
}
}
#[cfg(feature = "server")]
impl Accept for UnixListener {
type Connection = UnixStream;
type Error = io::Error;
fn poll_accept(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<io::Result<Self::Connection>> {
UnixListener::poll_accept(self.get_mut(), cx).map(|res| {
res.and_then(|(stream, remote)| Ok(UnixStream::new(stream, Some(remote.try_into()?))))
})
}
}