wasi_common/sync/
stdio.rs1use crate::sync::file::convert_systimespec;
2use fs_set_times::SetTimes;
3use std::any::Any;
4use std::io::{self, IsTerminal, Read, Write};
5use system_interface::io::ReadReady;
6
7use crate::{
8 file::{FdFlags, FileType, WasiFile},
9 Error, ErrorExt,
10};
11#[cfg(windows)]
12use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket};
13#[cfg(unix)]
14use io_lifetimes::{AsFd, BorrowedFd};
15#[cfg(windows)]
16use io_lifetimes::{AsHandle, BorrowedHandle};
17
18pub struct Stdin(std::io::Stdin);
19
20pub fn stdin() -> Stdin {
21 Stdin(std::io::stdin())
22}
23
24#[wiggle::async_trait]
25impl WasiFile for Stdin {
26 fn as_any(&self) -> &dyn Any {
27 self
28 }
29
30 #[cfg(unix)]
31 fn pollable(&self) -> Option<rustix::fd::BorrowedFd> {
32 Some(self.0.as_fd())
33 }
34
35 #[cfg(windows)]
36 fn pollable(&self) -> Option<io_extras::os::windows::RawHandleOrSocket> {
37 Some(self.0.as_raw_handle_or_socket())
38 }
39
40 async fn get_filetype(&self) -> Result<FileType, Error> {
41 if self.isatty() {
42 Ok(FileType::CharacterDevice)
43 } else {
44 Ok(FileType::Unknown)
45 }
46 }
47 async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {
48 let n = self.0.lock().read_vectored(bufs)?;
49 Ok(n.try_into().map_err(|_| Error::range())?)
50 }
51 async fn read_vectored_at<'a>(
52 &self,
53 _bufs: &mut [io::IoSliceMut<'a>],
54 _offset: u64,
55 ) -> Result<u64, Error> {
56 Err(Error::seek_pipe())
57 }
58 async fn seek(&self, _pos: std::io::SeekFrom) -> Result<u64, Error> {
59 Err(Error::seek_pipe())
60 }
61 async fn peek(&self, _buf: &mut [u8]) -> Result<u64, Error> {
62 Err(Error::seek_pipe())
63 }
64 async fn set_times(
65 &self,
66 atime: Option<crate::SystemTimeSpec>,
67 mtime: Option<crate::SystemTimeSpec>,
68 ) -> Result<(), Error> {
69 self.0
70 .set_times(convert_systimespec(atime), convert_systimespec(mtime))?;
71 Ok(())
72 }
73 fn num_ready_bytes(&self) -> Result<u64, Error> {
74 Ok(self.0.num_ready_bytes()?)
75 }
76 fn isatty(&self) -> bool {
77 #[cfg(unix)]
78 return self.0.as_fd().is_terminal();
79 #[cfg(windows)]
80 return self.0.as_handle().is_terminal();
81 }
82}
83#[cfg(windows)]
84impl AsHandle for Stdin {
85 fn as_handle(&self) -> BorrowedHandle<'_> {
86 self.0.as_handle()
87 }
88}
89#[cfg(windows)]
90impl AsRawHandleOrSocket for Stdin {
91 #[inline]
92 fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
93 self.0.as_raw_handle_or_socket()
94 }
95}
96#[cfg(unix)]
97impl AsFd for Stdin {
98 fn as_fd(&self) -> BorrowedFd<'_> {
99 self.0.as_fd()
100 }
101}
102
103macro_rules! wasi_file_write_impl {
104 ($ty:ty, $ident:ident) => {
105 #[wiggle::async_trait]
106 impl WasiFile for $ty {
107 fn as_any(&self) -> &dyn Any {
108 self
109 }
110 #[cfg(unix)]
111 fn pollable(&self) -> Option<rustix::fd::BorrowedFd> {
112 Some(self.0.as_fd())
113 }
114 #[cfg(windows)]
115 fn pollable(&self) -> Option<io_extras::os::windows::RawHandleOrSocket> {
116 Some(self.0.as_raw_handle_or_socket())
117 }
118 async fn get_filetype(&self) -> Result<FileType, Error> {
119 if self.isatty() {
120 Ok(FileType::CharacterDevice)
121 } else {
122 Ok(FileType::Unknown)
123 }
124 }
125 async fn get_fdflags(&self) -> Result<FdFlags, Error> {
126 Ok(FdFlags::APPEND)
127 }
128 async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
129 let mut io = self.0.lock();
130 let n = io.write_vectored(bufs)?;
131 io.flush()?;
135 Ok(n.try_into().map_err(|_| {
136 Error::range().context("converting write_vectored total length")
137 })?)
138 }
139 async fn write_vectored_at<'a>(
140 &self,
141 _bufs: &[io::IoSlice<'a>],
142 _offset: u64,
143 ) -> Result<u64, Error> {
144 Err(Error::seek_pipe())
145 }
146 async fn seek(&self, _pos: std::io::SeekFrom) -> Result<u64, Error> {
147 Err(Error::seek_pipe())
148 }
149 async fn set_times(
150 &self,
151 atime: Option<crate::SystemTimeSpec>,
152 mtime: Option<crate::SystemTimeSpec>,
153 ) -> Result<(), Error> {
154 self.0
155 .set_times(convert_systimespec(atime), convert_systimespec(mtime))?;
156 Ok(())
157 }
158 fn isatty(&self) -> bool {
159 self.0.is_terminal()
160 }
161 }
162 #[cfg(windows)]
163 impl AsHandle for $ty {
164 fn as_handle(&self) -> BorrowedHandle<'_> {
165 self.0.as_handle()
166 }
167 }
168 #[cfg(unix)]
169 impl AsFd for $ty {
170 fn as_fd(&self) -> BorrowedFd<'_> {
171 self.0.as_fd()
172 }
173 }
174 #[cfg(windows)]
175 impl AsRawHandleOrSocket for $ty {
176 #[inline]
177 fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
178 self.0.as_raw_handle_or_socket()
179 }
180 }
181 };
182}
183
184pub struct Stdout(std::io::Stdout);
185
186pub fn stdout() -> Stdout {
187 Stdout(std::io::stdout())
188}
189wasi_file_write_impl!(Stdout, Stdout);
190
191pub struct Stderr(std::io::Stderr);
192
193pub fn stderr() -> Stderr {
194 Stderr(std::io::stderr())
195}
196wasi_file_write_impl!(Stderr, Stderr);