1 use crate::sync::file::convert_systimespec; 2 use fs_set_times::SetTimes; 3 use std::any::Any; 4 use std::io::{self, IsTerminal, Read, Write}; 5 use system_interface::io::ReadReady; 6 7 use crate::{ 8 Error, ErrorExt, 9 file::{FdFlags, FileType, WasiFile}, 10 }; 11 #[cfg(windows)] 12 use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket}; 13 #[cfg(unix)] 14 use io_lifetimes::{AsFd, BorrowedFd}; 15 #[cfg(windows)] 16 use io_lifetimes::{AsHandle, BorrowedHandle}; 17 18 pub struct Stdin(std::io::Stdin); 19 20 pub fn stdin() -> Stdin { 21 Stdin(std::io::stdin()) 22 } 23 24 #[async_trait::async_trait] 25 impl 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)] 84 impl AsHandle for Stdin { 85 fn as_handle(&self) -> BorrowedHandle<'_> { 86 self.0.as_handle() 87 } 88 } 89 #[cfg(windows)] 90 impl 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)] 97 impl AsFd for Stdin { 98 fn as_fd(&self) -> BorrowedFd<'_> { 99 self.0.as_fd() 100 } 101 } 102 103 macro_rules! wasi_file_write_impl { 104 ($ty:ty, $ident:ident) => { 105 #[async_trait::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 // On a successful write additionally flush out the bytes to 132 // handle stdio buffering done by libstd since WASI interfaces 133 // here aren't buffered. 134 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 184 pub struct Stdout(std::io::Stdout); 185 186 pub fn stdout() -> Stdout { 187 Stdout(std::io::stdout()) 188 } 189 wasi_file_write_impl!(Stdout, Stdout); 190 191 pub struct Stderr(std::io::Stderr); 192 193 pub fn stderr() -> Stderr { 194 Stderr(std::io::stderr()) 195 } 196 wasi_file_write_impl!(Stderr, Stderr); 197