utf8_io/
utf8_duplexer.rs

1use crate::utf8_input::Utf8Input;
2use crate::utf8_output::Utf8Output;
3use crate::{ReadStr, WriteStr};
4use duplex::{Duplex, HalfDuplex};
5#[cfg(windows)]
6use io_extras::os::windows::{
7    AsHandleOrSocket, AsRawHandleOrSocket, AsReadWriteHandleOrSocket, BorrowedHandleOrSocket,
8    RawHandleOrSocket,
9};
10use std::io::{self, Read, Write};
11use std::{fmt, str};
12#[cfg(feature = "terminal-io")]
13use terminal_io::{DuplexTerminal, ReadTerminal, Terminal, TerminalColorSupport, WriteTerminal};
14#[cfg(feature = "layered-io")]
15use {
16    crate::ReadStrLayered,
17    layered_io::{Bufferable, HalfDuplexLayered, ReadLayered, Status, WriteLayered},
18    std::cmp::max,
19};
20#[cfg(not(windows))]
21use {
22    io_extras::os::rustix::{AsRawFd, AsReadWriteFd, RawFd},
23    std::os::fd::{AsFd, BorrowedFd},
24};
25
26/// An interactive UTF-8 stream, combining `Utf8Reader` and `Utf8Writer`.
27pub struct Utf8Duplexer<Inner: HalfDuplex> {
28    /// The wrapped byte stream.
29    pub(crate) inner: Inner,
30
31    /// UTF-8 translation state.
32    pub(crate) input: Utf8Input,
33    pub(crate) output: Utf8Output,
34}
35
36impl<Inner: HalfDuplex> Utf8Duplexer<Inner> {
37    /// Construct a new instance of `Utf8Duplexer` wrapping `inner`.
38    #[inline]
39    pub fn new(inner: Inner) -> Self {
40        Self {
41            inner,
42            input: Utf8Input::new(),
43            output: Utf8Output::new(),
44        }
45    }
46
47    /// Flush any pending output and return the inner output stream.
48    #[inline]
49    pub fn into_inner(self) -> io::Result<Inner> {
50        Utf8Output::into_inner(self)
51    }
52}
53
54#[cfg(feature = "layered-io")]
55impl<Inner: HalfDuplexLayered> Utf8Duplexer<Inner> {
56    /// Flush and close the underlying stream and return the underlying
57    /// stream object.
58    #[inline]
59    pub fn close_into_inner(self) -> io::Result<Inner> {
60        Utf8Output::into_inner(self)
61    }
62
63    /// Discard and close the underlying stream and return the underlying
64    /// stream object.
65    #[inline]
66    pub fn abandon_into_inner(self) -> Inner {
67        Utf8Output::abandon_into_inner(self)
68    }
69}
70
71#[cfg(feature = "terminal-io")]
72impl<Inner: Duplex + DuplexTerminal> Terminal for Utf8Duplexer<Inner> {}
73
74#[cfg(feature = "terminal-io")]
75impl<Inner: Duplex + DuplexTerminal> ReadTerminal for Utf8Duplexer<Inner> {
76    #[inline]
77    fn is_line_by_line(&self) -> bool {
78        self.inner.is_line_by_line()
79    }
80
81    #[inline]
82    fn is_input_terminal(&self) -> bool {
83        self.inner.is_input_terminal()
84    }
85}
86
87#[cfg(feature = "terminal-io")]
88impl<Inner: Duplex + DuplexTerminal> WriteTerminal for Utf8Duplexer<Inner> {
89    #[inline]
90    fn color_support(&self) -> TerminalColorSupport {
91        self.inner.color_support()
92    }
93
94    #[inline]
95    fn color_preference(&self) -> bool {
96        self.inner.color_preference()
97    }
98
99    #[inline]
100    fn is_output_terminal(&self) -> bool {
101        self.inner.is_output_terminal()
102    }
103}
104
105#[cfg(feature = "terminal-io")]
106impl<Inner: HalfDuplex + DuplexTerminal> DuplexTerminal for Utf8Duplexer<Inner> {}
107
108impl<Inner: HalfDuplex> ReadStr for Utf8Duplexer<Inner> {
109    #[inline]
110    fn read_str(&mut self, buf: &mut str) -> io::Result<usize> {
111        Utf8Input::read_str(self, buf)
112    }
113
114    #[inline]
115    fn read_exact_str(&mut self, buf: &mut str) -> io::Result<()> {
116        Utf8Input::read_exact_str(self, buf)
117    }
118}
119
120#[cfg(feature = "layered-io")]
121impl<Inner: HalfDuplexLayered> ReadStrLayered for Utf8Duplexer<Inner> {
122    #[inline]
123    fn read_str_with_status(&mut self, buf: &mut str) -> io::Result<(usize, Status)> {
124        Utf8Input::read_str_with_status(self, buf)
125    }
126
127    #[inline]
128    fn read_exact_str_using_status(&mut self, buf: &mut str) -> io::Result<Status> {
129        Utf8Input::read_exact_str_using_status(self, buf)
130    }
131}
132
133#[cfg(feature = "layered-io")]
134impl<Inner: HalfDuplexLayered> ReadLayered for Utf8Duplexer<Inner> {
135    #[inline]
136    fn read_with_status(&mut self, buf: &mut [u8]) -> io::Result<(usize, Status)> {
137        Utf8Input::read_with_status(self, buf)
138    }
139
140    #[inline]
141    fn minimum_buffer_size(&self) -> usize {
142        Utf8Input::minimum_buffer_size(self)
143    }
144}
145
146#[cfg(feature = "layered-io")]
147impl<Inner: HalfDuplexLayered> Bufferable for Utf8Duplexer<Inner> {
148    #[inline]
149    fn abandon(&mut self) {
150        Utf8Input::abandon(self);
151        Utf8Output::abandon(self);
152    }
153
154    #[inline]
155    fn suggested_buffer_size(&self) -> usize {
156        max(
157            Utf8Input::suggested_buffer_size(self),
158            Utf8Output::suggested_buffer_size(self),
159        )
160    }
161}
162
163impl<Inner: HalfDuplex> Read for Utf8Duplexer<Inner> {
164    #[inline]
165    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
166        Utf8Input::read(self, buf)
167    }
168
169    #[inline]
170    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
171        Utf8Input::read_to_string(self, buf)
172    }
173}
174
175#[cfg(feature = "layered-io")]
176impl<Inner: HalfDuplexLayered> WriteLayered for Utf8Duplexer<Inner> {
177    #[inline]
178    fn close(&mut self) -> io::Result<()> {
179        Utf8Output::close(self)
180    }
181}
182
183impl<Inner: HalfDuplex> WriteStr for Utf8Duplexer<Inner> {
184    #[inline]
185    fn write_str(&mut self, s: &str) -> io::Result<()> {
186        Utf8Output::write_str(self, s)
187    }
188}
189
190impl<Inner: HalfDuplex> Duplex for Utf8Duplexer<Inner> {}
191
192impl<Inner: HalfDuplex> Write for Utf8Duplexer<Inner> {
193    #[inline]
194    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
195        Utf8Output::write(self, buf)
196    }
197
198    #[inline]
199    fn flush(&mut self) -> io::Result<()> {
200        Utf8Output::flush(self)
201    }
202}
203
204#[cfg(not(windows))]
205impl<Inner: HalfDuplex + AsRawFd> AsRawFd for Utf8Duplexer<Inner> {
206    #[inline]
207    fn as_raw_fd(&self) -> RawFd {
208        self.inner.as_raw_fd()
209    }
210}
211
212#[cfg(not(windows))]
213impl<Inner: HalfDuplex + AsFd> AsFd for Utf8Duplexer<Inner> {
214    #[inline]
215    fn as_fd(&self) -> BorrowedFd<'_> {
216        self.inner.as_fd()
217    }
218}
219
220#[cfg(windows)]
221impl<Inner: HalfDuplex + AsRawHandleOrSocket> AsRawHandleOrSocket for Utf8Duplexer<Inner> {
222    #[inline]
223    fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
224        self.inner.as_raw_handle_or_socket()
225    }
226}
227
228#[cfg(windows)]
229impl<Inner: HalfDuplex + AsHandleOrSocket> AsHandleOrSocket for Utf8Duplexer<Inner> {
230    #[inline]
231    fn as_handle_or_socket(&self) -> BorrowedHandleOrSocket<'_> {
232        self.inner.as_handle_or_socket()
233    }
234}
235
236impl<Inner: HalfDuplex + fmt::Debug> fmt::Debug for Utf8Duplexer<Inner> {
237    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
238        let mut b = f.debug_struct("Utf8Duplexer");
239        b.field("inner", &self.inner);
240        b.finish()
241    }
242}
243
244#[cfg(not(windows))]
245impl<Inner: HalfDuplex + AsReadWriteFd> AsReadWriteFd for Utf8Duplexer<Inner> {
246    #[inline]
247    fn as_read_fd(&self) -> BorrowedFd<'_> {
248        self.inner.as_read_fd()
249    }
250
251    #[inline]
252    fn as_write_fd(&self) -> BorrowedFd<'_> {
253        self.inner.as_write_fd()
254    }
255}
256
257#[cfg(windows)]
258impl<Inner: HalfDuplex + AsReadWriteHandleOrSocket> AsReadWriteHandleOrSocket
259    for Utf8Duplexer<Inner>
260{
261    #[inline]
262    fn as_read_handle_or_socket(&self) -> BorrowedHandleOrSocket<'_> {
263        self.inner.as_read_handle_or_socket()
264    }
265
266    #[inline]
267    fn as_write_handle_or_socket(&self) -> BorrowedHandleOrSocket<'_> {
268        self.inner.as_write_handle_or_socket()
269    }
270}