utf8_io/
utf8_writer.rs

1use crate::utf8_output::Utf8Output;
2use crate::WriteStr;
3#[cfg(windows)]
4use io_extras::os::windows::{
5    AsHandleOrSocket, AsRawHandleOrSocket, BorrowedHandleOrSocket, RawHandleOrSocket,
6};
7#[cfg(feature = "layered-io")]
8use layered_io::{Bufferable, WriteLayered};
9use std::io::{self, Write};
10use std::{fmt, str};
11#[cfg(feature = "terminal-io")]
12use terminal_io::{Terminal, TerminalColorSupport, WriteTerminal};
13#[cfg(not(windows))]
14use {
15    io_extras::os::rustix::{AsRawFd, RawFd},
16    std::os::fd::{AsFd, BorrowedFd},
17};
18
19/// A [`Write`] implementation which translates into an output `Write`
20/// producing a valid UTF-8 sequence from an arbitrary byte sequence from an
21/// arbitrary byte sequence. Attempts to write invalid encodings are reported
22/// as errors.
23///
24/// This type's `write` is not guaranteed to perform a single underlying
25/// `write` operation, because short writes could produce invalid UTF-8, so
26/// `write` will retry as needed.
27pub struct Utf8Writer<Inner: Write> {
28    /// The wrapped byte stream.
29    pub(crate) inner: Inner,
30
31    /// UTF-8 translation state.
32    pub(crate) output: Utf8Output,
33}
34
35impl<Inner: Write> Utf8Writer<Inner> {
36    /// Construct a new instance of `Utf8Writer` wrapping `inner`.
37    #[inline]
38    pub fn new(inner: Inner) -> Self {
39        Self {
40            inner,
41            output: Utf8Output::new(),
42        }
43    }
44
45    /// Flush any pending output and return the inner stream.
46    #[inline]
47    pub fn into_inner(mut self) -> io::Result<Inner> {
48        self.flush()?;
49        Utf8Output::into_inner(self)
50    }
51}
52
53#[cfg(feature = "layered-io")]
54impl<Inner: WriteLayered> Utf8Writer<Inner> {
55    /// Flush and close the underlying stream and return the underlying
56    /// stream object.
57    #[inline]
58    pub fn close_into_inner(self) -> io::Result<Inner> {
59        Utf8Output::into_inner(self)
60    }
61
62    /// Discard and close the underlying stream and return the underlying
63    /// stream object.
64    #[inline]
65    pub fn abandon_into_inner(self) -> Inner {
66        Utf8Output::abandon_into_inner(self)
67    }
68}
69
70#[cfg(feature = "terminal-io")]
71impl<Inner: Write + WriteTerminal> Terminal for Utf8Writer<Inner> {}
72
73#[cfg(feature = "terminal-io")]
74impl<Inner: Write + WriteTerminal> WriteTerminal for Utf8Writer<Inner> {
75    #[inline]
76    fn color_support(&self) -> TerminalColorSupport {
77        self.inner.color_support()
78    }
79
80    #[inline]
81    fn color_preference(&self) -> bool {
82        self.inner.color_preference()
83    }
84
85    #[inline]
86    fn is_output_terminal(&self) -> bool {
87        self.inner.is_output_terminal()
88    }
89}
90
91#[cfg(feature = "layered-io")]
92impl<Inner: WriteLayered> WriteLayered for Utf8Writer<Inner> {
93    #[inline]
94    fn close(&mut self) -> io::Result<()> {
95        Utf8Output::close(self)
96    }
97}
98
99impl<Inner: Write> WriteStr for Utf8Writer<Inner> {
100    #[inline]
101    fn write_str(&mut self, s: &str) -> io::Result<()> {
102        Utf8Output::write_str(self, s)
103    }
104}
105
106#[cfg(feature = "layered-io")]
107impl<Inner: WriteLayered> Bufferable for Utf8Writer<Inner> {
108    #[inline]
109    fn abandon(&mut self) {
110        Utf8Output::abandon(self)
111    }
112
113    #[inline]
114    fn suggested_buffer_size(&self) -> usize {
115        Utf8Output::suggested_buffer_size(self)
116    }
117}
118
119impl<Inner: Write> Write for Utf8Writer<Inner> {
120    #[inline]
121    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
122        Utf8Output::write(self, buf)
123    }
124
125    #[inline]
126    fn flush(&mut self) -> io::Result<()> {
127        Utf8Output::flush(self)
128    }
129}
130
131#[cfg(not(windows))]
132impl<Inner: Write + AsRawFd> AsRawFd for Utf8Writer<Inner> {
133    #[inline]
134    fn as_raw_fd(&self) -> RawFd {
135        self.inner.as_raw_fd()
136    }
137}
138
139#[cfg(not(windows))]
140impl<Inner: Write + AsFd> AsFd for Utf8Writer<Inner> {
141    #[inline]
142    fn as_fd(&self) -> BorrowedFd<'_> {
143        self.inner.as_fd()
144    }
145}
146
147#[cfg(windows)]
148impl<Inner: Write + AsRawHandleOrSocket> AsRawHandleOrSocket for Utf8Writer<Inner> {
149    #[inline]
150    fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
151        self.inner.as_raw_handle_or_socket()
152    }
153}
154
155#[cfg(windows)]
156impl<Inner: Write + AsHandleOrSocket> AsHandleOrSocket for Utf8Writer<Inner> {
157    #[inline]
158    fn as_handle_or_socket(&self) -> BorrowedHandleOrSocket<'_> {
159        self.inner.as_handle_or_socket()
160    }
161}
162
163impl<Inner: Write + fmt::Debug> fmt::Debug for Utf8Writer<Inner> {
164    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165        let mut b = f.debug_struct("Utf8Writer");
166        b.field("inner", &self.inner);
167        b.finish()
168    }
169}