wasi_cap_std_sync/
stdio.rs

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