• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::util::color::ColorChoice;
2 
3 use std::{
4     fmt::{self, Display, Formatter},
5     io::{self, Write},
6 };
7 
8 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
9 pub(crate) enum Stream {
10     Stdout,
11     Stderr,
12 }
13 
14 #[derive(Clone, Debug)]
15 pub(crate) struct Colorizer {
16     stream: Stream,
17     #[allow(unused)]
18     color_when: ColorChoice,
19     pieces: Vec<(String, Style)>,
20 }
21 
22 impl Colorizer {
23     #[inline(never)]
new(stream: Stream, color_when: ColorChoice) -> Self24     pub(crate) fn new(stream: Stream, color_when: ColorChoice) -> Self {
25         Colorizer {
26             stream,
27             color_when,
28             pieces: vec![],
29         }
30     }
31 
32     #[inline(never)]
good(&mut self, msg: impl Into<String>)33     pub(crate) fn good(&mut self, msg: impl Into<String>) {
34         self.pieces.push((msg.into(), Style::Good));
35     }
36 
37     #[inline(never)]
warning(&mut self, msg: impl Into<String>)38     pub(crate) fn warning(&mut self, msg: impl Into<String>) {
39         self.pieces.push((msg.into(), Style::Warning));
40     }
41 
42     #[inline(never)]
error(&mut self, msg: impl Into<String>)43     pub(crate) fn error(&mut self, msg: impl Into<String>) {
44         self.pieces.push((msg.into(), Style::Error));
45     }
46 
47     #[inline(never)]
48     #[allow(dead_code)]
hint(&mut self, msg: impl Into<String>)49     pub(crate) fn hint(&mut self, msg: impl Into<String>) {
50         self.pieces.push((msg.into(), Style::Hint));
51     }
52 
53     #[inline(never)]
none(&mut self, msg: impl Into<String>)54     pub(crate) fn none(&mut self, msg: impl Into<String>) {
55         self.pieces.push((msg.into(), Style::Default));
56     }
57 }
58 
59 /// Printing methods.
60 impl Colorizer {
61     #[cfg(feature = "color")]
print(&self) -> io::Result<()>62     pub(crate) fn print(&self) -> io::Result<()> {
63         use termcolor::{BufferWriter, ColorChoice as DepColorChoice, ColorSpec, WriteColor};
64 
65         let color_when = match self.color_when {
66             ColorChoice::Always => DepColorChoice::Always,
67             ColorChoice::Auto if is_a_tty(self.stream) => DepColorChoice::Auto,
68             _ => DepColorChoice::Never,
69         };
70 
71         let writer = match self.stream {
72             Stream::Stderr => BufferWriter::stderr(color_when),
73             Stream::Stdout => BufferWriter::stdout(color_when),
74         };
75 
76         let mut buffer = writer.buffer();
77 
78         for piece in &self.pieces {
79             let mut color = ColorSpec::new();
80             match piece.1 {
81                 Style::Good => {
82                     color.set_fg(Some(termcolor::Color::Green));
83                 }
84                 Style::Warning => {
85                     color.set_fg(Some(termcolor::Color::Yellow));
86                 }
87                 Style::Error => {
88                     color.set_fg(Some(termcolor::Color::Red));
89                     color.set_bold(true);
90                 }
91                 Style::Hint => {
92                     color.set_dimmed(true);
93                 }
94                 Style::Default => {}
95             }
96 
97             buffer.set_color(&color)?;
98             buffer.write_all(piece.0.as_bytes())?;
99             buffer.reset()?;
100         }
101 
102         writer.print(&buffer)
103     }
104 
105     #[cfg(not(feature = "color"))]
print(&self) -> io::Result<()>106     pub(crate) fn print(&self) -> io::Result<()> {
107         // [e]println can't be used here because it panics
108         // if something went wrong. We don't want that.
109         match self.stream {
110             Stream::Stdout => {
111                 let stdout = std::io::stdout();
112                 let mut stdout = stdout.lock();
113                 write!(stdout, "{}", self)
114             }
115             Stream::Stderr => {
116                 let stderr = std::io::stderr();
117                 let mut stderr = stderr.lock();
118                 write!(stderr, "{}", self)
119             }
120         }
121     }
122 }
123 
124 /// Color-unaware printing. Never uses coloring.
125 impl Display for Colorizer {
fmt(&self, f: &mut Formatter) -> fmt::Result126     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
127         for piece in &self.pieces {
128             Display::fmt(&piece.0, f)?;
129         }
130 
131         Ok(())
132     }
133 }
134 
135 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
136 pub enum Style {
137     Good,
138     Warning,
139     Error,
140     Hint,
141     Default,
142 }
143 
144 impl Default for Style {
default() -> Self145     fn default() -> Self {
146         Self::Default
147     }
148 }
149 
150 #[cfg(feature = "color")]
is_a_tty(stream: Stream) -> bool151 fn is_a_tty(stream: Stream) -> bool {
152     let stream = match stream {
153         Stream::Stdout => atty::Stream::Stdout,
154         Stream::Stderr => atty::Stream::Stderr,
155     };
156 
157     atty::is(stream)
158 }
159