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