compio_fs/stdio/
windows.rs

1use std::{
2    io::{self, IsTerminal, Read, Write},
3    os::windows::io::AsRawHandle,
4    pin::Pin,
5    sync::OnceLock,
6    task::Poll,
7};
8
9use compio_buf::{BufResult, IntoInner, IoBuf, IoBufMut};
10use compio_driver::{
11    AsRawFd, OpCode, OpType, RawFd, SharedFd,
12    op::{BufResultExt, Recv, Send},
13};
14use compio_io::{AsyncRead, AsyncWrite};
15use compio_runtime::Runtime;
16use windows_sys::Win32::System::IO::OVERLAPPED;
17
18#[cfg(doc)]
19use super::{stderr, stdin, stdout};
20
21struct StdRead<R: Read, B: IoBufMut> {
22    reader: R,
23    buffer: B,
24}
25
26impl<R: Read, B: IoBufMut> StdRead<R, B> {
27    pub fn new(reader: R, buffer: B) -> Self {
28        Self { reader, buffer }
29    }
30}
31
32impl<R: Read, B: IoBufMut> OpCode for StdRead<R, B> {
33    fn op_type(&self) -> OpType {
34        OpType::Blocking
35    }
36
37    unsafe fn operate(self: Pin<&mut Self>, _optr: *mut OVERLAPPED) -> Poll<io::Result<usize>> {
38        let this = self.get_unchecked_mut();
39        let slice = this.buffer.as_mut_slice();
40        #[cfg(feature = "read_buf")]
41        {
42            let mut buf = io::BorrowedBuf::from(slice);
43            let mut cursor = buf.unfilled();
44            this.reader.read_buf(cursor.reborrow())?;
45            Poll::Ready(Ok(cursor.written()))
46        }
47        #[cfg(not(feature = "read_buf"))]
48        {
49            use std::mem::MaybeUninit;
50
51            slice.fill(MaybeUninit::new(0));
52            this.reader
53                .read(std::slice::from_raw_parts_mut(
54                    this.buffer.as_buf_mut_ptr(),
55                    this.buffer.buf_capacity(),
56                ))
57                .into()
58        }
59    }
60}
61
62impl<R: Read, B: IoBufMut> IntoInner for StdRead<R, B> {
63    type Inner = B;
64
65    fn into_inner(self) -> Self::Inner {
66        self.buffer
67    }
68}
69
70struct StdWrite<W: Write, B: IoBuf> {
71    writer: W,
72    buffer: B,
73}
74
75impl<W: Write, B: IoBuf> StdWrite<W, B> {
76    pub fn new(writer: W, buffer: B) -> Self {
77        Self { writer, buffer }
78    }
79}
80
81impl<W: Write, B: IoBuf> OpCode for StdWrite<W, B> {
82    fn op_type(&self) -> OpType {
83        OpType::Blocking
84    }
85
86    unsafe fn operate(self: Pin<&mut Self>, _optr: *mut OVERLAPPED) -> Poll<io::Result<usize>> {
87        let this = self.get_unchecked_mut();
88        let slice = this.buffer.as_slice();
89        this.writer.write(slice).into()
90    }
91}
92
93impl<W: Write, B: IoBuf> IntoInner for StdWrite<W, B> {
94    type Inner = B;
95
96    fn into_inner(self) -> Self::Inner {
97        self.buffer
98    }
99}
100
101static STDIN_ISATTY: OnceLock<bool> = OnceLock::new();
102
103/// A handle to the standard input stream of a process.
104///
105/// See [`stdin`].
106#[derive(Debug, Clone)]
107pub struct Stdin {
108    fd: SharedFd<RawFd>,
109    isatty: bool,
110}
111
112impl Stdin {
113    pub(crate) fn new() -> Self {
114        let stdin = io::stdin();
115        let isatty = *STDIN_ISATTY.get_or_init(|| {
116            stdin.is_terminal()
117                || Runtime::with_current(|r| r.attach(stdin.as_raw_handle() as _)).is_err()
118        });
119        Self {
120            fd: SharedFd::new(stdin.as_raw_handle() as _),
121            isatty,
122        }
123    }
124}
125
126impl AsyncRead for Stdin {
127    async fn read<B: IoBufMut>(&mut self, buf: B) -> BufResult<usize, B> {
128        if self.isatty {
129            let op = StdRead::new(io::stdin(), buf);
130            compio_runtime::submit(op).await.into_inner()
131        } else {
132            let op = Recv::new(self.fd.clone(), buf);
133            compio_runtime::submit(op).await.into_inner()
134        }
135        .map_advanced()
136    }
137}
138
139impl AsRawFd for Stdin {
140    fn as_raw_fd(&self) -> RawFd {
141        self.fd.as_raw_fd()
142    }
143}
144
145static STDOUT_ISATTY: OnceLock<bool> = OnceLock::new();
146
147/// A handle to the standard output stream of a process.
148///
149/// See [`stdout`].
150#[derive(Debug, Clone)]
151pub struct Stdout {
152    fd: SharedFd<RawFd>,
153    isatty: bool,
154}
155
156impl Stdout {
157    pub(crate) fn new() -> Self {
158        let stdout = io::stdout();
159        let isatty = *STDOUT_ISATTY.get_or_init(|| {
160            stdout.is_terminal()
161                || Runtime::with_current(|r| r.attach(stdout.as_raw_handle() as _)).is_err()
162        });
163        Self {
164            fd: SharedFd::new(stdout.as_raw_handle() as _),
165            isatty,
166        }
167    }
168}
169
170impl AsyncWrite for Stdout {
171    async fn write<T: IoBuf>(&mut self, buf: T) -> BufResult<usize, T> {
172        if self.isatty {
173            let op = StdWrite::new(io::stdout(), buf);
174            compio_runtime::submit(op).await.into_inner()
175        } else {
176            let op = Send::new(self.fd.clone(), buf);
177            compio_runtime::submit(op).await.into_inner()
178        }
179    }
180
181    async fn flush(&mut self) -> io::Result<()> {
182        Ok(())
183    }
184
185    async fn shutdown(&mut self) -> io::Result<()> {
186        self.flush().await
187    }
188}
189
190impl AsRawFd for Stdout {
191    fn as_raw_fd(&self) -> RawFd {
192        self.fd.as_raw_fd()
193    }
194}
195
196static STDERR_ISATTY: OnceLock<bool> = OnceLock::new();
197
198/// A handle to the standard output stream of a process.
199///
200/// See [`stderr`].
201#[derive(Debug, Clone)]
202pub struct Stderr {
203    fd: SharedFd<RawFd>,
204    isatty: bool,
205}
206
207impl Stderr {
208    pub(crate) fn new() -> Self {
209        let stderr = io::stderr();
210        let isatty = *STDERR_ISATTY.get_or_init(|| {
211            stderr.is_terminal()
212                || Runtime::with_current(|r| r.attach(stderr.as_raw_handle() as _)).is_err()
213        });
214        Self {
215            fd: SharedFd::new(stderr.as_raw_handle() as _),
216            isatty,
217        }
218    }
219}
220
221impl AsyncWrite for Stderr {
222    async fn write<T: IoBuf>(&mut self, buf: T) -> BufResult<usize, T> {
223        if self.isatty {
224            let op = StdWrite::new(io::stderr(), buf);
225            compio_runtime::submit(op).await.into_inner()
226        } else {
227            let op = Send::new(self.fd.clone(), buf);
228            compio_runtime::submit(op).await.into_inner()
229        }
230    }
231
232    async fn flush(&mut self) -> io::Result<()> {
233        Ok(())
234    }
235
236    async fn shutdown(&mut self) -> io::Result<()> {
237        self.flush().await
238    }
239}
240
241impl AsRawFd for Stderr {
242    fn as_raw_fd(&self) -> RawFd {
243        self.fd.as_raw_fd()
244    }
245}