use std::io::IsTerminal;
fn env_decision() -> Option<bool> {
if std::env::var_os("NO_COLOR").is_some() {
return Some(false);
}
if let Some(v) = std::env::var_os("CLICOLOR_FORCE") {
if !v.is_empty() && v != "0" {
return Some(true);
}
}
None
}
#[must_use]
pub fn use_color_stdout() -> bool {
env_decision().unwrap_or_else(|| std::io::stdout().is_terminal())
}
#[must_use]
pub fn use_color_stderr() -> bool {
env_decision().unwrap_or_else(|| std::io::stderr().is_terminal())
}
pub struct Style {
enabled: bool,
}
impl Style {
#[must_use]
pub fn detect_stderr() -> Self {
Self {
enabled: use_color_stderr(),
}
}
#[must_use]
pub fn detect_stdout() -> Self {
Self {
enabled: use_color_stdout(),
}
}
#[cfg(test)]
#[must_use]
pub fn always() -> Self {
Self { enabled: true }
}
#[cfg(test)]
#[must_use]
pub fn never() -> Self {
Self { enabled: false }
}
#[allow(dead_code)]
#[must_use]
pub fn is_enabled(&self) -> bool {
self.enabled
}
fn wrap(&self, codes: &str, text: &str) -> String {
if self.enabled {
format!("\x1b[{codes}m{text}\x1b[0m")
} else {
text.to_string()
}
}
#[must_use]
pub fn step(&self, text: &str) -> String {
self.wrap("1;36", text)
}
#[must_use]
pub fn ok(&self, text: &str) -> String {
self.wrap("1;32", text)
}
#[must_use]
pub fn warn(&self, text: &str) -> String {
self.wrap("1;33", text)
}
#[must_use]
pub fn err(&self, text: &str) -> String {
self.wrap("1;31", text)
}
#[must_use]
pub fn dim(&self, text: &str) -> String {
self.wrap("2", text)
}
#[allow(dead_code)]
#[must_use]
pub fn bold(&self, text: &str) -> String {
self.wrap("1", text)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn always_palette_wraps_with_sgr_codes() {
let s = Style::always();
assert_eq!(s.step("Checking"), "\x1b[1;36mChecking\x1b[0m");
assert_eq!(s.ok("done."), "\x1b[1;32mdone.\x1b[0m");
assert_eq!(s.warn("warning"), "\x1b[1;33mwarning\x1b[0m");
assert_eq!(s.err("oops"), "\x1b[1;31moops\x1b[0m");
assert_eq!(s.dim("hint"), "\x1b[2mhint\x1b[0m");
assert_eq!(s.bold("strong"), "\x1b[1mstrong\x1b[0m");
}
#[test]
fn never_palette_returns_plain_text() {
let s = Style::never();
assert_eq!(s.step("Checking"), "Checking");
assert_eq!(s.ok("done."), "done.");
assert_eq!(s.warn("warning"), "warning");
assert_eq!(s.err("oops"), "oops");
assert_eq!(s.dim("hint"), "hint");
assert_eq!(s.bold("strong"), "strong");
}
#[test]
fn is_enabled_reflects_constructor() {
assert!(Style::always().is_enabled());
assert!(!Style::never().is_enabled());
}
}