trapeze 0.7.1

A rust implementation of ttrpc
Documentation
use std::future::poll_fn;
use std::io::Result as IoResult;
use std::os::unix::net::{
    SocketAddr, UnixListener as StdUnixListener, UnixStream as StdUnixStream,
};

use async_trait::async_trait;
use tokio::net::{UnixListener, UnixStream};

use super::{Connection, Listener};

#[async_trait]
impl Listener for UnixListener {
    async fn accept(&mut self) -> IoResult<Box<dyn Connection>> {
        let (conn, _) = UnixListener::accept(self).await?;
        Ok(Box::new(conn))
    }
}

pub fn bind(addr: impl AsRef<str>) -> IoResult<impl Listener> {
    let addr: String = addr.as_ref().into();
    let inner = {
        let addr = make_socket_addr(&addr)?;
        let inner = StdUnixListener::bind_addr(&addr)?;
        inner.set_nonblocking(true)?;
        UnixListener::from_std(inner)?
    };

    let addr = Some(addr);
    Ok(RaiiListener { inner, addr })
}

pub async fn connect(addr: impl AsRef<str>) -> IoResult<impl Connection> {
    let addr = make_socket_addr(addr.as_ref())?;
    let conn = StdUnixStream::connect_addr(&addr)?;
    conn.set_nonblocking(true)?;
    let conn = UnixStream::from_std(conn)?;
    poll_fn(|cx| conn.poll_write_ready(cx)).await?;
    Ok(conn)
}

struct RaiiListener {
    inner: UnixListener,
    addr: Option<String>,
}

#[async_trait]
impl Listener for RaiiListener {
    async fn accept(&mut self) -> IoResult<Box<dyn Connection>> {
        let (conn, _) = UnixListener::accept(&self.inner).await?;
        Ok(Box::new(conn))
    }
}

impl Drop for RaiiListener {
    fn drop(&mut self) {
        if let Some(addr) = &self.addr {
            cleanup_socket(addr);
        }
    }
}

fn make_socket_addr(addr: &str) -> IoResult<SocketAddr> {
    #[cfg(any(target_os = "linux", target_os = "android"))]
    if let Some(name) = addr.strip_prefix('@') {
        use std::os::linux::net::SocketAddrExt;
        return SocketAddr::from_abstract_name(name);
    }
    SocketAddr::from_pathname(addr)
}

fn cleanup_socket(addr: &str) {
    #[cfg(any(target_os = "linux", target_os = "android"))]
    if addr.strip_prefix('@').is_some() {
        return;
    }
    let _ = std::fs::remove_file(addr);
}