1use std::default::Default;
4use std::env;
5use std::io::{self, IsTerminal};
6use std::sync::atomic::{AtomicBool, Ordering};
7
8#[allow(clippy::result_unit_err)]
29#[cfg(windows)]
30pub fn set_virtual_terminal(use_virtual: bool) -> Result<(), ()> {
31 use windows_sys::Win32::System::Console::{
32 GetConsoleMode, GetStdHandle, SetConsoleMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING,
33 STD_OUTPUT_HANDLE,
34 };
35
36 unsafe {
37 let handle = GetStdHandle(STD_OUTPUT_HANDLE);
38 let mut original_mode = 0;
39 GetConsoleMode(handle, &mut original_mode);
40
41 let enabled = original_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING
42 == ENABLE_VIRTUAL_TERMINAL_PROCESSING;
43
44 match (use_virtual, enabled) {
45 (true, false) => {
47 SetConsoleMode(handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING | original_mode)
48 }
49 (false, true) => {
51 SetConsoleMode(handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING ^ original_mode)
52 }
53 _ => 0,
54 };
55 }
56
57 Ok(())
58}
59
60pub struct ShouldColorize {
62 clicolor: bool,
63 clicolor_force: Option<bool>,
64 has_manual_override: AtomicBool,
66 manual_override: AtomicBool,
67}
68
69pub fn set_override(override_colorize: bool) {
72 SHOULD_COLORIZE.set_override(override_colorize)
73}
74
75pub fn unset_override() {
78 SHOULD_COLORIZE.unset_override()
79}
80
81lazy_static! {
82pub static ref SHOULD_COLORIZE: ShouldColorize = ShouldColorize::from_env();
84}
85
86impl Default for ShouldColorize {
87 fn default() -> ShouldColorize {
88 ShouldColorize {
89 clicolor: true,
90 clicolor_force: None,
91 has_manual_override: AtomicBool::new(false),
92 manual_override: AtomicBool::new(false),
93 }
94 }
95}
96
97impl ShouldColorize {
98 pub fn from_env() -> Self {
103 ShouldColorize {
104 clicolor: ShouldColorize::normalize_env(env::var("CLICOLOR")).unwrap_or(true)
105 && io::stdout().is_terminal(),
106 clicolor_force: ShouldColorize::resolve_clicolor_force(
107 env::var("NO_COLOR"),
108 env::var("CLICOLOR_FORCE"),
109 ),
110 ..ShouldColorize::default()
111 }
112 }
113
114 pub fn should_colorize(&self) -> bool {
116 if self.has_manual_override.load(Ordering::Relaxed) {
117 return self.manual_override.load(Ordering::Relaxed);
118 }
119
120 if let Some(forced_value) = self.clicolor_force {
121 return forced_value;
122 }
123
124 self.clicolor
125 }
126
127 pub fn set_override(&self, override_colorize: bool) {
129 self.has_manual_override.store(true, Ordering::Relaxed);
130 self.manual_override
131 .store(override_colorize, Ordering::Relaxed);
132 }
133
134 pub fn unset_override(&self) {
136 self.has_manual_override.store(false, Ordering::Relaxed);
137 }
138
139 fn normalize_env(env_res: Result<String, env::VarError>) -> Option<bool> {
142 match env_res {
143 Ok(string) => Some(string != "0"),
144 Err(_) => None,
145 }
146 }
147
148 fn resolve_clicolor_force(
149 no_color: Result<String, env::VarError>,
150 clicolor_force: Result<String, env::VarError>,
151 ) -> Option<bool> {
152 if ShouldColorize::normalize_env(clicolor_force) == Some(true) {
153 Some(true)
154 } else if ShouldColorize::normalize_env(no_color).is_some() {
155 Some(false)
156 } else {
157 None
158 }
159 }
160}
161
162#[cfg(test)]
163mod specs {
164 use super::*;
165 use rspec;
166 use rspec::context::*;
167 use std::env;
168
169 #[test]
170 fn clicolor_behavior() {
171 use std::io;
172
173 let stdout = &mut io::stdout();
174 let mut formatter = rspec::formatter::Simple::new(stdout);
175 let mut runner = describe("ShouldColorize", |ctx| {
176 ctx.describe("::normalize_env", |ctx| {
177 ctx.it("should return None if error", || {
178 assert_eq!(
179 None,
180 ShouldColorize::normalize_env(Err(env::VarError::NotPresent))
181 );
182 assert_eq!(
183 None,
184 ShouldColorize::normalize_env(Err(env::VarError::NotUnicode("".into())))
185 )
186 });
187
188 ctx.it("should return Some(true) if != 0", || {
189 Some(true) == ShouldColorize::normalize_env(Ok(String::from("1")))
190 });
191
192 ctx.it("should return Some(false) if == 0", || {
193 Some(false) == ShouldColorize::normalize_env(Ok(String::from("0")))
194 });
195 });
196
197 ctx.describe("::resolve_clicolor_force", |ctx| {
198 ctx.it(
199 "should return None if NO_COLOR is not set and CLICOLOR_FORCE is not set or set to 0",
200 || {
201 assert_eq!(
202 None,
203 ShouldColorize::resolve_clicolor_force(
204 Err(env::VarError::NotPresent),
205 Err(env::VarError::NotPresent)
206 )
207 );
208 assert_eq!(
209 None,
210 ShouldColorize::resolve_clicolor_force(
211 Err(env::VarError::NotPresent),
212 Ok(String::from("0")),
213 )
214 );
215 },
216 );
217
218 ctx.it(
219 "should return Some(false) if NO_COLOR is set and CLICOLOR_FORCE is not enabled",
220 || {
221 assert_eq!(
222 Some(false),
223 ShouldColorize::resolve_clicolor_force(
224 Ok(String::from("0")),
225 Err(env::VarError::NotPresent)
226 )
227 );
228 assert_eq!(
229 Some(false),
230 ShouldColorize::resolve_clicolor_force(
231 Ok(String::from("1")),
232 Err(env::VarError::NotPresent)
233 )
234 );
235 assert_eq!(
236 Some(false),
237 ShouldColorize::resolve_clicolor_force(
238 Ok(String::from("1")),
239 Ok(String::from("0")),
240 )
241 );
242 },
243 );
244
245 ctx.it(
246 "should prioritize CLICOLOR_FORCE over NO_COLOR if CLICOLOR_FORCE is set to non-zero value",
247 || {
248 assert_eq!(
249 Some(true),
250 ShouldColorize::resolve_clicolor_force(
251 Ok(String::from("1")),
252 Ok(String::from("1")),
253 )
254 );
255 assert_eq!(
256 Some(false),
257 ShouldColorize::resolve_clicolor_force(
258 Ok(String::from("1")),
259 Ok(String::from("0")),
260 )
261 );
262 assert_eq!(
263 Some(true),
264 ShouldColorize::resolve_clicolor_force(
265 Err(env::VarError::NotPresent),
266 Ok(String::from("1")),
267 )
268 );
269 },
270 );
271 });
272
273 ctx.describe("constructors", |ctx| {
274 ctx.it("should have a default constructor", || {
275 ShouldColorize::default();
276 });
277
278 ctx.it("should have an environment constructor", || {
279 ShouldColorize::from_env();
280 });
281 });
282
283 ctx.describe("when only changing clicolors", |ctx| {
284 ctx.it("clicolor == false means no colors", || {
285 let colorize_control = ShouldColorize {
286 clicolor: false,
287 ..ShouldColorize::default()
288 };
289 false == colorize_control.should_colorize()
290 });
291
292 ctx.it("clicolor == true means colors !", || {
293 let colorize_control = ShouldColorize {
294 clicolor: true,
295 ..ShouldColorize::default()
296 };
297 true == colorize_control.should_colorize()
298 });
299
300 ctx.it("unset clicolors implies true", || {
301 true == ShouldColorize::default().should_colorize()
302 });
303 });
304
305 ctx.describe("when using clicolor_force", |ctx| {
306 ctx.it(
307 "clicolor_force should force to true no matter clicolor",
308 || {
309 let colorize_control = ShouldColorize {
310 clicolor: false,
311 clicolor_force: Some(true),
312 ..ShouldColorize::default()
313 };
314
315 true == colorize_control.should_colorize()
316 },
317 );
318
319 ctx.it(
320 "clicolor_force should force to false no matter clicolor",
321 || {
322 let colorize_control = ShouldColorize {
323 clicolor: true,
324 clicolor_force: Some(false),
325 ..ShouldColorize::default()
326 };
327
328 false == colorize_control.should_colorize()
329 },
330 );
331 });
332
333 ctx.describe("using a manual override", |ctx| {
334 ctx.it("shoud colorize if manual_override is true, but clicolor is false and clicolor_force also false", || {
335 let colorize_control = ShouldColorize {
336 clicolor: false,
337 clicolor_force: None,
338 has_manual_override: AtomicBool::new(true),
339 manual_override: AtomicBool::new(true),
340 .. ShouldColorize::default()
341 };
342
343 true == colorize_control.should_colorize()
344 });
345
346 ctx.it("should not colorize if manual_override is false, but clicolor is true or clicolor_force is true", || {
347 let colorize_control = ShouldColorize {
348 clicolor: true,
349 clicolor_force: Some(true),
350 has_manual_override: AtomicBool::new(true),
351 manual_override: AtomicBool::new(false),
352 .. ShouldColorize::default()
353 };
354
355 false == colorize_control.should_colorize()
356 })
357 });
358
359 ctx.describe("::set_override", |ctx| {
360 ctx.it("should exists", || {
361 let colorize_control = ShouldColorize::default();
362 colorize_control.set_override(true);
363 });
364
365 ctx.it("set the manual_override property", || {
366 let colorize_control = ShouldColorize::default();
367 colorize_control.set_override(true);
368 {
369 assert_eq!(
370 true,
371 colorize_control.has_manual_override.load(Ordering::Relaxed)
372 );
373 let val = colorize_control.manual_override.load(Ordering::Relaxed);
374 assert_eq!(true, val);
375 }
376 colorize_control.set_override(false);
377 {
378 assert_eq!(
379 true,
380 colorize_control.has_manual_override.load(Ordering::Relaxed)
381 );
382 let val = colorize_control.manual_override.load(Ordering::Relaxed);
383 assert_eq!(false, val);
384 }
385 });
386 });
387
388 ctx.describe("::unset_override", |ctx| {
389 ctx.it("should exists", || {
390 let colorize_control = ShouldColorize::default();
391 colorize_control.unset_override();
392 });
393
394 ctx.it("unset the manual_override property", || {
395 let colorize_control = ShouldColorize::default();
396 colorize_control.set_override(true);
397 colorize_control.unset_override();
398 assert_eq!(
399 false,
400 colorize_control.has_manual_override.load(Ordering::Relaxed)
401 );
402 });
403 });
404 });
405 runner.add_event_handler(&mut formatter);
406 runner.run().unwrap();
407 }
408}