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::file::{FdFlags, FileType, WasiFile};
11use crate::Error;
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/// ```no_run
21/// use wasi_common::{pipe::ReadPipe, WasiCtx, Table};
22/// let stdin = ReadPipe::from("hello from stdin!");
23/// // Brint these instances from elsewhere (e.g. wasi-cap-std-sync):
24/// let random = todo!();
25/// let clocks = todo!();
26/// let sched = todo!();
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> {
33    reader: Arc<RwLock<R>>,
34}
35
36impl<R: Read> Clone for ReadPipe<R> {
37    fn clone(&self) -> Self {
38        Self {
39            reader: self.reader.clone(),
40        }
41    }
42}
43
44impl<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.
48    pub fn new(r: R) -> Self {
49        Self::from_shared(Arc::new(RwLock::new(r)))
50    }
51
52    /// Create a new pipe from a shareable `Read` type.
53    ///
54    /// All `Handle` read operations delegate to reading from this underlying reader.
55    pub fn from_shared(reader: Arc<RwLock<R>>) -> Self {
56        Self { reader }
57    }
58
59    /// 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.
62    pub fn try_into_inner(mut self) -> Result<R, Self> {
63        match Arc::try_unwrap(self.reader) {
64            Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),
65            Err(reader) => {
66                self.reader = reader;
67                Err(self)
68            }
69        }
70    }
71    fn borrow(&self) -> std::sync::RwLockWriteGuard<R> {
72        RwLock::write(&self.reader).unwrap()
73    }
74}
75
76impl From<Vec<u8>> for ReadPipe<io::Cursor<Vec<u8>>> {
77    fn from(r: Vec<u8>) -> Self {
78        Self::new(io::Cursor::new(r))
79    }
80}
81
82impl From<&[u8]> for ReadPipe<io::Cursor<Vec<u8>>> {
83    fn from(r: &[u8]) -> Self {
84        Self::from(r.to_vec())
85    }
86}
87
88impl From<String> for ReadPipe<io::Cursor<String>> {
89    fn from(r: String) -> Self {
90        Self::new(io::Cursor::new(r))
91    }
92}
93
94impl From<&str> for ReadPipe<io::Cursor<String>> {
95    fn from(r: &str) -> Self {
96        Self::from(r.to_string())
97    }
98}
99
100#[wiggle::async_trait]
101impl<R: Read + Any + Send + Sync> WasiFile for ReadPipe<R> {
102    fn as_any(&self) -> &dyn Any {
103        self
104    }
105    async fn get_filetype(&self) -> Result<FileType, Error> {
106        Ok(FileType::Pipe)
107    }
108    async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {
109        let n = self.borrow().read_vectored(bufs)?;
110        Ok(n.try_into()?)
111    }
112}
113
114/// A virtual pipe write end.
115///
116/// ```no_run
117/// use wasi_common::{pipe::WritePipe, WasiCtx, Table};
118/// let stdout = WritePipe::new_in_memory();
119/// // Brint these instances from elsewhere (e.g. wasi-cap-std-sync):
120/// let random = todo!();
121/// let clocks = todo!();
122/// let sched = todo!();
123/// let table = Table::new();
124/// let mut ctx = WasiCtx::new(random, clocks, sched, table);
125/// ctx.set_stdout(Box::new(stdout.clone()));
126/// // use ctx in an instance, then make sure it is dropped:
127/// drop(ctx);
128/// let contents: Vec<u8> = stdout.try_into_inner().expect("sole remaining reference to WritePipe").into_inner();
129/// println!("contents of stdout: {:?}", contents);
130/// ```
131#[derive(Debug)]
132pub struct WritePipe<W: Write> {
133    writer: Arc<RwLock<W>>,
134}
135
136impl<W: Write> Clone for WritePipe<W> {
137    fn clone(&self) -> Self {
138        Self {
139            writer: self.writer.clone(),
140        }
141    }
142}
143
144impl<W: Write> WritePipe<W> {
145    /// Create a new pipe from a `Write` type.
146    ///
147    /// All `Handle` write operations delegate to writing to this underlying writer.
148    pub fn new(w: W) -> Self {
149        Self::from_shared(Arc::new(RwLock::new(w)))
150    }
151
152    /// Create a new pipe from a shareable `Write` type.
153    ///
154    /// All `Handle` write operations delegate to writing to this underlying writer.
155    pub fn from_shared(writer: Arc<RwLock<W>>) -> Self {
156        Self { writer }
157    }
158
159    /// Try to convert this `WritePipe<W>` back to the underlying `W` type.
160    ///
161    /// This will fail with `Err(self)` if multiple references to the underlying `W` exist.
162    pub fn try_into_inner(mut self) -> Result<W, Self> {
163        match Arc::try_unwrap(self.writer) {
164            Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),
165            Err(writer) => {
166                self.writer = writer;
167                Err(self)
168            }
169        }
170    }
171
172    fn borrow(&self) -> std::sync::RwLockWriteGuard<W> {
173        RwLock::write(&self.writer).unwrap()
174    }
175}
176
177impl WritePipe<io::Cursor<Vec<u8>>> {
178    /// Create a new writable virtual pipe backed by a `Vec<u8>` buffer.
179    pub fn new_in_memory() -> Self {
180        Self::new(io::Cursor::new(vec![]))
181    }
182}
183
184#[wiggle::async_trait]
185impl<W: Write + Any + Send + Sync> WasiFile for WritePipe<W> {
186    fn as_any(&self) -> &dyn Any {
187        self
188    }
189    async fn get_filetype(&self) -> Result<FileType, Error> {
190        Ok(FileType::Pipe)
191    }
192    async fn get_fdflags(&self) -> Result<FdFlags, Error> {
193        Ok(FdFlags::APPEND)
194    }
195    async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
196        let n = self.borrow().write_vectored(bufs)?;
197        Ok(n.try_into()?)
198    }
199}