use std::borrow::Cow;
use std::ffi::OsStr;
use std::io;
use std::pin::Pin;
use std::rc::Rc;
use std::task::Context;
use std::task::Poll;
use deno_core::AsyncRefCell;
use deno_core::AsyncResult;
use deno_core::CancelHandle;
use deno_core::CancelTryFuture;
use deno_core::RcRef;
use deno_core::Resource;
use tokio::io::AsyncRead;
use tokio::io::AsyncReadExt;
use tokio::io::AsyncWrite;
use tokio::io::AsyncWriteExt;
use tokio::io::ReadBuf;
use tokio::io::ReadHalf;
use tokio::io::WriteHalf;
use tokio::net::windows::named_pipe;
pub struct NamedPipe {
read_half: AsyncRefCell<NamedPipeRead>,
write_half: AsyncRefCell<NamedPipeWrite>,
cancel: CancelHandle,
pending_server: AsyncRefCell<Option<named_pipe::NamedPipeServer>>,
}
enum NamedPipeRead {
Server(ReadHalf<named_pipe::NamedPipeServer>),
Client(ReadHalf<named_pipe::NamedPipeClient>),
None,
}
enum NamedPipeWrite {
Server(WriteHalf<named_pipe::NamedPipeServer>),
Client(WriteHalf<named_pipe::NamedPipeClient>),
None,
}
impl NamedPipe {
pub fn new_server(
addr: impl AsRef<OsStr>,
options: &named_pipe::ServerOptions,
) -> io::Result<NamedPipe> {
let server = options.create(addr)?;
Ok(NamedPipe {
read_half: AsyncRefCell::new(NamedPipeRead::None),
write_half: AsyncRefCell::new(NamedPipeWrite::None),
cancel: Default::default(),
pending_server: AsyncRefCell::new(Some(server)),
})
}
pub fn new_client(
addr: impl AsRef<OsStr>,
options: &named_pipe::ClientOptions,
) -> io::Result<NamedPipe> {
let client = options.open(addr)?;
let (read, write) = tokio::io::split(client);
Ok(NamedPipe {
read_half: AsyncRefCell::new(NamedPipeRead::Client(read)),
write_half: AsyncRefCell::new(NamedPipeWrite::Client(write)),
cancel: Default::default(),
pending_server: AsyncRefCell::new(None),
})
}
pub async fn connect(self: Rc<Self>) -> io::Result<()> {
let mut pending =
RcRef::map(&self, |s| &s.pending_server).borrow_mut().await;
let cancel = RcRef::map(&self, |s| &s.cancel);
if let Some(server) = pending.take() {
server.connect().try_or_cancel(cancel).await?;
let (read, write) = tokio::io::split(server);
let mut read_half =
RcRef::map(&self, |s| &s.read_half).borrow_mut().await;
let mut write_half =
RcRef::map(&self, |s| &s.write_half).borrow_mut().await;
*read_half = NamedPipeRead::Server(read);
*write_half = NamedPipeWrite::Server(write);
}
Ok(())
}
pub async fn write(self: Rc<Self>, buf: &[u8]) -> io::Result<usize> {
let mut write_half =
RcRef::map(&self, |s| &s.write_half).borrow_mut().await;
let cancel = RcRef::map(&self, |s| &s.cancel);
match &mut *write_half {
NamedPipeWrite::Server(w) => w.write(buf).try_or_cancel(cancel).await,
NamedPipeWrite::Client(w) => w.write(buf).try_or_cancel(cancel).await,
NamedPipeWrite::None => Err(io::Error::new(
io::ErrorKind::NotConnected,
"pipe not connected",
)),
}
}
pub async fn read(self: Rc<Self>, buf: &mut [u8]) -> io::Result<usize> {
let mut read_half = RcRef::map(&self, |s| &s.read_half).borrow_mut().await;
let cancel = RcRef::map(&self, |s| &s.cancel);
match &mut *read_half {
NamedPipeRead::Server(r) => r.read(buf).try_or_cancel(cancel).await,
NamedPipeRead::Client(r) => r.read(buf).try_or_cancel(cancel).await,
NamedPipeRead::None => Err(io::Error::new(
io::ErrorKind::NotConnected,
"pipe not connected",
)),
}
}
pub fn cancel_pending_ops(&self) {
self.cancel.cancel();
}
pub fn into_client(self) -> io::Result<named_pipe::NamedPipeClient> {
let read_half = self.read_half.into_inner();
let write_half = self.write_half.into_inner();
match (read_half, write_half) {
(NamedPipeRead::Client(r), NamedPipeWrite::Client(w)) => Ok(r.unsplit(w)),
_ => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"cannot extract client from non-client pipe",
)),
}
}
}
impl Resource for NamedPipe {
deno_core::impl_readable_byob!();
deno_core::impl_writable!();
fn name(&self) -> Cow<'_, str> {
Cow::Borrowed("namedPipe")
}
fn close(self: Rc<Self>) {
self.cancel.cancel();
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct WindowsPipeAddr;
pub struct WindowsPipeStream(named_pipe::NamedPipeClient);
impl WindowsPipeStream {
pub fn new(client: named_pipe::NamedPipeClient) -> Self {
Self(client)
}
pub fn local_addr(&self) -> io::Result<WindowsPipeAddr> {
Ok(WindowsPipeAddr)
}
pub fn peer_addr(&self) -> io::Result<WindowsPipeAddr> {
Ok(WindowsPipeAddr)
}
pub fn into_split(
self,
) -> (tokio::io::ReadHalf<Self>, tokio::io::WriteHalf<Self>) {
tokio::io::split(self)
}
}
impl AsyncRead for WindowsPipeStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
Pin::new(&mut self.get_mut().0).poll_read(cx, buf)
}
}
impl AsyncWrite for WindowsPipeStream {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.get_mut().0).poll_write(cx, buf)
}
fn poll_flush(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<io::Result<()>> {
Pin::new(&mut self.get_mut().0).poll_flush(cx)
}
fn poll_shutdown(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<io::Result<()>> {
Pin::new(&mut self.get_mut().0).poll_shutdown(cx)
}
}
pub struct WindowsPipeListener;
impl WindowsPipeListener {
#[allow(clippy::unused_async, reason = "same interface as unix")]
pub async fn accept(
&self,
) -> io::Result<(WindowsPipeStream, WindowsPipeAddr)> {
Err(io::Error::new(
io::ErrorKind::Unsupported,
"WindowsPipeListener::accept is not supported",
))
}
pub fn local_addr(&self) -> io::Result<WindowsPipeAddr> {
Err(io::Error::new(
io::ErrorKind::Unsupported,
"WindowsPipeListener::local_addr is not supported",
))
}
}