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}