wasi_common/pipe.rs
1// This is mostly stubs
2#![allow(unused_variables, dead_code)]
3//! Virtual pipes.
4//!
5//! These types provide easy implementations of `WasiFile` that mimic much of the behavior of Unix
6//! pipes. These are particularly helpful for redirecting WASI stdio handles to destinations other
7//! than OS files.
8//!
9//! Some convenience constructors are included for common backing types like `Vec<u8>` and `String`,
10//! but the virtual pipes can be instantiated with any `Read` or `Write` type.
11//!
12use crate::file::{FdFlags, FileType, WasiFile};
13use crate::Error;
14use std::any::Any;
15use std::io::{self, Read, Write};
16use std::sync::{Arc, RwLock};
17
18/// A virtual pipe read end.
19///
20/// A variety of `From` impls are provided so that common pipe types are easy to create. For example:
21///
22/// ```no_run
23/// use wasi_common::{pipe::ReadPipe, WasiCtx, Table};
24/// let stdin = ReadPipe::from("hello from stdin!");
25/// // Brint these instances from elsewhere (e.g. wasi-cap-std-sync):
26/// let random = todo!();
27/// let clocks = todo!();
28/// let sched = todo!();
29/// let table = Table::new();
30/// let mut ctx = WasiCtx::new(random, clocks, sched, table);
31/// ctx.set_stdin(Box::new(stdin.clone()));
32/// ```
33#[derive(Debug)]
34pub struct ReadPipe<R: Read> {
35 reader: Arc<RwLock<R>>,
36}
37
38impl<R: Read> Clone for ReadPipe<R> {
39 fn clone(&self) -> Self {
40 Self {
41 reader: self.reader.clone(),
42 }
43 }
44}
45
46impl<R: Read> ReadPipe<R> {
47 /// Create a new pipe from a `Read` type.
48 ///
49 /// All `Handle` read operations delegate to reading from this underlying reader.
50 pub fn new(r: R) -> Self {
51 Self::from_shared(Arc::new(RwLock::new(r)))
52 }
53
54 /// Create a new pipe from a shareable `Read` type.
55 ///
56 /// All `Handle` read operations delegate to reading from this underlying reader.
57 pub fn from_shared(reader: Arc<RwLock<R>>) -> Self {
58 Self { reader }
59 }
60
61 /// Try to convert this `ReadPipe<R>` back to the underlying `R` type.
62 ///
63 /// This will fail with `Err(self)` if multiple references to the underlying `R` exist.
64 pub fn try_into_inner(mut self) -> Result<R, Self> {
65 match Arc::try_unwrap(self.reader) {
66 Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),
67 Err(reader) => {
68 self.reader = reader;
69 Err(self)
70 }
71 }
72 }
73 fn borrow(&self) -> std::sync::RwLockWriteGuard<R> {
74 RwLock::write(&self.reader).unwrap()
75 }
76}
77
78impl From<Vec<u8>> for ReadPipe<io::Cursor<Vec<u8>>> {
79 fn from(r: Vec<u8>) -> Self {
80 Self::new(io::Cursor::new(r))
81 }
82}
83
84impl From<&[u8]> for ReadPipe<io::Cursor<Vec<u8>>> {
85 fn from(r: &[u8]) -> Self {
86 Self::from(r.to_vec())
87 }
88}
89
90impl From<String> for ReadPipe<io::Cursor<String>> {
91 fn from(r: String) -> Self {
92 Self::new(io::Cursor::new(r))
93 }
94}
95
96impl From<&str> for ReadPipe<io::Cursor<String>> {
97 fn from(r: &str) -> Self {
98 Self::from(r.to_string())
99 }
100}
101
102#[wiggle::async_trait]
103impl<R: Read + Any + Send + Sync> WasiFile for ReadPipe<R> {
104 fn as_any(&self) -> &dyn Any {
105 self
106 }
107 async fn get_filetype(&self) -> Result<FileType, Error> {
108 Ok(FileType::Pipe)
109 }
110 async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {
111 let n = self.borrow().read_vectored(bufs)?;
112 Ok(n.try_into()?)
113 }
114}
115
116/// A virtual pipe write end.
117///
118/// ```no_run
119/// use wasi_common::{pipe::WritePipe, WasiCtx, Table};
120/// let stdout = WritePipe::new_in_memory();
121/// // Brint these instances from elsewhere (e.g. wasi-cap-std-sync):
122/// let random = todo!();
123/// let clocks = todo!();
124/// let sched = todo!();
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#[wiggle::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}