use std::io::IsTerminal;
use std::sync::LazyLock;
use crossterm::style::{Attribute, Color, Stylize};
static ENABLED: LazyLock<bool> =
LazyLock::new(|| std::env::var_os("NO_COLOR").is_none() && std::io::stdout().is_terminal());
fn enabled() -> bool {
*ENABLED
}
fn paint(text: &str, color: Color) -> String {
if enabled() {
text.with(color).to_string()
} else {
text.to_string()
}
}
pub fn green(s: &str) -> String {
paint(s, Color::Green)
}
pub fn red(s: &str) -> String {
paint(s, Color::Red)
}
pub fn yellow(s: &str) -> String {
paint(s, Color::Yellow)
}
pub fn cyan(s: &str) -> String {
paint(s, Color::Cyan)
}
pub fn blue(s: &str) -> String {
paint(s, Color::Blue)
}
pub fn dim(s: &str) -> String {
paint(s, Color::DarkGrey)
}
pub fn bold(s: &str) -> String {
if enabled() {
s.attribute(Attribute::Bold).to_string()
} else {
s.to_string()
}
}
pub fn success(msg: &str) -> String {
format!("{} {}", green("✓"), msg)
}
pub fn error(msg: &str) -> String {
format!("{} {}", red("✗"), msg)
}
pub fn warning(msg: &str) -> String {
format!("{} {}", yellow("!"), msg)
}
pub fn info(msg: &str) -> String {
format!("{} {}", blue("•"), msg)
}
pub fn hint(msg: &str) -> String {
format!(" {} {}", dim("→"), msg)
}
pub fn section(title: &str) -> String {
let padding = 50usize.saturating_sub(title.len() + 3);
let bar = "─".repeat(padding);
if enabled() {
format!("{} {} {}", dim("─"), bold(title), dim(&bar))
} else {
format!("─ {title} {bar}")
}
}
pub fn path(p: impl AsRef<std::path::Path>) -> String {
cyan(&p.as_ref().display().to_string())
}
use clap::builder::{Styles, styling::AnsiColor};
pub const CLAP_STYLES: Styles = Styles::styled()
.header(AnsiColor::BrightGreen.on_default().bold())
.usage(AnsiColor::BrightGreen.on_default().bold())
.literal(AnsiColor::BrightCyan.on_default().bold())
.placeholder(AnsiColor::Cyan.on_default())
.error(AnsiColor::BrightRed.on_default().bold())
.valid(AnsiColor::Green.on_default())
.invalid(AnsiColor::Yellow.on_default());