interprocess 1.0.0

Interprocess communication toolkit
Documentation
use std::{
    io::{self, IoSlice, IoSliceMut, prelude::*},
    fmt::{self, Formatter, Debug},
    ffi::{OsStr, OsString, CStr, CString},
    borrow::Cow,
    os::unix::{
        io::{AsRawFd, IntoRawFd, FromRawFd},
        ffi::{OsStrExt, OsStringExt},
    },
};
use crate::local_socket::{
    NameTypeSupport,
    LocalSocketName,
    ToLocalSocketName,
};
use super::udsocket::{
    UdStreamListener,
    UdSocketPath,
    UdStream,
};

pub(crate) struct LocalSocketListener {
    inner: UdStreamListener,
}
impl LocalSocketListener {
    #[inline]
    pub fn bind<'a>(name: impl ToLocalSocketName<'a>) -> io::Result<Self> {
        let path = local_socket_name_to_ud_socket_path(name.to_local_socket_name()?)?;
        let inner = UdStreamListener::bind(path)?;
        Ok(Self {inner})
    }
    #[inline(always)]
    pub fn accept(&self) -> io::Result<LocalSocketStream> {
        let inner = self.inner.accept()?;
        Ok(LocalSocketStream {inner})
    }
}
impl Debug for LocalSocketListener {
    #[inline]
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.debug_struct("LocalSocketListener")
            .field("file_descriptor", &self.inner.as_raw_fd())
            .finish()
    }
}
impl AsRawFd for LocalSocketListener {
    #[inline(always)]
    fn as_raw_fd(&self) -> i32 {
        self.inner.as_raw_fd()
    }
}
impl IntoRawFd for LocalSocketListener {
    #[inline(always)]
    fn into_raw_fd(self) -> i32 {
        self.inner.into_raw_fd()
    }
}
impl FromRawFd for LocalSocketListener {
    #[inline(always)]
    unsafe fn from_raw_fd(fd: i32) -> Self {
        Self {inner: UdStreamListener::from_raw_fd(fd)}
    }
}

pub(crate) struct LocalSocketStream {
    inner: UdStream,
}
impl LocalSocketStream {
    #[inline]
    pub fn connect<'a>(name: impl ToLocalSocketName<'a>) -> io::Result<Self> {
        let path = local_socket_name_to_ud_socket_path(name.to_local_socket_name()?)?;
        let inner = UdStream::connect(path)?;
        Ok(Self {inner})
    }
}
impl Read for LocalSocketStream {
    #[inline(always)]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf)
    }
    #[inline(always)]
    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
        self.inner.read_vectored(bufs)
    }
}
impl Write for LocalSocketStream {
    #[inline(always)]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.inner.write(buf)
    }
    #[inline(always)]
    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
        self.inner.write_vectored(bufs)
    }
    #[inline(always)]
    fn flush(&mut self) -> io::Result<()> {
        self.inner.flush()
    }
}
impl Debug for LocalSocketStream {
    #[inline]
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.debug_struct("LocalSocketStream")
            .field("file_descriptor", &self.inner.as_raw_fd())
            .finish()
    }
}
impl AsRawFd for LocalSocketStream {
    #[inline(always)]
    fn as_raw_fd(&self) -> i32 {
        self.inner.as_raw_fd()
    }
}
impl IntoRawFd for LocalSocketStream {
    #[inline(always)]
    fn into_raw_fd(self) -> i32 {
        self.inner.into_raw_fd()
    }
}
impl FromRawFd for LocalSocketStream {
    #[inline(always)]
    unsafe fn from_raw_fd(fd: i32) -> Self {
        Self {inner: UdStream::from_raw_fd(fd)}
    }
}

#[inline]
fn local_socket_name_to_ud_socket_path(name: LocalSocketName<'_>) -> io::Result<UdSocketPath<'_>> {
    #[inline]
    fn cow_osstr_to_cstr(osstr: Cow<'_, OsStr>) -> io::Result<Cow<'_, CStr>> {
        match osstr {
            Cow::Borrowed(val) => {
                if val.as_bytes().last() == Some(&0) {
                    Ok(Cow::Borrowed(
                        CStr::from_bytes_with_nul(val.as_bytes())
                            .map_err(|error| io::Error::new(io::ErrorKind::InvalidInput, error))?
                    ))
                } else {
                    let owned = val.to_os_string();
                    Ok(Cow::Owned(
                        CString::new(owned.into_vec())?
                    ))
                }
            },
            Cow::Owned(val) => {
                Ok(Cow::Owned(
                    CString::new(val.into_vec())?
                ))
            },
        }
    }
    #[cfg(target_os = "linux")]
    if name.is_namespaced() {
        return Ok(UdSocketPath::Namespaced(
            cow_osstr_to_cstr(name.into_inner_cow())?
        ));
    }
    Ok(UdSocketPath::File(
        cow_osstr_to_cstr(name.into_inner_cow())?
    ))
}

#[inline(always)]
pub fn name_type_support_query() -> NameTypeSupport {
    NAME_TYPE_ALWAYS_SUPPORTED
}
#[cfg(target_os = "linux")]
pub const NAME_TYPE_ALWAYS_SUPPORTED: NameTypeSupport = NameTypeSupport::Both;
#[cfg(not(target_os = "linux"))]
pub const NAME_TYPE_ALWAYS_SUPPORTED: NameTypeSupport = NameTypeSupport::OnlyPaths;

const AT_SIGN: u8 = 0x40;

#[inline]
pub fn to_local_socket_name_osstr(mut val: &OsStr) -> LocalSocketName<'_> {
    let mut namespaced = false;
    if let Some(AT_SIGN) = val.as_bytes().get(0).copied() {
        if val.len() >= 2 {
            val = OsStr::from_bytes(&val.as_bytes()[1..]);
        } else {
            val = OsStr::from_bytes(&[]);
        }
        namespaced = true;
    }
    LocalSocketName::from_raw_parts(Cow::Borrowed(val), namespaced)
}
#[inline]
pub fn to_local_socket_name_osstring(mut val: OsString) -> LocalSocketName<'static> {
    let mut namespaced = false;
    if let Some(AT_SIGN) = val.as_bytes().get(0).copied() {
        let new_val = {
            let mut vec = val.into_vec();
            vec.remove(0);
            OsString::from_vec(vec)
        };
        val = new_val;
        namespaced = true;
    }
    LocalSocketName::from_raw_parts(Cow::Owned(val), namespaced)
}