nextest_runner/
write_str.rs

1// Copyright (c) The nextest Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Support for string-only writes.
5//!
6//! There are potential situations where nextest needs to abstract over multiple kinds of writers,
7//! but some of them do not accept arbitrary bytes -- they must be valid UTF-8.
8//!
9//! This is similar to [`std::fmt::Write`], but it returns [`std::io::Error`] instead for better
10//! error handling.
11
12use std::{
13    fmt,
14    io::{self, BufWriter, Write},
15};
16
17/// A trait that abstracts over writing strings to a writer.
18///
19/// For more, see the [module-level documentation](self).
20pub trait WriteStr {
21    /// Writes a string to the writer.
22    fn write_str(&mut self, s: &str) -> io::Result<()>;
23
24    /// Flushes the writer, ensuring that all intermediately buffered contents reach their
25    /// destination.
26    fn write_str_flush(&mut self) -> io::Result<()>;
27
28    /// Writes a single character to the writer.
29    fn write_char(&mut self, c: char) -> io::Result<()> {
30        self.write_str(c.encode_utf8(&mut [0; 4]))
31    }
32
33    /// Writes a formatted string to the writer.
34    fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
35        // This code is adapted from the `write_fmt` implementation for `std::io::Write`, and is
36        // used under the terms of the MIT and Apache-2.0 licenses.
37
38        // Create a shim which translates self to a fmt::Write and saves off errors instead of
39        // discarding them.
40        struct Adapter<'a, T: ?Sized> {
41            inner: &'a mut T,
42            error: Result<(), io::Error>,
43        }
44
45        impl<T: ?Sized + WriteStr> fmt::Write for Adapter<'_, T> {
46            fn write_str(&mut self, s: &str) -> fmt::Result {
47                match self.inner.write_str(s) {
48                    Ok(()) => Ok(()),
49                    Err(e) => {
50                        self.error = Err(e);
51                        Err(fmt::Error)
52                    }
53                }
54            }
55        }
56
57        let mut output = Adapter {
58            inner: self,
59            error: Ok(()),
60        };
61        match fmt::write(&mut output, fmt) {
62            Ok(()) => Ok(()),
63            Err(_) => {
64                // check if the error came from the underlying `Write` or not
65                if output.error.is_err() {
66                    output.error
67                } else {
68                    Err(io::Error::other("formatter error"))
69                }
70            }
71        }
72    }
73}
74
75impl WriteStr for String {
76    fn write_str(&mut self, s: &str) -> io::Result<()> {
77        self.push_str(s);
78        Ok(())
79    }
80
81    fn write_str_flush(&mut self) -> io::Result<()> {
82        Ok(())
83    }
84
85    fn write_char(&mut self, c: char) -> io::Result<()> {
86        self.push(c);
87        Ok(())
88    }
89}
90
91impl<W: Write> WriteStr for BufWriter<W> {
92    fn write_str(&mut self, s: &str) -> io::Result<()> {
93        self.write_all(s.as_bytes())
94    }
95
96    fn write_str_flush(&mut self) -> io::Result<()> {
97        self.flush()
98    }
99
100    fn write_char(&mut self, c: char) -> io::Result<()> {
101        self.write_all(c.encode_utf8(&mut [0; 4]).as_bytes())
102    }
103}
104
105impl<T: WriteStr + ?Sized> WriteStr for &mut T {
106    fn write_str(&mut self, s: &str) -> io::Result<()> {
107        (**self).write_str(s)
108    }
109
110    fn write_str_flush(&mut self) -> io::Result<()> {
111        (**self).write_str_flush()
112    }
113
114    fn write_char(&mut self, c: char) -> io::Result<()> {
115        (**self).write_char(c)
116    }
117
118    fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
119        (**self).write_fmt(fmt)
120    }
121}
122
123// Add more impls as needed.