use colored::{ColoredString, Colorize};
use is_terminal::IsTerminal;
use std::env;
use std::io::stdout;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ColorMode {
#[default]
Auto, Always, Never, }
impl std::str::FromStr for ColorMode {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"auto" => Ok(ColorMode::Auto),
"always" => Ok(ColorMode::Always),
"never" => Ok(ColorMode::Never),
_ => Err(format!(
"Invalid color mode: '{}'. Valid options: auto, always, never",
s
)),
}
}
}
#[derive(Clone)]
pub struct ColorHelper {
mode: ColorMode,
stdout_is_terminal: bool,
no_color: bool,
}
impl ColorHelper {
pub fn new(mode: ColorMode) -> Self {
Self {
mode,
stdout_is_terminal: stdout().is_terminal(),
no_color: env::var("NO_COLOR").is_ok()
&& !env::var("NO_COLOR").unwrap_or_default().is_empty(),
}
}
pub fn should_color_stdout(&self) -> bool {
self.should_use_colors(self.stdout_is_terminal)
}
fn should_use_colors(&self, is_terminal: bool) -> bool {
if self.no_color {
return false;
}
match self.mode {
ColorMode::Never => false,
ColorMode::Always => true,
ColorMode::Auto => is_terminal,
}
}
pub fn red(&self, text: &str) -> ColoredString {
if self.should_color_stdout() {
text.red()
} else {
text.normal()
}
}
pub fn green(&self, text: &str) -> ColoredString {
if self.should_color_stdout() {
text.green()
} else {
text.normal()
}
}
pub fn blue(&self, text: &str) -> ColoredString {
if self.should_color_stdout() {
text.blue()
} else {
text.normal()
}
}
pub fn yellow(&self, text: &str) -> ColoredString {
if self.should_color_stdout() {
text.yellow()
} else {
text.normal()
}
}
pub fn cyan(&self, text: &str) -> ColoredString {
if self.should_color_stdout() {
text.cyan()
} else {
text.normal()
}
}
pub fn bold(&self, text: &str) -> ColoredString {
if self.should_color_stdout() {
text.bold()
} else {
text.normal()
}
}
pub fn dimmed(&self, text: &str) -> ColoredString {
if self.should_color_stdout() {
text.dimmed()
} else {
text.normal()
}
}
pub fn style(&self) -> StyleBuilder {
StyleBuilder::new(self.should_color_stdout())
}
}
pub struct StyleBuilder {
should_color: bool,
}
impl StyleBuilder {
pub fn new(should_color: bool) -> Self {
Self { should_color }
}
pub fn red(self, text: &str) -> ChainedStyle {
ChainedStyle::new(text, self.should_color).red()
}
pub fn green(self, text: &str) -> ChainedStyle {
ChainedStyle::new(text, self.should_color).green()
}
pub fn blue(self, text: &str) -> ChainedStyle {
ChainedStyle::new(text, self.should_color).blue()
}
pub fn yellow(self, text: &str) -> ChainedStyle {
ChainedStyle::new(text, self.should_color).yellow()
}
}
pub struct ChainedStyle {
text: String,
should_color: bool,
}
impl ChainedStyle {
fn new(text: &str, should_color: bool) -> Self {
Self {
text: text.to_string(),
should_color,
}
}
pub fn red(mut self) -> Self {
if self.should_color {
self.text = self.text.red().to_string();
}
self
}
pub fn green(mut self) -> Self {
if self.should_color {
self.text = self.text.green().to_string();
}
self
}
pub fn blue(mut self) -> Self {
if self.should_color {
self.text = self.text.blue().to_string();
}
self
}
pub fn yellow(mut self) -> Self {
if self.should_color {
self.text = self.text.yellow().to_string();
}
self
}
pub fn bold(mut self) -> Self {
if self.should_color {
self.text = self.text.bold().to_string();
}
self
}
}
impl std::fmt::Display for ChainedStyle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.text)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_color_mode_parsing() {
assert_eq!("auto".parse::<ColorMode>().unwrap(), ColorMode::Auto);
assert_eq!("always".parse::<ColorMode>().unwrap(), ColorMode::Always);
assert_eq!("never".parse::<ColorMode>().unwrap(), ColorMode::Never);
assert!("invalid".parse::<ColorMode>().is_err());
}
#[test]
fn test_color_helper_never() {
let helper = ColorHelper::new(ColorMode::Never);
assert!(!helper.should_color_stdout());
}
#[test]
fn test_color_helper_always() {
let helper = ColorHelper::new(ColorMode::Always);
if !helper.no_color {
assert!(helper.should_color_stdout());
}
}
}