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