Path: blob/main/crates/wasi-common/src/pipe.rs
1691 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/// ```no_run20/// use wasi_common::{pipe::ReadPipe, WasiCtx, Table};21/// let stdin = ReadPipe::from("hello from stdin!");22/// // Brint these instances from elsewhere (e.g. wasi-cap-std-sync):23/// let random = todo!();24/// let clocks = todo!();25/// let sched = todo!();26/// let table = Table::new();27/// let mut ctx = WasiCtx::new(random, clocks, sched, table);28/// ctx.set_stdin(Box::new(stdin.clone()));29/// ```30#[derive(Debug)]31pub struct ReadPipe<R: Read> {32reader: Arc<RwLock<R>>,33}3435impl<R: Read> Clone for ReadPipe<R> {36fn clone(&self) -> Self {37Self {38reader: self.reader.clone(),39}40}41}4243impl<R: Read> ReadPipe<R> {44/// Create a new pipe from a `Read` type.45///46/// All `Handle` read operations delegate to reading from this underlying reader.47pub fn new(r: R) -> Self {48Self::from_shared(Arc::new(RwLock::new(r)))49}5051/// Create a new pipe from a shareable `Read` type.52///53/// All `Handle` read operations delegate to reading from this underlying reader.54pub fn from_shared(reader: Arc<RwLock<R>>) -> Self {55Self { reader }56}5758/// Try to convert this `ReadPipe<R>` back to the underlying `R` type.59///60/// This will fail with `Err(self)` if multiple references to the underlying `R` exist.61pub fn try_into_inner(mut self) -> Result<R, Self> {62match Arc::try_unwrap(self.reader) {63Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),64Err(reader) => {65self.reader = reader;66Err(self)67}68}69}70fn borrow(&self) -> std::sync::RwLockWriteGuard<'_, R> {71RwLock::write(&self.reader).unwrap()72}73}7475impl From<Vec<u8>> for ReadPipe<io::Cursor<Vec<u8>>> {76fn from(r: Vec<u8>) -> Self {77Self::new(io::Cursor::new(r))78}79}8081impl From<&[u8]> for ReadPipe<io::Cursor<Vec<u8>>> {82fn from(r: &[u8]) -> Self {83Self::from(r.to_vec())84}85}8687impl From<String> for ReadPipe<io::Cursor<String>> {88fn from(r: String) -> Self {89Self::new(io::Cursor::new(r))90}91}9293impl From<&str> for ReadPipe<io::Cursor<String>> {94fn from(r: &str) -> Self {95Self::from(r.to_string())96}97}9899#[wiggle::async_trait]100impl<R: Read + Any + Send + Sync> WasiFile for ReadPipe<R> {101fn as_any(&self) -> &dyn Any {102self103}104async fn get_filetype(&self) -> Result<FileType, Error> {105Ok(FileType::Pipe)106}107async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {108let n = self.borrow().read_vectored(bufs)?;109Ok(n.try_into()?)110}111}112113/// A virtual pipe write end.114///115/// ```no_run116/// use wasi_common::{pipe::WritePipe, WasiCtx, Table};117/// let stdout = WritePipe::new_in_memory();118/// // Brint these instances from elsewhere (e.g. wasi-cap-std-sync):119/// let random = todo!();120/// let clocks = todo!();121/// let sched = todo!();122/// let table = Table::new();123/// let mut ctx = WasiCtx::new(random, clocks, sched, table);124/// ctx.set_stdout(Box::new(stdout.clone()));125/// // use ctx in an instance, then make sure it is dropped:126/// drop(ctx);127/// let contents: Vec<u8> = stdout.try_into_inner().expect("sole remaining reference to WritePipe").into_inner();128/// println!("contents of stdout: {:?}", contents);129/// ```130#[derive(Debug)]131pub struct WritePipe<W: Write> {132writer: Arc<RwLock<W>>,133}134135impl<W: Write> Clone for WritePipe<W> {136fn clone(&self) -> Self {137Self {138writer: self.writer.clone(),139}140}141}142143impl<W: Write> WritePipe<W> {144/// Create a new pipe from a `Write` type.145///146/// All `Handle` write operations delegate to writing to this underlying writer.147pub fn new(w: W) -> Self {148Self::from_shared(Arc::new(RwLock::new(w)))149}150151/// Create a new pipe from a shareable `Write` type.152///153/// All `Handle` write operations delegate to writing to this underlying writer.154pub fn from_shared(writer: Arc<RwLock<W>>) -> Self {155Self { writer }156}157158/// Try to convert this `WritePipe<W>` back to the underlying `W` type.159///160/// This will fail with `Err(self)` if multiple references to the underlying `W` exist.161pub fn try_into_inner(mut self) -> Result<W, Self> {162match Arc::try_unwrap(self.writer) {163Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),164Err(writer) => {165self.writer = writer;166Err(self)167}168}169}170171fn borrow(&self) -> std::sync::RwLockWriteGuard<'_, W> {172RwLock::write(&self.writer).unwrap()173}174}175176impl WritePipe<io::Cursor<Vec<u8>>> {177/// Create a new writable virtual pipe backed by a `Vec<u8>` buffer.178pub fn new_in_memory() -> Self {179Self::new(io::Cursor::new(vec![]))180}181}182183#[wiggle::async_trait]184impl<W: Write + Any + Send + Sync> WasiFile for WritePipe<W> {185fn as_any(&self) -> &dyn Any {186self187}188async fn get_filetype(&self) -> Result<FileType, Error> {189Ok(FileType::Pipe)190}191async fn get_fdflags(&self) -> Result<FdFlags, Error> {192Ok(FdFlags::APPEND)193}194async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {195let n = self.borrow().write_vectored(bufs)?;196Ok(n.try_into()?)197}198}199200201