clap/
fmt.rs

1#[cfg(all(feature = "color", not(target_os = "windows")))]
2use ansi_term::ANSIString;
3
4#[cfg(all(feature = "color", not(target_os = "windows")))]
5use ansi_term::Colour::{Green, Red, Yellow};
6
7use std::env;
8use std::fmt;
9
10#[doc(hidden)]
11#[derive(Debug, Copy, Clone, PartialEq)]
12pub enum ColorWhen {
13    Auto,
14    Always,
15    Never,
16}
17
18#[cfg(feature = "color")]
19pub 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"))]
30pub fn is_a_tty(_: bool) -> bool {
31    debugln!("is_a_tty;");
32    false
33}
34
35pub fn is_term_dumb() -> bool {
36    env::var("TERM").ok() == Some(String::from("dumb"))
37}
38
39#[doc(hidden)]
40pub struct ColorizerOption {
41    pub use_stderr: bool,
42    pub when: ColorWhen,
43}
44
45#[doc(hidden)]
46pub struct Colorizer {
47    when: ColorWhen,
48}
49
50macro_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
60impl Colorizer {
61    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
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
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
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
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
106impl Default for Colorizer {
107    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)]
119pub 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")))]
131impl<T: AsRef<str>> Format<T> {
132    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))]
144impl<T: fmt::Display> Format<T> {
145    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")))]
156impl<T: AsRef<str>> fmt::Display for Format<T> {
157    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"))]
163impl<T: fmt::Display> fmt::Display for Format<T> {
164    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")))]
170mod test {
171    use super::Format;
172    use ansi_term::ANSIString;
173    use ansi_term::Colour::{Green, Red, Yellow};
174
175    #[test]
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}