term_transcript/test/
utils.rs1use std::{
2 io::{self, IsTerminal, Write},
3 str,
4};
5
6use termcolor::{Ansi, ColorChoice, ColorSpec, NoColor, StandardStream, WriteColor};
7
8#[cfg(test)]
9use self::tests::print_to_buffer;
10
11#[cfg(test)]
13macro_rules! print {
14 ($($arg:tt)*) => (print_to_buffer(std::format_args!($($arg)*)));
15}
16#[cfg(test)]
17macro_rules! println {
18 ($($arg:tt)*) => {
19 print_to_buffer(std::format_args!($($arg)*));
20 print_to_buffer(std::format_args!("\n"));
21 }
22}
23
24#[derive(Debug)]
26pub(super) struct IndentingWriter<W> {
27 inner: W,
28 padding: &'static [u8],
29 new_line: bool,
30}
31
32impl<W: Write> IndentingWriter<W> {
33 pub fn new(writer: W, padding: &'static [u8]) -> Self {
34 Self {
35 inner: writer,
36 padding,
37 new_line: true,
38 }
39 }
40}
41
42impl<W: Write> Write for IndentingWriter<W> {
43 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
44 for (i, line) in buf.split(|&c| c == b'\n').enumerate() {
45 if i > 0 {
46 self.inner.write_all(b"\n")?;
47 }
48 if !line.is_empty() && (i > 0 || self.new_line) {
49 self.inner.write_all(self.padding)?;
50 }
51 self.inner.write_all(line)?;
52 }
53 self.new_line = buf.ends_with(b"\n");
54 Ok(buf.len())
55 }
56
57 fn flush(&mut self) -> io::Result<()> {
58 self.inner.flush()
59 }
60}
61
62#[derive(Debug, Default)]
76pub(super) struct PrintlnWriter {
77 line_buffer: Vec<u8>,
78}
79
80impl Write for PrintlnWriter {
81 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
82 for (i, line) in buf.split(|&c| c == b'\n').enumerate() {
83 if i > 0 {
84 let str = str::from_utf8(&self.line_buffer)
86 .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
87 println!("{str}");
88 self.line_buffer.clear();
89 }
90 self.line_buffer.extend_from_slice(line);
91 }
92 Ok(buf.len())
93 }
94
95 fn flush(&mut self) -> io::Result<()> {
96 let str = str::from_utf8(&self.line_buffer)
97 .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
98 print!("{str}");
99 self.line_buffer.clear();
100 Ok(())
101 }
102}
103
104pub(super) enum ColorPrintlnWriter {
106 NoColor(NoColor<PrintlnWriter>),
107 Ansi(Ansi<PrintlnWriter>),
108}
109
110impl ColorPrintlnWriter {
111 pub fn new(color_choice: ColorChoice) -> Self {
112 let is_ansi = match color_choice {
113 ColorChoice::Never => false,
114 ColorChoice::Always | ColorChoice::AlwaysAnsi => true,
115 ColorChoice::Auto => {
116 if io::stdout().is_terminal() {
117 StandardStream::stdout(color_choice).supports_color()
118 } else {
119 false
120 }
121 }
122 };
123
124 let inner = PrintlnWriter::default();
125 if is_ansi {
126 Self::Ansi(Ansi::new(inner))
127 } else {
128 Self::NoColor(NoColor::new(inner))
129 }
130 }
131}
132
133impl Write for ColorPrintlnWriter {
134 #[inline]
135 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
136 match self {
137 Self::Ansi(ansi) => ansi.write(buf),
138 Self::NoColor(no_color) => no_color.write(buf),
139 }
140 }
141
142 #[inline]
143 fn flush(&mut self) -> io::Result<()> {
144 match self {
145 Self::Ansi(ansi) => ansi.flush(),
146 Self::NoColor(no_color) => no_color.flush(),
147 }
148 }
149}
150
151impl WriteColor for ColorPrintlnWriter {
152 #[inline]
153 fn supports_color(&self) -> bool {
154 match self {
155 Self::Ansi(ansi) => ansi.supports_color(),
156 Self::NoColor(no_color) => no_color.supports_color(),
157 }
158 }
159
160 #[inline]
161 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
162 match self {
163 Self::Ansi(ansi) => ansi.set_color(spec),
164 Self::NoColor(no_color) => no_color.set_color(spec),
165 }
166 }
167
168 #[inline]
169 fn reset(&mut self) -> io::Result<()> {
170 match self {
171 Self::Ansi(ansi) => ansi.reset(),
172 Self::NoColor(no_color) => no_color.reset(),
173 }
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use std::{cell::RefCell, fmt, mem};
180
181 use super::*;
182
183 thread_local! {
184 static OUTPUT_CAPTURE: RefCell<Vec<u8>> = RefCell::default();
185 }
186
187 pub fn print_to_buffer(args: fmt::Arguments<'_>) {
188 OUTPUT_CAPTURE.with(|capture| {
189 let mut lock = capture.borrow_mut();
190 lock.write_fmt(args).ok();
191 });
192 }
193
194 #[test]
195 fn indenting_writer_basics() -> io::Result<()> {
196 let mut buffer = vec![];
197 let mut writer = IndentingWriter::new(&mut buffer, b" ");
198 write!(writer, "Hello, ")?;
199 writeln!(writer, "world!")?;
200 writeln!(writer, "many\n lines!")?;
201
202 assert_eq!(buffer, b" Hello, world!\n many\n lines!\n" as &[u8]);
203 Ok(())
204 }
205
206 #[test]
207 fn println_writer_basics() -> io::Result<()> {
208 let mut writer = PrintlnWriter::default();
209 write!(writer, "Hello, ")?;
210 writeln!(writer, "world!")?;
211 writeln!(writer, "many\n lines!")?;
212
213 let captured = OUTPUT_CAPTURE.with(|capture| {
214 let mut lock = capture.borrow_mut();
215 mem::take(&mut *lock)
216 });
217
218 assert_eq!(captured, b"Hello, world!\nmany\n lines!\n");
219 Ok(())
220 }
221}