custom_print/io_try_writer.rs
1use core::fmt::{Arguments, Debug};
2use std::ffi;
3use std::io;
4
5use crate::{Flush, IntoTryWriteFn, WriteBytes, WriteStr};
6
7/// A writer that uses `write_bytes` and has both `write` and `flush` methods.
8///
9/// It calls `write_bytes` for each formatted chunk like the [`FmtTryWriter`],
10/// but provides write and flush methods that allows you to use [`BufWriter`], [`LineWriter`] etc.
11///
12/// Write function can return either `()`, `usize`, [`io::Result`]`<()>`, [`io::Result`]`<usize>`,
13/// [`Result`]`<(), `[`ffi::NulError`]`>` or [`Result`]`<usize, `[`ffi::NulError`]`>`.
14///
15/// The `usize` itself or in `Result` indicates how many bytes were written.
16/// `write_fmt` method that is used by [`write!`] and [`writeln!`]
17/// will continuously call write until there is no more data to be written
18/// or a non-[`ErrorKind::Interrupted`] kind is returned.
19///
20/// Flush function can return either `()` or [`io::Result`]`<()>`.
21///
22/// Writer propagates error to the caller if the write function returns `Result::Err`.
23///
24/// [`FmtTryWriter`]: struct.FmtTryWriter.html
25/// [`write!`]: https://doc.rust-lang.org/std/macro.write.html
26/// [`writeln!`]: https://doc.rust-lang.org/std/macro.writeln.html
27/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
28/// [`io::Result`]: https://doc.rust-lang.org/std/io/type.Result.html
29/// [`ffi::NulError`]: https://doc.rust-lang.org/std/ffi/struct.NulError.html
30/// [`BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html
31/// [`LineWriter`]: https://doc.rust-lang.org/std/io/struct.LineWriter.html
32/// [`ErrorKind::Interrupted`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.Interrupted
33#[derive(Clone, Copy, Debug, Eq, PartialEq)]
34pub struct IoTryWriter<F1, F2>(F1, F2);
35
36/// A helper trait used by [`IoTryWriter`] write method
37/// to convert wrapped function result to [`io::Result`] with error propagation.
38///
39/// [`io::Result`]: https://doc.rust-lang.org/std/io/type.Result.html
40pub trait IntoIoWriteResult {
41 /// Performs the conversion with error propagation.
42 fn into_io_write_result(self, buf: &[u8]) -> io::Result<usize>;
43}
44
45/// A helper trait used by [`IoTryWriter`] flush method
46/// to convert wrapped function result to [`io::Result`] with error propagation.
47///
48/// [`io::Result`]: https://doc.rust-lang.org/std/io/type.Result.html
49pub trait IntoIoFlushResult {
50 /// Performs the conversion with error propagation.
51 fn into_io_flush_result(self) -> io::Result<()>;
52}
53
54impl<F1, F2> IoTryWriter<F1, F2>
55where
56 F1: WriteStr,
57 F2: Flush,
58{
59 /// Creates a new `IoTryWriter` from an object that implements [`WriteBytes`]
60 /// and object that implements [`Flush`].
61 pub fn new(write: F1, flush: F2) -> Self {
62 Self(write, flush)
63 }
64}
65
66impl<F1> IoTryWriter<F1, ()>
67where
68 F1: WriteStr,
69{
70 /// Creates a new `IoTryWriter` with a [`WriteBytes`] wrapper
71 /// deduced with [`IntoTryWriteFn`] by the closure signature and constructed from it.
72 pub fn from_closure<F, Ts>(closure: F) -> Self
73 where
74 F: IntoTryWriteFn<Ts, TryWriteFn = F1>,
75 {
76 Self(closure.into_try_write_fn(), ())
77 }
78}
79
80impl<F1> IoTryWriter<F1, ()>
81where
82 Self: io::Write,
83{
84 /// Writes a formatted string into this writer, returning any error encountered.
85 ///
86 /// This method is primarily used to interface with the [`format_args!`] macro,
87 /// but it is rare that this should explicitly be called.
88 /// The [`write!`] macro should be favored to invoke this method instead.
89 ///
90 /// [`write!`]: https://doc.rust-lang.org/std/macro.write.html
91 /// [`format_args!`]: https://doc.rust-lang.org/std/macro.format_args.html
92 pub fn write_fmt(&mut self, args: Arguments<'_>) -> io::Result<()> {
93 io::Write::write_fmt(self, args)
94 }
95}
96
97impl<F1, F2> io::Write for IoTryWriter<F1, F2>
98where
99 Self: WriteBytes<Output = io::Result<usize>> + Flush<Output = io::Result<()>>,
100{
101 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
102 WriteBytes::write_bytes(self, buf)
103 }
104
105 fn flush(&mut self) -> io::Result<()> {
106 Flush::flush(self)
107 }
108}
109
110impl<F1, F2> WriteBytes for IoTryWriter<F1, F2>
111where
112 F1: WriteBytes,
113 F1::Output: IntoIoWriteResult,
114{
115 type Output = io::Result<usize>;
116
117 fn write_bytes(&mut self, buf: &[u8]) -> Self::Output {
118 self.0.write_bytes(buf).into_io_write_result(buf)
119 }
120}
121
122impl<F1, F2> Flush for IoTryWriter<F1, F2>
123where
124 F2: Flush,
125 F2::Output: IntoIoFlushResult,
126{
127 type Output = io::Result<()>;
128
129 fn flush(&mut self) -> Self::Output {
130 self.1.flush().into_io_flush_result()
131 }
132}
133
134impl IntoIoWriteResult for () {
135 fn into_io_write_result(self, buf: &[u8]) -> io::Result<usize> {
136 Ok(buf.len())
137 }
138}
139
140impl IntoIoWriteResult for usize {
141 fn into_io_write_result(self, buf: &[u8]) -> io::Result<usize> {
142 let _ = buf;
143 Ok(self)
144 }
145}
146
147impl IntoIoWriteResult for Result<(), ffi::NulError> {
148 fn into_io_write_result(self, buf: &[u8]) -> io::Result<usize> {
149 self.map_or_else(
150 |err| Err(io::Error::new(io::ErrorKind::InvalidData, err)),
151 |_| Ok(buf.len()),
152 )
153 }
154}
155
156impl IntoIoWriteResult for Result<usize, ffi::NulError> {
157 fn into_io_write_result(self, buf: &[u8]) -> io::Result<usize> {
158 let _ = buf;
159 self.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
160 }
161}
162
163impl IntoIoWriteResult for io::Result<()> {
164 fn into_io_write_result(self, buf: &[u8]) -> io::Result<usize> {
165 self.map(|_| buf.len())
166 }
167}
168
169impl IntoIoWriteResult for io::Result<usize> {
170 fn into_io_write_result(self, buf: &[u8]) -> io::Result<usize> {
171 let _ = buf;
172 self
173 }
174}
175
176impl IntoIoFlushResult for () {
177 fn into_io_flush_result(self) -> io::Result<()> {
178 Ok(())
179 }
180}
181
182impl IntoIoFlushResult for io::Result<()> {
183 fn into_io_flush_result(self) -> io::Result<()> {
184 self
185 }
186}