1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
// This is mostly stubs
#![allow(unused_variables, dead_code)]
//! Virtual pipes.
//!
//! These types provide easy implementations of `WasiFile` that mimic much of the behavior of Unix
//! pipes. These are particularly helpful for redirecting WASI stdio handles to destinations other
//! than OS files.
//!
//! Some convenience constructors are included for common backing types like `Vec<u8>` and `String`,
//! but the virtual pipes can be instantiated with any `Read` or `Write` type.
//!
use crate::file::{FdFlags, FileType, WasiFile};
use crate::Error;
use std::any::Any;
use std::convert::TryInto;
use std::io::{self, Read, Write};
use std::sync::{Arc, RwLock};
/// A virtual pipe read end.
///
/// A variety of `From` impls are provided so that common pipe types are easy to create. For example:
///
/// ```no_run
/// use wasi_common::{pipe::ReadPipe, WasiCtx, Table};
/// let stdin = ReadPipe::from("hello from stdin!");
/// // Brint these instances from elsewhere (e.g. wasi-cap-std-sync):
/// let random = todo!();
/// let clocks = todo!();
/// let sched = todo!();
/// let table = Table::new();
/// let mut ctx = WasiCtx::new(random, clocks, sched, table);
/// ctx.set_stdin(Box::new(stdin.clone()));
/// ```
#[derive(Debug)]
pub struct ReadPipe<R: Read> {
reader: Arc<RwLock<R>>,
}
impl<R: Read> Clone for ReadPipe<R> {
fn clone(&self) -> Self {
Self {
reader: self.reader.clone(),
}
}
}
impl<R: Read> ReadPipe<R> {
/// Create a new pipe from a `Read` type.
///
/// All `Handle` read operations delegate to reading from this underlying reader.
pub fn new(r: R) -> Self {
Self::from_shared(Arc::new(RwLock::new(r)))
}
/// Create a new pipe from a shareable `Read` type.
///
/// All `Handle` read operations delegate to reading from this underlying reader.
pub fn from_shared(reader: Arc<RwLock<R>>) -> Self {
Self { reader }
}
/// Try to convert this `ReadPipe<R>` back to the underlying `R` type.
///
/// This will fail with `Err(self)` if multiple references to the underlying `R` exist.
pub fn try_into_inner(mut self) -> Result<R, Self> {
match Arc::try_unwrap(self.reader) {
Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),
Err(reader) => {
self.reader = reader;
Err(self)
}
}
}
fn borrow(&self) -> std::sync::RwLockWriteGuard<R> {
RwLock::write(&self.reader).unwrap()
}
}
impl From<Vec<u8>> for ReadPipe<io::Cursor<Vec<u8>>> {
fn from(r: Vec<u8>) -> Self {
Self::new(io::Cursor::new(r))
}
}
impl From<&[u8]> for ReadPipe<io::Cursor<Vec<u8>>> {
fn from(r: &[u8]) -> Self {
Self::from(r.to_vec())
}
}
impl From<String> for ReadPipe<io::Cursor<String>> {
fn from(r: String) -> Self {
Self::new(io::Cursor::new(r))
}
}
impl From<&str> for ReadPipe<io::Cursor<String>> {
fn from(r: &str) -> Self {
Self::from(r.to_string())
}
}
#[wiggle::async_trait]
impl<R: Read + Any + Send + Sync> WasiFile for ReadPipe<R> {
fn as_any(&self) -> &dyn Any {
self
}
async fn get_filetype(&self) -> Result<FileType, Error> {
Ok(FileType::Pipe)
}
async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {
let n = self.borrow().read_vectored(bufs)?;
Ok(n.try_into()?)
}
}
/// A virtual pipe write end.
///
/// ```no_run
/// use wasi_common::{pipe::WritePipe, WasiCtx, Table};
/// let stdout = WritePipe::new_in_memory();
/// // Brint these instances from elsewhere (e.g. wasi-cap-std-sync):
/// let random = todo!();
/// let clocks = todo!();
/// let sched = todo!();
/// let table = Table::new();
/// let mut ctx = WasiCtx::new(random, clocks, sched, table);
/// ctx.set_stdout(Box::new(stdout.clone()));
/// // use ctx in an instance, then make sure it is dropped:
/// drop(ctx);
/// let contents: Vec<u8> = stdout.try_into_inner().expect("sole remaining reference to WritePipe").into_inner();
/// println!("contents of stdout: {:?}", contents);
/// ```
#[derive(Debug)]
pub struct WritePipe<W: Write> {
writer: Arc<RwLock<W>>,
}
impl<W: Write> Clone for WritePipe<W> {
fn clone(&self) -> Self {
Self {
writer: self.writer.clone(),
}
}
}
impl<W: Write> WritePipe<W> {
/// Create a new pipe from a `Write` type.
///
/// All `Handle` write operations delegate to writing to this underlying writer.
pub fn new(w: W) -> Self {
Self::from_shared(Arc::new(RwLock::new(w)))
}
/// Create a new pipe from a shareable `Write` type.
///
/// All `Handle` write operations delegate to writing to this underlying writer.
pub fn from_shared(writer: Arc<RwLock<W>>) -> Self {
Self { writer }
}
/// Try to convert this `WritePipe<W>` back to the underlying `W` type.
///
/// This will fail with `Err(self)` if multiple references to the underlying `W` exist.
pub fn try_into_inner(mut self) -> Result<W, Self> {
match Arc::try_unwrap(self.writer) {
Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),
Err(writer) => {
self.writer = writer;
Err(self)
}
}
}
fn borrow(&self) -> std::sync::RwLockWriteGuard<W> {
RwLock::write(&self.writer).unwrap()
}
}
impl WritePipe<io::Cursor<Vec<u8>>> {
/// Create a new writable virtual pipe backed by a `Vec<u8>` buffer.
pub fn new_in_memory() -> Self {
Self::new(io::Cursor::new(vec![]))
}
}
#[wiggle::async_trait]
impl<W: Write + Any + Send + Sync> WasiFile for WritePipe<W> {
fn as_any(&self) -> &dyn Any {
self
}
async fn get_filetype(&self) -> Result<FileType, Error> {
Ok(FileType::Pipe)
}
async fn get_fdflags(&self) -> Result<FdFlags, Error> {
Ok(FdFlags::APPEND)
}
async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
let n = self.borrow().write_vectored(bufs)?;
Ok(n.try_into()?)
}
}