1 #[cfg(all(feature = "color", not(target_os = "windows")))]
2 use ansi_term::ANSIString;
3 
4 #[cfg(all(feature = "color", not(target_os = "windows")))]
5 use ansi_term::Colour::{Green, Red, Yellow};
6 
7 use std::env;
8 use std::fmt;
9 
10 #[doc(hidden)]
11 #[derive(Debug, Copy, Clone, PartialEq)]
12 pub enum ColorWhen {
13     Auto,
14     Always,
15     Never,
16 }
17 
18 #[cfg(feature = "color")]
is_a_tty(stderr: bool) -> bool19 pub fn is_a_tty(stderr: bool) -> bool {
20     debugln!("is_a_tty: stderr={:?}", stderr);
21     let stream = if stderr {
22         atty::Stream::Stderr
23     } else {
24         atty::Stream::Stdout
25     };
26     atty::is(stream)
27 }
28 
29 #[cfg(not(feature = "color"))]
is_a_tty(_: bool) -> bool30 pub fn is_a_tty(_: bool) -> bool {
31     debugln!("is_a_tty;");
32     false
33 }
34 
is_term_dumb() -> bool35 pub fn is_term_dumb() -> bool {
36     env::var("TERM").ok() == Some(String::from("dumb"))
37 }
38 
39 #[doc(hidden)]
40 pub struct ColorizerOption {
41     pub use_stderr: bool,
42     pub when: ColorWhen,
43 }
44 
45 #[doc(hidden)]
46 pub struct Colorizer {
47     when: ColorWhen,
48 }
49 
50 macro_rules! color {
51     ($_self:ident, $c:ident, $m:expr) => {
52         match $_self.when {
53             ColorWhen::Auto => Format::$c($m),
54             ColorWhen::Always => Format::$c($m),
55             ColorWhen::Never => Format::None($m),
56         }
57     };
58 }
59 
60 impl Colorizer {
new(option: ColorizerOption) -> Colorizer61     pub fn new(option: ColorizerOption) -> Colorizer {
62         let is_a_tty = is_a_tty(option.use_stderr);
63         let is_term_dumb = is_term_dumb();
64         Colorizer {
65             when: match option.when {
66                 ColorWhen::Auto if is_a_tty && !is_term_dumb => ColorWhen::Auto,
67                 ColorWhen::Auto => ColorWhen::Never,
68                 when => when,
69             },
70         }
71     }
72 
good<T>(&self, msg: T) -> Format<T> where T: fmt::Display + AsRef<str>,73     pub fn good<T>(&self, msg: T) -> Format<T>
74     where
75         T: fmt::Display + AsRef<str>,
76     {
77         debugln!("Colorizer::good;");
78         color!(self, Good, msg)
79     }
80 
warning<T>(&self, msg: T) -> Format<T> where T: fmt::Display + AsRef<str>,81     pub fn warning<T>(&self, msg: T) -> Format<T>
82     where
83         T: fmt::Display + AsRef<str>,
84     {
85         debugln!("Colorizer::warning;");
86         color!(self, Warning, msg)
87     }
88 
error<T>(&self, msg: T) -> Format<T> where T: fmt::Display + AsRef<str>,89     pub fn error<T>(&self, msg: T) -> Format<T>
90     where
91         T: fmt::Display + AsRef<str>,
92     {
93         debugln!("Colorizer::error;");
94         color!(self, Error, msg)
95     }
96 
none<T>(&self, msg: T) -> Format<T> where T: fmt::Display + AsRef<str>,97     pub fn none<T>(&self, msg: T) -> Format<T>
98     where
99         T: fmt::Display + AsRef<str>,
100     {
101         debugln!("Colorizer::none;");
102         Format::None(msg)
103     }
104 }
105 
106 impl Default for Colorizer {
default() -> Self107     fn default() -> Self {
108         Colorizer::new(ColorizerOption {
109             use_stderr: true,
110             when: ColorWhen::Auto,
111         })
112     }
113 }
114 
115 /// Defines styles for different types of error messages. Defaults to Error=Red, Warning=Yellow,
116 /// and Good=Green
117 #[derive(Debug)]
118 #[doc(hidden)]
119 pub enum Format<T> {
120     /// Defines the style used for errors, defaults to Red
121     Error(T),
122     /// Defines the style used for warnings, defaults to Yellow
123     Warning(T),
124     /// Defines the style used for good values, defaults to Green
125     Good(T),
126     /// Defines no formatting style
127     None(T),
128 }
129 
130 #[cfg(all(feature = "color", not(target_os = "windows")))]
131 impl<T: AsRef<str>> Format<T> {
format(&self) -> ANSIString132     fn format(&self) -> ANSIString {
133         match *self {
134             Format::Error(ref e) => Red.bold().paint(e.as_ref()),
135             Format::Warning(ref e) => Yellow.paint(e.as_ref()),
136             Format::Good(ref e) => Green.paint(e.as_ref()),
137             Format::None(ref e) => ANSIString::from(e.as_ref()),
138         }
139     }
140 }
141 
142 #[cfg(any(not(feature = "color"), target_os = "windows"))]
143 #[cfg_attr(feature = "cargo-clippy", allow(clippy::match_same_arms))]
144 impl<T: fmt::Display> Format<T> {
format(&self) -> &T145     fn format(&self) -> &T {
146         match *self {
147             Format::Error(ref e) => e,
148             Format::Warning(ref e) => e,
149             Format::Good(ref e) => e,
150             Format::None(ref e) => e,
151         }
152     }
153 }
154 
155 #[cfg(all(feature = "color", not(target_os = "windows")))]
156 impl<T: AsRef<str>> fmt::Display for Format<T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result157     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158         write!(f, "{}", &self.format())
159     }
160 }
161 
162 #[cfg(any(not(feature = "color"), target_os = "windows"))]
163 impl<T: fmt::Display> fmt::Display for Format<T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result164     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
165         write!(f, "{}", &self.format())
166     }
167 }
168 
169 #[cfg(all(test, feature = "color", not(target_os = "windows")))]
170 mod test {
171     use super::Format;
172     use ansi_term::ANSIString;
173     use ansi_term::Colour::{Green, Red, Yellow};
174 
175     #[test]
colored_output()176     fn colored_output() {
177         let err = Format::Error("error");
178         assert_eq!(
179             &*format!("{}", err),
180             &*format!("{}", Red.bold().paint("error"))
181         );
182         let good = Format::Good("good");
183         assert_eq!(&*format!("{}", good), &*format!("{}", Green.paint("good")));
184         let warn = Format::Warning("warn");
185         assert_eq!(&*format!("{}", warn), &*format!("{}", Yellow.paint("warn")));
186         let none = Format::None("none");
187         assert_eq!(
188             &*format!("{}", none),
189             &*format!("{}", ANSIString::from("none"))
190         );
191     }
192 }
193