Skip to content

Commit

Permalink
Add windows and *nix based env vars to control color output in logs
Browse files Browse the repository at this point in the history
  • Loading branch information
bconn98 committed Dec 22, 2023
1 parent 58b92c8 commit 621fd51
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 17 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ jobs:
test:
# https://github.com/xd009642/tarpaulin#github-actions
name: coverage
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
container:
image: xd009642/tarpaulin:develop-nightly
options: --security-opt seccomp=unconfined
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ parking_lot = { version = "0.12.0", optional = true }
thiserror = "1.0.15"
anyhow = "1.0.28"
derivative = "2.2"
once_cell = "1.17.1"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", optional = true, features = ["handleapi", "minwindef", "processenv", "winbase", "wincon"] }
Expand Down
101 changes: 86 additions & 15 deletions src/encode/writer/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,42 @@
use std::{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);
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);
if clicolor {
ColorMode::Auto
} else {
ColorMode::Never
}
}
});

/// The color output mode for a `ConsoleAppender`
#[derive(Clone, Copy, Default)]
pub enum ColorMode {
/// Print color only if the output is recognized as a console
#[default]
Auto,
/// Force color output
Always,
/// Never print color
Never,
}

/// An `encode::Write`r that outputs to a console.
pub struct ConsoleWriter(imp::Writer);
Expand Down Expand Up @@ -88,27 +124,48 @@ mod imp {
use std::{fmt, io};

use crate::{
encode::{self, writer::ansi::AnsiWriter, Style},
encode::{
self,
writer::{
ansi::AnsiWriter,
console::{ColorMode, COLOR_MODE},
},
Style,
},
priv_io::{StdWriter, StdWriterLock},
};

pub struct Writer(AnsiWriter<StdWriter>);

impl Writer {
pub fn stdout() -> Option<Writer> {
if unsafe { libc::isatty(libc::STDOUT_FILENO) } != 1 {
return None;
let writer = || Writer(AnsiWriter(StdWriter::stdout()));
match *COLOR_MODE {
ColorMode::Auto => {
if unsafe { libc::isatty(libc::STDOUT_FILENO) } != 1 {
None
} else {
Some(writer())
}
}
ColorMode::Always => Some(writer()),
ColorMode::Never => None,
}

Some(Writer(AnsiWriter(StdWriter::stdout())))
}

pub fn stderr() -> Option<Writer> {
if unsafe { libc::isatty(libc::STDERR_FILENO) } != 1 {
return None;
let writer = || Writer(AnsiWriter(StdWriter::stderr()));
match *COLOR_MODE {
ColorMode::Auto => {
if unsafe { libc::isatty(libc::STDERR_FILENO) } != 1 {
None
} else {
Some(writer())
}
}
ColorMode::Always => Some(writer()),
ColorMode::Never => None,
}

Some(Writer(AnsiWriter(StdWriter::stderr())))
}

pub fn lock(&self) -> WriterLock {
Expand Down Expand Up @@ -180,7 +237,11 @@ mod imp {
};

use crate::{
encode::{self, Color, Style},
encode::{
self,
writer::console::{ColorMode, COLOR_MODE},
Color, Style,
},
priv_io::{StdWriter, StdWriterLock},
};

Expand Down Expand Up @@ -266,13 +327,18 @@ mod imp {
return None;
}

Some(Writer {
let writer = Writer {
console: RawConsole {
handle,
defaults: info.wAttributes,
},
inner: StdWriter::stdout(),
})
};

match *COLOR_MODE {
ColorMode::Auto | ColorMode::Always => Some(writer),
ColorMode::Never => None,
}
}
}

Expand All @@ -288,13 +354,18 @@ mod imp {
return None;
}

Some(Writer {
let writer = Writer {
console: RawConsole {
handle,
defaults: info.wAttributes,
},
inner: StdWriter::stderr(),
})
inner: StdWriter::stdout(),
};

match *COLOR_MODE {
ColorMode::Auto | ColorMode::Always => Some(writer),
ColorMode::Never => None,
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ authors = ["Steven Fackler <sfackler@gmail.com>"]

[dependencies]
log = "0.4.0"
log4rs = { path = ".." }
log4rs = { path = "../.." }
24 changes: 24 additions & 0 deletions tests/color_control.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use std::process::Command;

fn execute_test(env_key: &str, env_val: &str) {
let mut child_proc = Command::new("cargo")
.args(&["run", "--example", "compile_time_config"])
.env(env_key, env_val)
.spawn()
.expect("Cargo command failed to start");

let ecode = child_proc.wait().expect("failed to wait on child");

assert!(ecode.success());
}

// Maintaining as a single test to avoid blocking calls to the package cache
#[test]
fn test_no_color() {
let keys = vec!["NO_COLOR", "CLICOLOR_FORCE", "CLICOLOR"];

for key in keys {
execute_test(key, "1");
execute_test(key, "0");
}
}

0 comments on commit 621fd51

Please sign in to comment.