use crate::config::partial::OutMode;
use crate::config::EnvSnapshot;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Style {
Plain,
Pretty { ascii: bool },
}
#[derive(Clone, Copy)]
pub enum Sgr {
Dim,
Bold,
Yellow,
Green,
Red,
}
impl Sgr {
fn open(self) -> &'static str {
match self {
Sgr::Dim => "\x1b[2m",
Sgr::Bold => "\x1b[1m",
Sgr::Yellow => "\x1b[33m",
Sgr::Green => "\x1b[32m",
Sgr::Red => "\x1b[31m",
}
}
}
#[derive(Clone, Copy)]
pub enum Glyph {
Tool,
Footer,
Error,
}
impl Glyph {
fn pair(self) -> (&'static str, &'static str) {
match self {
Glyph::Tool => ("⚙", "*"),
Glyph::Footer => ("✓", "+"),
Glyph::Error => ("✗", "x"),
}
}
}
impl Style {
pub fn resolve(stdout_tty: bool, output: OutMode, env: &EnvSnapshot) -> Style {
let term_ok = matches!(env.get("TERM"), Some(t) if t != "dumb");
let pretty =
stdout_tty && output == OutMode::Text && env.get("NO_COLOR").is_none() && term_ok;
if pretty {
Style::Pretty {
ascii: !utf8_locale(env),
}
} else {
Style::Plain
}
}
pub fn is_pretty(self) -> bool {
matches!(self, Style::Pretty { .. })
}
pub fn paint(self, sgr: Sgr, text: &str) -> String {
match self {
Style::Plain => text.to_owned(),
Style::Pretty { .. } => format!("{}{text}\x1b[0m", sgr.open()),
}
}
pub fn glyph(self, g: Glyph) -> &'static str {
let (utf8, ascii) = g.pair();
match self {
Style::Pretty { ascii: false } => utf8,
_ => ascii,
}
}
}
fn utf8_locale(env: &EnvSnapshot) -> bool {
let locale = env
.get("LC_ALL")
.or_else(|| env.get("LC_CTYPE"))
.or_else(|| env.get("LANG"))
.unwrap_or("");
let lower = locale.to_ascii_lowercase();
lower.contains("utf-8") || lower.contains("utf8")
}