Skip to content

Commit

Permalink
refactor: COLOR_MODE to OnceCell
Browse files Browse the repository at this point in the history
  • Loading branch information
bconn98 committed Apr 2, 2024
1 parent f688e38 commit e6f0127
Showing 1 changed file with 173 additions and 35 deletions.
208 changes: 173 additions & 35 deletions src/encode/writer/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,37 @@
//!
//! Requires the `console_writer` feature.
use std::{fmt, io};
use std::{env, fmt, io};

use crate::encode::{self, Style};
use once_cell::sync::Lazy;

static COLOR_MODE: Lazy<ColorMode> = Lazy::new(|| {
let no_color = std::env::var("NO_COLOR")
.map(|var| var != "0")
.unwrap_or(false);
let clicolor_force = std::env::var("CLICOLOR_FORCE")
.map(|var| var != "0")
.unwrap_or(false);
use once_cell::sync::OnceCell;

static COLOR_MODE: OnceCell<ColorMode> = OnceCell::new();

fn set_color_mode(
no_color: Result<String, env::VarError>,
clicolor_force: Result<String, env::VarError>,
clicolor: Result<String, env::VarError>,
) -> ColorMode {
let no_color = no_color.map(|var| var != "0").unwrap_or(false);
let clicolor_force = clicolor_force.map(|var| var != "0").unwrap_or(false);

if no_color {
ColorMode::Never
} else if clicolor_force {
ColorMode::Always
} else {
let clicolor = std::env::var("CLICOLOR")
.map(|var| var != "0")
.unwrap_or(true);
let clicolor = clicolor.map(|var| var != "0").unwrap_or(true);
if clicolor {
ColorMode::Auto
} else {
ColorMode::Never
}
}
});
}

/// The color output mode for a `ConsoleAppender`
#[derive(Clone, Copy, Default)]
#[derive(Clone, Copy, Default, Debug, PartialEq)]
pub enum ColorMode {
/// Print color only if the output is recognized as a console
#[default]
Expand Down Expand Up @@ -121,14 +122,14 @@ impl<'a> encode::Write for ConsoleWriterLock<'a> {

#[cfg(unix)]
mod imp {
use std::{fmt, io};
use std::{env, fmt, io};

use crate::{
encode::{
self,
writer::{
ansi::AnsiWriter,
console::{ColorMode, COLOR_MODE},
console::{set_color_mode, ColorMode, COLOR_MODE},
},
Style,
},
Expand All @@ -140,7 +141,13 @@ mod imp {
impl Writer {
pub fn stdout() -> Option<Writer> {
let writer = || Writer(AnsiWriter(StdWriter::stdout()));
match *COLOR_MODE {
let color_mode_init = {
let no_color = env::var("NO_COLOR");
let clicolor_force = env::var("CLICOLOR_FORCE");
let clicolor = env::var("CLICOLOR");
set_color_mode(no_color, clicolor_force, clicolor)
};
match COLOR_MODE.get_or_init(|| color_mode_init) {
ColorMode::Auto => {
if unsafe { libc::isatty(libc::STDOUT_FILENO) } != 1 {
None
Expand All @@ -155,7 +162,13 @@ mod imp {

pub fn stderr() -> Option<Writer> {
let writer = || Writer(AnsiWriter(StdWriter::stderr()));
match *COLOR_MODE {
let color_mode_init = {
let no_color = env::var("NO_COLOR");
let clicolor_force = env::var("CLICOLOR_FORCE");
let clicolor = env::var("CLICOLOR");
set_color_mode(no_color, clicolor_force, clicolor)
};
match COLOR_MODE.get_or_init(|| color_mode_init) {
ColorMode::Auto => {
if unsafe { libc::isatty(libc::STDERR_FILENO) } != 1 {
None
Expand Down Expand Up @@ -227,7 +240,7 @@ mod imp {
#[cfg(windows)]
mod imp {
use std::{
fmt,
env, fmt,
io::{self, Write},
mem,
};
Expand All @@ -239,7 +252,7 @@ mod imp {
use crate::{
encode::{
self,
writer::console::{ColorMode, COLOR_MODE},
writer::console::{set_color_mode, ColorMode, COLOR_MODE},
Color, Style,
},
priv_io::{StdWriter, StdWriterLock},
Expand Down Expand Up @@ -335,7 +348,13 @@ mod imp {
inner: StdWriter::stdout(),
};

match *COLOR_MODE {
let color_mode_init = {
let no_color = env::var("NO_COLOR");
let clicolor_force = env::var("CLICOLOR_FORCE");
let clicolor = env::var("CLICOLOR");
set_color_mode(no_color, clicolor_force, clicolor)
};
match COLOR_MODE.get_or_init(|| color_mode_init) {
ColorMode::Auto | ColorMode::Always => Some(writer),
ColorMode::Never => None,
}
Expand All @@ -362,7 +381,13 @@ mod imp {
inner: StdWriter::stdout(),
};

match *COLOR_MODE {
let color_mode_init = {
let no_color = env::var("NO_COLOR");
let clicolor_force = env::var("CLICOLOR_FORCE");
let clicolor = env::var("CLICOLOR");
set_color_mode(no_color, clicolor_force, clicolor)
};
match COLOR_MODE.get_or_init(|| color_mode_init) {
ColorMode::Auto | ColorMode::Always => Some(writer),
ColorMode::Never => None,
}
Expand Down Expand Up @@ -435,32 +460,145 @@ mod imp {

#[cfg(test)]
mod test {
use std::io::Write;

use super::*;
use crate::encode::{Color, Style, Write as EncodeWrite};
use std::{env::VarError, io::Write};

// Unable to test the non locked Console as by definition, the unlocked
// console results in race conditions. Codecov tooling does not seem to
// see this test as coverage of the ConsoleWritterLock or WriterLock
// class, however, it should completely cover either.
#[test]
fn basic() {
fn test_stdout_console_writer_lock() {
let w = match ConsoleWriter::stdout() {
Some(w) => w,
None => return,
};
let mut w = w.lock();

w.write_all(b"normal ").unwrap();
w.write(b"normal ").unwrap();
w.set_style(
Style::new()
.text(Color::Red)
.background(Color::Blue)
.intense(true),
)
.unwrap();
Style::new()
.text(Color::Red)
.background(Color::Blue)
.intense(true),
)
.unwrap();
w.write_all(b"styled").unwrap();
w.set_style(Style::new().text(Color::Green)).unwrap();
w.set_style(&Style::new().text(Color::Green).intense(false))
.unwrap();
w.write_all(b" styled2").unwrap();
w.set_style(&Style::new()).unwrap();
w.write_all(b" normal\n").unwrap();
w.write_fmt(format_args!(" {} \n", "normal")).unwrap();
w.flush().unwrap();
}

#[test]
fn test_color_mode_default() {
let no_color = Err(VarError::NotPresent);
let clicolor_force = Err(VarError::NotPresent);
let clicolor = Err(VarError::NotPresent);

let color_mode: OnceCell<ColorMode> = OnceCell::new();
assert_eq!(
color_mode.get_or_init(|| set_color_mode(no_color, clicolor_force, clicolor)),
&ColorMode::Auto
);
}

// Note that NO_COLOR has priority over all other fields
#[test]
fn test_no_color() {
let no_color = Ok("1".to_owned());
let clicolor_force = Err(VarError::NotPresent);
let clicolor = Err(VarError::NotPresent);

let mut color_mode: OnceCell<ColorMode> = OnceCell::new();
assert_eq!(
color_mode.get_or_init(|| set_color_mode(no_color, clicolor_force, clicolor)),
&ColorMode::Never
);

let no_color = Ok("1".to_owned());
let clicolor_force = Ok("1".to_owned());
let clicolor = Ok("1".to_owned());

let _ = color_mode.take(); // Clear the owned value
assert_eq!(
color_mode.get_or_init(|| set_color_mode(no_color, clicolor_force, clicolor)),
&ColorMode::Never
);
}

#[test]
fn test_cli_force() {
// CLICOLOR_FORCE is the only set field
let no_color = Err(VarError::NotPresent);
let clicolor_force = Ok("1".to_owned());
let clicolor = Err(VarError::NotPresent);

let mut color_mode: OnceCell<ColorMode> = OnceCell::new();
assert_eq!(
color_mode.get_or_init(|| set_color_mode(no_color, clicolor_force, clicolor)),
&ColorMode::Always
);

// Although NO_COLOR has priority, when set to 0 next in line
// is CLICOLOR_FORCE which maintains precedence over clicolor
// regardless of how it's set. Attempt both settings below
let no_color = Ok("0".to_owned());
let clicolor_force = Ok("1".to_owned());
let clicolor = Ok("1".to_owned());

let _ = color_mode.take(); // Clear the owned value
assert_eq!(
color_mode.get_or_init(|| set_color_mode(no_color, clicolor_force, clicolor)),
&ColorMode::Always
);

let no_color = Ok("0".to_owned());
let clicolor_force = Ok("1".to_owned());
let clicolor = Ok("0".to_owned());

let _ = color_mode.take(); // Clear the owned value
assert_eq!(
color_mode.get_or_init(|| set_color_mode(no_color, clicolor_force, clicolor)),
&ColorMode::Always
);
}

#[test]
fn test_cli_on() {
// CLICOLOR is the only set field
let no_color = Err(VarError::NotPresent);
let clicolor_force = Err(VarError::NotPresent);
let clicolor = Ok("1".to_owned());

let mut color_mode: OnceCell<ColorMode> = OnceCell::new();
assert_eq!(
color_mode.get_or_init(|| set_color_mode(no_color, clicolor_force, clicolor)),
&ColorMode::Auto
);

let no_color = Err(VarError::NotPresent);
let clicolor_force = Err(VarError::NotPresent);
let clicolor = Ok("0".to_owned());

let _ = color_mode.take(); // Clear the owned value
assert_eq!(
color_mode.get_or_init(|| set_color_mode(no_color, clicolor_force, clicolor)),
&ColorMode::Never
);

// CLICOLOR_FORCE is disabled
let no_color = Err(VarError::NotPresent);
let clicolor_force = Ok("0".to_owned());
let clicolor = Ok("1".to_owned());

let _ = color_mode.take(); // Clear the owned value
assert_eq!(
color_mode.get_or_init(|| set_color_mode(no_color, clicolor_force, clicolor)),
&ColorMode::Auto
);
}
}

0 comments on commit e6f0127

Please sign in to comment.