]>
Commit | Line | Data |
---|---|---|
04454e1e FG |
1 | use crate::util::color::ColorChoice; |
2 | ||
3 | use std::{ | |
4 | fmt::{self, Display, Formatter}, | |
5 | io::{self, Write}, | |
6 | }; | |
7 | ||
923072b8 FG |
8 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
9 | pub(crate) enum Stream { | |
10 | Stdout, | |
11 | Stderr, | |
12 | } | |
13 | ||
04454e1e FG |
14 | #[derive(Clone, Debug)] |
15 | pub(crate) struct Colorizer { | |
923072b8 | 16 | stream: Stream, |
04454e1e FG |
17 | #[allow(unused)] |
18 | color_when: ColorChoice, | |
19 | pieces: Vec<(String, Style)>, | |
20 | } | |
21 | ||
22 | impl Colorizer { | |
23 | #[inline(never)] | |
923072b8 | 24 | pub(crate) fn new(stream: Stream, color_when: ColorChoice) -> Self { |
04454e1e | 25 | Colorizer { |
923072b8 | 26 | stream, |
04454e1e FG |
27 | color_when, |
28 | pieces: vec![], | |
29 | } | |
30 | } | |
31 | ||
32 | #[inline(never)] | |
33 | pub(crate) fn good(&mut self, msg: impl Into<String>) { | |
34 | self.pieces.push((msg.into(), Style::Good)); | |
35 | } | |
36 | ||
37 | #[inline(never)] | |
38 | pub(crate) fn warning(&mut self, msg: impl Into<String>) { | |
39 | self.pieces.push((msg.into(), Style::Warning)); | |
40 | } | |
41 | ||
42 | #[inline(never)] | |
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)] | |
49 | pub(crate) fn hint(&mut self, msg: impl Into<String>) { | |
50 | self.pieces.push((msg.into(), Style::Hint)); | |
51 | } | |
52 | ||
53 | #[inline(never)] | |
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")] | |
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, | |
923072b8 | 67 | ColorChoice::Auto if is_a_tty(self.stream) => DepColorChoice::Auto, |
04454e1e FG |
68 | _ => DepColorChoice::Never, |
69 | }; | |
70 | ||
923072b8 FG |
71 | let writer = match self.stream { |
72 | Stream::Stderr => BufferWriter::stderr(color_when), | |
73 | Stream::Stdout => BufferWriter::stdout(color_when), | |
04454e1e FG |
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"))] | |
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. | |
923072b8 FG |
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 | } | |
04454e1e FG |
120 | } |
121 | } | |
122 | } | |
123 | ||
124 | /// Color-unaware printing. Never uses coloring. | |
125 | impl Display for Colorizer { | |
126 | 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 { | |
145 | fn default() -> Self { | |
146 | Self::Default | |
147 | } | |
148 | } | |
149 | ||
150 | #[cfg(feature = "color")] | |
923072b8 FG |
151 | fn is_a_tty(stream: Stream) -> bool { |
152 | let stream = match stream { | |
153 | Stream::Stdout => atty::Stream::Stdout, | |
154 | Stream::Stderr => atty::Stream::Stderr, | |
04454e1e FG |
155 | }; |
156 | ||
157 | atty::is(stream) | |
158 | } |