trybuild_internals_api/
term.rs

1use std::io::{Result, Write};
2use std::sync::{Mutex, MutexGuard, OnceLock, PoisonError};
3use termcolor::{Color, ColorChoice, ColorSpec, StandardStream as Stream, WriteColor};
4
5static TERM: OnceLock<Mutex<Term>> = OnceLock::new();
6
7pub fn lock() -> MutexGuard<'static, Term> {
8    TERM.get_or_init(|| Mutex::new(Term::new()))
9        .lock()
10        .unwrap_or_else(PoisonError::into_inner)
11}
12
13pub fn bold() {
14    lock().set_color(ColorSpec::new().set_bold(true));
15}
16
17pub fn color(color: Color) {
18    lock().set_color(ColorSpec::new().set_fg(Some(color)));
19}
20
21pub fn bold_color(color: Color) {
22    lock().set_color(ColorSpec::new().set_bold(true).set_fg(Some(color)));
23}
24
25pub fn reset() {
26    lock().reset();
27}
28
29#[deny(unused_macros)]
30macro_rules! print {
31    ($($args:tt)*) => {{
32        use std::io::Write;
33        let _ = std::write!($crate::term::lock(), $($args)*);
34    }};
35}
36
37#[deny(unused_macros)]
38macro_rules! println {
39    ($($args:tt)*) => {{
40        use std::io::Write;
41        let _ = std::writeln!($crate::term::lock(), $($args)*);
42    }};
43}
44
45pub struct Term {
46    spec: ColorSpec,
47    stream: Stream,
48    start_of_line: bool,
49}
50
51impl Term {
52    fn new() -> Self {
53        Term {
54            spec: ColorSpec::new(),
55            stream: Stream::stderr(ColorChoice::Auto),
56            start_of_line: true,
57        }
58    }
59
60    fn set_color(&mut self, spec: &ColorSpec) {
61        if self.spec != *spec {
62            self.spec = spec.clone();
63            self.start_of_line = true;
64        }
65    }
66
67    fn reset(&mut self) {
68        self.spec = ColorSpec::new();
69        let _ = self.stream.reset();
70    }
71}
72
73impl Write for Term {
74    // Color one line at a time because Travis does not preserve color setting
75    // across output lines.
76    fn write(&mut self, mut buf: &[u8]) -> Result<usize> {
77        if self.spec.is_none() {
78            return self.stream.write(buf);
79        }
80
81        let len = buf.len();
82        while !buf.is_empty() {
83            if self.start_of_line {
84                let _ = self.stream.set_color(&self.spec);
85            }
86            match buf.iter().position(|byte| *byte == b'\n') {
87                Some(line_len) => {
88                    self.stream.write_all(&buf[..line_len + 1])?;
89                    self.start_of_line = true;
90                    buf = &buf[line_len + 1..];
91                }
92                None => {
93                    self.stream.write_all(buf)?;
94                    self.start_of_line = false;
95                    break;
96                }
97            }
98        }
99        Ok(len)
100    }
101
102    fn flush(&mut self) -> Result<()> {
103        self.stream.flush()
104    }
105}