#![warn(missing_docs, missing_debug_implementations)]
#![forbid(clippy::unwrap_used)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#[cfg(not(windows))]
mod unix;
#[cfg(windows)]
mod win;
use std::io;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::Stream;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
mod platform {
#[cfg(unix)]
pub(crate) use crate::unix::{
Connection, Endpoint, IpcStream, SecurityAttributes, from_std_stream,
};
#[cfg(windows)]
pub(crate) use crate::win::{Connection, Endpoint, IpcStream, SecurityAttributes};
}
pub trait IntoIpcPath: Send {
fn into_ipc_path(self) -> io::Result<PathBuf>;
}
impl IntoIpcPath for PathBuf {
fn into_ipc_path(self) -> io::Result<PathBuf> {
Ok(self)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum OnConflict {
Ignore,
Error,
Overwrite,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ServerId<T>
where
T: Into<String> + Send,
{
id: T,
parent_folder: Option<PathBuf>,
}
impl<T> ServerId<T>
where
T: Into<String> + Send,
{
pub fn new(id: T) -> Self {
Self {
id,
parent_folder: None,
}
}
pub fn parent_folder<P>(mut self, folder: P) -> Self
where
P: Into<PathBuf>,
{
self.parent_folder = Some(folder.into());
self
}
}
impl<T> IntoIpcPath for ServerId<T>
where
T: Into<String> + Send,
{
fn into_ipc_path(self) -> io::Result<PathBuf> {
self.into_ipc_path()
}
}
#[derive(Debug)]
pub struct SecurityAttributes(platform::SecurityAttributes);
impl SecurityAttributes {
pub fn empty() -> Self {
Self(platform::SecurityAttributes::empty())
}
pub fn allow_everyone_connect() -> io::Result<Self> {
Ok(Self(platform::SecurityAttributes::allow_everyone_connect()?))
}
pub fn mode(self, mode: u16) -> io::Result<Self> {
Ok(Self(self.0.mode(mode)?))
}
pub fn allow_everyone_create() -> io::Result<Self> {
Ok(Self(platform::SecurityAttributes::allow_everyone_create()?))
}
}
#[derive(Debug)]
pub struct Endpoint(platform::Endpoint);
impl Endpoint {
pub fn incoming(self) -> io::Result<IpcStream> {
Ok(IpcStream(self.0.incoming()?))
}
pub fn security_attributes(mut self, security_attributes: SecurityAttributes) -> Self {
self.0 = self.0.security_attributes(security_attributes.0);
self
}
pub fn path(&self) -> &Path {
self.0.path()
}
pub async fn connect<P>(path: P) -> io::Result<Connection>
where
P: IntoIpcPath,
{
Ok(Connection(platform::Endpoint::connect(path).await?))
}
pub fn new<P>(path: P, on_conflict: OnConflict) -> io::Result<Self>
where
P: IntoIpcPath,
{
Ok(Self(platform::Endpoint::new(path, on_conflict)?))
}
}
#[derive(Debug)]
pub struct Connection(platform::Connection);
impl Connection {
#[cfg(unix)]
pub async fn from_std_stream(stream: std::os::unix::net::UnixStream) -> io::Result<Self> {
Ok(Self(platform::from_std_stream(stream).await?))
}
}
impl AsyncRead for Connection {
fn poll_read(
self: Pin<&mut Self>,
ctx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
let this = Pin::into_inner(self);
Pin::new(&mut this.0).poll_read(ctx, buf)
}
}
impl AsyncWrite for Connection {
fn poll_write(
self: Pin<&mut Self>,
ctx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, io::Error>> {
let this = Pin::into_inner(self);
Pin::new(&mut this.0).poll_write(ctx, buf)
}
fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
let this = Pin::into_inner(self);
Pin::new(&mut this.0).poll_flush(ctx)
}
fn poll_shutdown(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
let this = Pin::into_inner(self);
Pin::new(&mut this.0).poll_shutdown(ctx)
}
}
#[derive(Debug)]
pub struct IpcStream(platform::IpcStream);
impl IpcStream {
#[cfg(unix)]
pub fn from_std_listener(listener: std::os::unix::net::UnixListener) -> io::Result<Self> {
Ok(Self(platform::IpcStream::from_std_listener(listener)?))
}
}
impl Stream for IpcStream {
type Item = io::Result<Connection>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = Pin::into_inner(self);
Pin::new(&mut this.0).poll_next(cx).map_ok(Connection)
}
}