stylish_core/io.rs
1//! Traits and associated types for writing [`stylish`] attributed data to
2//! fallible IO sinks.
3
4pub use std::io::{Error, ErrorKind, Result};
5
6use crate::{Arguments, Style};
7
8struct ErrorTrap<W: Write> {
9 inner: W,
10 error: Option<Error>,
11}
12
13impl<W: Write> ErrorTrap<W> {
14 fn new(inner: W) -> Self {
15 Self { inner, error: None }
16 }
17
18 fn error(&mut self) -> Error {
19 self.error
20 .take()
21 .unwrap_or_else(|| Error::new(ErrorKind::Other, "formatter error"))
22 }
23}
24
25impl<W: Write> crate::Write for ErrorTrap<W> {
26 fn write_str(&mut self, s: &str, style: Style) -> crate::Result {
27 match self.inner.write_all(s.as_bytes(), style) {
28 Ok(()) => Ok(()),
29 Err(err) => {
30 self.error = Some(err);
31 Err(crate::Error)
32 }
33 }
34 }
35}
36
37/// A trait for objects which are byte-oriented sinks and can handle attributed
38/// data.
39///
40/// Writers are defined by two required methods, [`write`] and [`flush`]:
41///
42/// * The [`write`] method will attempt to write some data into the object,
43/// returning how many bytes were successfully written.
44/// * The [`flush`] method is useful for adaptors and explicit buffers
45/// themselves for ensuring that all buffered data has been pushed out to the
46/// 'true sink'.
47///
48/// [`write`]: Write::write
49/// [`flush`]: Write::flush
50///
51/// # Examples
52///
53/// ```rust
54/// use stylish::{io::Write, Foreground, Color, Style};
55///
56/// let data = b"some bytes";
57/// let style = Style::default().with(Foreground(Color::Blue));
58///
59/// let mut pos = 0;
60/// let mut output = stylish::io::plain(std::io::stdout());
61///
62/// while pos < data.len() {
63/// let bytes_written = output.write(&data[pos..], style)?;
64/// pos += bytes_written;
65/// }
66///
67/// output.flush()?;
68/// # Ok::<(), std::io::Error>(())
69pub trait Write {
70 /// Write a buffer into this writer with a specified style, returning how
71 /// many bytes were written.
72 ///
73 /// This function will attempt to write the entire contents of `buf`, but
74 /// the entire write may not succeed, or the write may also generate an
75 /// error. A call to `write` represents at most one attempt to write to
76 /// any wrapped object, plus whatever is needed to change the style.
77 ///
78 /// Calls to `write` are not guaranteed to block waiting for data to be
79 /// written, and a write which would otherwise block can be indicated
80 /// through an [`Err`] variant.
81 ///
82 /// If the return value is `Ok(n)` then it must be guaranteed that `n <=
83 /// buf.len()`. A return value of `0` typically means that the
84 /// underlying object is no longer able to accept bytes and will likely
85 /// not be able to in the future as well, or that the buffer provided is
86 /// empty.
87 ///
88 /// # Errors
89 ///
90 /// Each call to `write` may generate an I/O error indicating that the
91 /// operation could not be completed. If an error is returned then no
92 /// bytes in the buffer were written to this writer, but the writers
93 /// state may have changed to match the new style (or may be in an
94 /// inconsistent state if the error occurred while changing style).
95 ///
96 /// It is not considered an error if the entire buffer could not be written
97 /// to this writer.
98 ///
99 /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the
100 /// write operation should be retried if there is nothing else to do. (Note
101 /// to implementors: this means you must internally retry if changing style
102 /// and an `Interrupted` error occurs).
103 ///
104 /// ```rust
105 /// use stylish::{io::Write, Color, Foreground, Style};
106 ///
107 /// let mut output = stylish::io::plain(std::io::stdout());
108 ///
109 /// // Writes some prefix of the byte string, not necessarily all of it.
110 /// output.write(
111 /// b"some bytes",
112 /// Style::default().with(Foreground(Color::Blue)),
113 /// )?;
114 ///
115 /// output.flush()?;
116 /// # Ok::<(), std::io::Error>(())
117 /// ```
118 fn write(&mut self, buf: &[u8], style: Style) -> Result<usize>;
119
120 /// Flush this output stream, ensuring that all intermediately buffered
121 /// contents reach their destination.
122 ///
123 /// # Errors
124 ///
125 /// It is considered an error if not all bytes could be written due to I/O
126 /// errors or EOF being reached.
127 ///
128 /// ```rust
129 /// use stylish::{io::Write, Color, Foreground, Style};
130 ///
131 /// let mut output = stylish::io::plain(std::io::stdout());
132 ///
133 /// output.write_all(
134 /// b"some bytes",
135 /// Style::default().with(Foreground(Color::Blue)),
136 /// )?;
137 ///
138 /// output.flush()?;
139 /// # Ok::<(), std::io::Error>(())
140 /// ```
141 fn flush(&mut self) -> Result<()>;
142
143 /// Attempts to write an entire buffer into this writer using a specified
144 /// style.
145 ///
146 /// This method will continuously call [`write`] until there is no more data
147 /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is
148 /// returned. This method will not return until the entire buffer has
149 /// been successfully written or such an error occurs. The first
150 /// error that is not of [`ErrorKind::Interrupted`] kind generated from this
151 /// method will be returned.
152 ///
153 /// If the buffer contains no data, this will never call [`write`].
154 ///
155 /// # Errors
156 ///
157 /// This function will return the first error of
158 /// non-[`ErrorKind::Interrupted`] kind that [`write`] returns.
159 ///
160 /// [`write`]: Write::write
161 ///
162 /// # Examples
163 ///
164 /// ```rust
165 /// use stylish::{io::Write, Color, Foreground, Style};
166 ///
167 /// let mut output = stylish::io::plain(std::io::stdout());
168 ///
169 /// output.write_all(
170 /// b"some bytes",
171 /// Style::default().with(Foreground(Color::Blue)),
172 /// )?;
173 /// # Ok::<(), std::io::Error>(())
174 /// ```
175 fn write_all(&mut self, mut buf: &[u8], style: Style) -> Result<()> {
176 while !buf.is_empty() {
177 match self.write(buf, style) {
178 Ok(0) => {
179 return Err(Error::new(
180 ErrorKind::WriteZero,
181 "failed to write whole buffer",
182 ));
183 }
184 Ok(n) => buf = &buf[n..],
185 Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
186 Err(e) => return Err(e),
187 }
188 }
189 Ok(())
190 }
191
192 /// Writes a formatted string into this writer, returning any error
193 /// encountered.
194 ///
195 /// This method is primarily used to interface with the
196 /// [`stylish::format_args!`] macro, but it is rare that this should
197 /// explicitly be called. The [`stylish::write!`] macro should be
198 /// favored to invoke this method instead.
199 ///
200 /// This function internally uses the [`write_all`](Write::write_all) method
201 /// on this trait and hence will continuously write data so long as no
202 /// errors are received. This also means that partial writes are not
203 /// indicated in this signature.
204 ///
205 /// # Errors
206 ///
207 /// This function will return any I/O error reported while formatting.
208 ///
209 /// # Examples
210 ///
211 /// ```rust
212 /// let mut output = stylish::io::plain(std::io::stdout());
213 ///
214 /// output.write_fmt(stylish::format_args!("{:(fg=red)}", '☎'))?;
215 /// # Ok::<(), std::io::Error>(())
216 /// ```
217 #[inline]
218 fn write_fmt(&mut self, args: Arguments<'_>) -> Result<()> {
219 let mut trap = ErrorTrap::new(self);
220
221 crate::Write::write_fmt(&mut trap, args).map_err(|crate::Error| trap.error())
222 }
223
224 /// Creates a "by reference" adaptor for this instance of `Write`.
225 ///
226 /// The returned adaptor also implements `Write` and will simply borrow this
227 /// current writer.
228 ///
229 /// # Examples
230 ///
231 /// ```rust
232 /// use stylish::{io::Write, Color, Foreground, Style};
233 ///
234 /// let mut output = stylish::io::plain(std::io::stdout());
235 ///
236 /// let reference = output.by_ref();
237 ///
238 /// // we can use reference just like our original output
239 /// reference.write_all(
240 /// b"some bytes",
241 /// Style::default().with(Foreground(Color::Blue)),
242 /// )?;
243 /// # Ok::<(), std::io::Error>(())
244 /// ```
245 #[inline]
246 fn by_ref(&mut self) -> &mut Self
247 where
248 Self: Sized,
249 {
250 self
251 }
252}
253
254impl<W: Write + ?Sized> Write for &mut W {
255 fn write(&mut self, s: &[u8], style: Style) -> Result<usize> {
256 (**self).write(s, style)
257 }
258
259 fn flush(&mut self) -> Result<()> {
260 (**self).flush()
261 }
262
263 fn write_all(&mut self, s: &[u8], style: Style) -> Result<()> {
264 (**self).write_all(s, style)
265 }
266
267 fn write_fmt(&mut self, args: Arguments<'_>) -> Result<()> {
268 (**self).write_fmt(args)
269 }
270}