1 | use once_cell::sync::OnceCell; |
2 | use std::io::{Result, Write}; |
3 | use std::sync::{Mutex, MutexGuard, PoisonError}; |
4 | use termcolor::{Color, ColorChoice, ColorSpec, StandardStream as Stream, WriteColor}; |
5 | |
6 | static TERM: OnceCell<Mutex<Term>> = OnceCell::new(); |
7 | |
8 | pub 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 | |
14 | pub fn bold() { |
15 | lock().set_color(ColorSpec::new().set_bold(true)); |
16 | } |
17 | |
18 | pub fn color(color: Color) { |
19 | lock().set_color(ColorSpec::new().set_fg(Some(color))); |
20 | } |
21 | |
22 | pub fn bold_color(color: Color) { |
23 | lock().set_color(ColorSpec::new().set_bold(true).set_fg(Some(color))); |
24 | } |
25 | |
26 | pub fn reset() { |
27 | lock().reset(); |
28 | } |
29 | |
30 | #[deny (unused_macros)] |
31 | macro_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)] |
39 | macro_rules! println { |
40 | ($($args:tt)*) => {{ |
41 | use std::io::Write; |
42 | let _ = std::writeln!($crate::term::lock(), $($args)*); |
43 | }}; |
44 | } |
45 | |
46 | pub struct Term { |
47 | spec: ColorSpec, |
48 | stream: Stream, |
49 | start_of_line: bool, |
50 | } |
51 | |
52 | impl 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 | |
74 | impl 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 | |