diff --git a/src/style/types/colored.rs b/src/style/types/colored.rs index a1a91b5..2830d09 100644 --- a/src/style/types/colored.rs +++ b/src/style/types/colored.rs @@ -1,4 +1,6 @@ +use parking_lot::Once; use std::fmt::{self, Formatter}; +use std::sync::atomic::{AtomicBool, Ordering}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -21,6 +23,9 @@ pub enum Colored { UnderlineColor(Color), } +static ANSI_COLOR_DISABLED: AtomicBool = AtomicBool::new(false); +static INITIALIZER: Once = Once::new(); + impl Colored { /// Parse an ANSI foreground or background color. /// This is the string that would appear within an `ESC [ m` escape sequence, as found in @@ -64,12 +69,39 @@ impl Colored { Some(output) } + + /// Checks whether ansi color sequences are disabled by setting of NO_COLOR + /// in environment as per https://no-color.org/ + pub fn ansi_color_disabled() -> bool { + !std::env::var("NO_COLOR") + .unwrap_or("".to_string()) + .is_empty() + } + + pub fn ansi_color_disabled_memoized() -> bool { + INITIALIZER.call_once(|| { + ANSI_COLOR_DISABLED.store(Self::ansi_color_disabled(), Ordering::SeqCst); + }); + + ANSI_COLOR_DISABLED.load(Ordering::SeqCst) + } + + #[cfg(test)] + pub fn set_ansi_color_disabled(val: bool) { + // Force the one-time initializer to run. + _ = Self::ansi_color_disabled_memoized(); + ANSI_COLOR_DISABLED.store(val, Ordering::SeqCst); + } } impl fmt::Display for Colored { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let color; + if Self::ansi_color_disabled_memoized() { + return Ok(()); + } + match *self { Colored::ForegroundColor(new_color) => { if new_color == Color::Reset { @@ -125,40 +157,47 @@ impl fmt::Display for Colored { mod tests { use crate::style::{Color, Colored}; + fn check_format_color(colored: Colored, expected: &str) { + Colored::set_ansi_color_disabled(true); + assert_eq!(colored.to_string(), ""); + Colored::set_ansi_color_disabled(false); + assert_eq!(colored.to_string(), expected); + } + #[test] fn test_format_fg_color() { let colored = Colored::ForegroundColor(Color::Red); - assert_eq!(colored.to_string(), "38;5;9"); + check_format_color(colored, "38;5;9"); } #[test] fn test_format_bg_color() { let colored = Colored::BackgroundColor(Color::Red); - assert_eq!(colored.to_string(), "48;5;9"); + check_format_color(colored, "48;5;9"); } #[test] fn test_format_reset_fg_color() { let colored = Colored::ForegroundColor(Color::Reset); - assert_eq!(colored.to_string(), "39"); + check_format_color(colored, "39"); } #[test] fn test_format_reset_bg_color() { let colored = Colored::BackgroundColor(Color::Reset); - assert_eq!(colored.to_string(), "49"); + check_format_color(colored, "49"); } #[test] fn test_format_fg_rgb_color() { let colored = Colored::BackgroundColor(Color::Rgb { r: 1, g: 2, b: 3 }); - assert_eq!(colored.to_string(), "48;2;1;2;3"); + check_format_color(colored, "48;2;1;2;3"); } #[test] fn test_format_fg_ansi_color() { let colored = Colored::ForegroundColor(Color::AnsiValue(255)); - assert_eq!(colored.to_string(), "38;5;255"); + check_format_color(colored, "38;5;255"); } #[test] @@ -267,4 +306,16 @@ mod tests { test("48;2;0;2;25;"); test("48;2;0;2;25;3"); } + + #[test] + fn test_no_color() { + std::env::set_var("NO_COLOR", "1"); + assert!(Colored::ansi_color_disabled()); + std::env::set_var("NO_COLOR", "XXX"); + assert!(Colored::ansi_color_disabled()); + std::env::set_var("NO_COLOR", ""); + assert!(!Colored::ansi_color_disabled()); + std::env::remove_var("NO_COLOR"); + assert!(!Colored::ansi_color_disabled()); + } }