wasi_common/
pipe.rs

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