wasi_common/sync/
stdio.rs

1use 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                // 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
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);