Path: blob/main/crates/wasi-common/src/pipe.rs
3073 views
//! Virtual pipes.1//!2//! These types provide easy implementations of `WasiFile` that mimic much of the behavior of Unix3//! pipes. These are particularly helpful for redirecting WASI stdio handles to destinations other4//! than OS files.5//!6//! Some convenience constructors are included for common backing types like `Vec<u8>` and `String`,7//! but the virtual pipes can be instantiated with any `Read` or `Write` type.8//!9use crate::Error;10use crate::file::{FdFlags, FileType, WasiFile};11use std::any::Any;12use std::io::{self, Read, Write};13use std::sync::{Arc, RwLock};1415/// A virtual pipe read end.16///17/// A variety of `From` impls are provided so that common pipe types are easy to create. For example:18///19/// ```rust20/// use wasi_common::{pipe::ReadPipe, WasiCtx, Table};21/// let stdin = ReadPipe::from("hello from stdin!");22/// // Bring these instances from elsewhere (e.g. wasi-cap-std-sync or wasi-cap-std-tokio):23/// use wasi_common::sync::{random_ctx, clocks_ctx, sched_ctx};24/// let random = random_ctx();25/// let clocks = clocks_ctx();26/// let sched = sched_ctx();27/// let table = Table::new();28/// let mut ctx = WasiCtx::new(random, clocks, sched, table);29/// ctx.set_stdin(Box::new(stdin.clone()));30/// ```31#[derive(Debug)]32pub struct ReadPipe<R: Read> {33reader: Arc<RwLock<R>>,34}3536impl<R: Read> Clone for ReadPipe<R> {37fn clone(&self) -> Self {38Self {39reader: self.reader.clone(),40}41}42}4344impl<R: Read> ReadPipe<R> {45/// Create a new pipe from a `Read` type.46///47/// All `Handle` read operations delegate to reading from this underlying reader.48pub fn new(r: R) -> Self {49Self::from_shared(Arc::new(RwLock::new(r)))50}5152/// Create a new pipe from a shareable `Read` type.53///54/// All `Handle` read operations delegate to reading from this underlying reader.55pub fn from_shared(reader: Arc<RwLock<R>>) -> Self {56Self { reader }57}5859/// Try to convert this `ReadPipe<R>` back to the underlying `R` type.60///61/// This will fail with `Err(self)` if multiple references to the underlying `R` exist.62pub fn try_into_inner(mut self) -> Result<R, Self> {63match Arc::try_unwrap(self.reader) {64Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),65Err(reader) => {66self.reader = reader;67Err(self)68}69}70}71fn borrow(&self) -> std::sync::RwLockWriteGuard<'_, R> {72RwLock::write(&self.reader).unwrap()73}74}7576impl From<Vec<u8>> for ReadPipe<io::Cursor<Vec<u8>>> {77fn from(r: Vec<u8>) -> Self {78Self::new(io::Cursor::new(r))79}80}8182impl From<&[u8]> for ReadPipe<io::Cursor<Vec<u8>>> {83fn from(r: &[u8]) -> Self {84Self::from(r.to_vec())85}86}8788impl From<String> for ReadPipe<io::Cursor<String>> {89fn from(r: String) -> Self {90Self::new(io::Cursor::new(r))91}92}9394impl From<&str> for ReadPipe<io::Cursor<String>> {95fn from(r: &str) -> Self {96Self::from(r.to_string())97}98}99100#[async_trait::async_trait]101impl<R: Read + Any + Send + Sync> WasiFile for ReadPipe<R> {102fn as_any(&self) -> &dyn Any {103self104}105async fn get_filetype(&self) -> Result<FileType, Error> {106Ok(FileType::Pipe)107}108async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {109let n = self.borrow().read_vectored(bufs)?;110Ok(n.try_into()?)111}112}113114/// A virtual pipe write end.115///116/// ```rust117/// use wasi_common::{pipe::WritePipe, WasiCtx, Table};118/// let stdout = WritePipe::new_in_memory();119/// // Bring these instances from elsewhere (e.g. wasi-cap-std-sync or wasi-cap-std-tokio):120/// use wasi_common::sync::{random_ctx, clocks_ctx, sched_ctx};121/// let random = random_ctx();122/// let clocks = clocks_ctx();123/// let sched = sched_ctx();124/// let table = Table::new();125/// let mut ctx = WasiCtx::new(random, clocks, sched, table);126/// ctx.set_stdout(Box::new(stdout.clone()));127/// // use ctx in an instance, then make sure it is dropped:128/// drop(ctx);129/// let contents: Vec<u8> = stdout.try_into_inner().expect("sole remaining reference to WritePipe").into_inner();130/// println!("contents of stdout: {:?}", contents);131/// ```132#[derive(Debug)]133pub struct WritePipe<W: Write> {134writer: Arc<RwLock<W>>,135}136137impl<W: Write> Clone for WritePipe<W> {138fn clone(&self) -> Self {139Self {140writer: self.writer.clone(),141}142}143}144145impl<W: Write> WritePipe<W> {146/// Create a new pipe from a `Write` type.147///148/// All `Handle` write operations delegate to writing to this underlying writer.149pub fn new(w: W) -> Self {150Self::from_shared(Arc::new(RwLock::new(w)))151}152153/// Create a new pipe from a shareable `Write` type.154///155/// All `Handle` write operations delegate to writing to this underlying writer.156pub fn from_shared(writer: Arc<RwLock<W>>) -> Self {157Self { writer }158}159160/// Try to convert this `WritePipe<W>` back to the underlying `W` type.161///162/// This will fail with `Err(self)` if multiple references to the underlying `W` exist.163pub fn try_into_inner(mut self) -> Result<W, Self> {164match Arc::try_unwrap(self.writer) {165Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),166Err(writer) => {167self.writer = writer;168Err(self)169}170}171}172173fn borrow(&self) -> std::sync::RwLockWriteGuard<'_, W> {174RwLock::write(&self.writer).unwrap()175}176}177178impl WritePipe<io::Cursor<Vec<u8>>> {179/// Create a new writable virtual pipe backed by a `Vec<u8>` buffer.180pub fn new_in_memory() -> Self {181Self::new(io::Cursor::new(vec![]))182}183}184185#[async_trait::async_trait]186impl<W: Write + Any + Send + Sync> WasiFile for WritePipe<W> {187fn as_any(&self) -> &dyn Any {188self189}190async fn get_filetype(&self) -> Result<FileType, Error> {191Ok(FileType::Pipe)192}193async fn get_fdflags(&self) -> Result<FdFlags, Error> {194Ok(FdFlags::APPEND)195}196async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {197let n = self.borrow().write_vectored(bufs)?;198Ok(n.try_into()?)199}200}201202203