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::{fmt, io};
13
14/// A trait that abstracts over writing strings to a writer.
15///
16/// For more, see the [module-level documentation](self).
17pub trait WriteStr {
18    /// Writes a string to the writer.
19    fn write_str(&mut self, s: &str) -> io::Result<()>;
20
21    /// Flushes the writer, ensuring that all intermediately buffered contents reach their
22    /// destination.
23    fn write_str_flush(&mut self) -> io::Result<()>;
24
25    /// Writes a single character to the writer.
26    fn write_char(&mut self, c: char) -> io::Result<()> {
27        self.write_str(c.encode_utf8(&mut [0; 4]))
28    }
29
30    /// Writes a formatted string to the writer.
31    fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
32        // This code is adapted from the `write_fmt` implementation for `std::io::Write`, and is
33        // used under the terms of the MIT and Apache-2.0 licenses.
34
35        // Create a shim which translates self to a fmt::Write and saves off errors instead of
36        // discarding them.
37        struct Adapter<'a, T: ?Sized> {
38            inner: &'a mut T,
39            error: Result<(), io::Error>,
40        }
41
42        impl<T: ?Sized + WriteStr> fmt::Write for Adapter<'_, T> {
43            fn write_str(&mut self, s: &str) -> fmt::Result {
44                match self.inner.write_str(s) {
45                    Ok(()) => Ok(()),
46                    Err(e) => {
47                        self.error = Err(e);
48                        Err(fmt::Error)
49                    }
50                }
51            }
52        }
53
54        let mut output = Adapter {
55            inner: self,
56            error: Ok(()),
57        };
58        match fmt::write(&mut output, fmt) {
59            Ok(()) => Ok(()),
60            Err(_) => {
61                // check if the error came from the underlying `Write` or not
62                if output.error.is_err() {
63                    output.error
64                } else {
65                    Err(io::Error::new(io::ErrorKind::Other, "formatter error"))
66                }
67            }
68        }
69    }
70}
71
72impl WriteStr for String {
73    fn write_str(&mut self, s: &str) -> io::Result<()> {
74        self.push_str(s);
75        Ok(())
76    }
77
78    fn write_str_flush(&mut self) -> io::Result<()> {
79        Ok(())
80    }
81
82    fn write_char(&mut self, c: char) -> io::Result<()> {
83        self.push(c);
84        Ok(())
85    }
86}
87
88impl<T: WriteStr + ?Sized> WriteStr for &mut T {
89    fn write_str(&mut self, s: &str) -> io::Result<()> {
90        (**self).write_str(s)
91    }
92
93    fn write_str_flush(&mut self) -> io::Result<()> {
94        (**self).write_str_flush()
95    }
96
97    fn write_char(&mut self, c: char) -> io::Result<()> {
98        (**self).write_char(c)
99    }
100
101    fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
102        (**self).write_fmt(fmt)
103    }
104}
105
106// Add more impls as needed.