use std::io::IsTerminal;
use std::sync::atomic::{AtomicBool, Ordering};
static PRINT_COLOR: AtomicBool = AtomicBool::new(false);
pub fn initialize_color() {
let color = if std::env::var_os("NO_COLOR").is_some() {
false
} else if std::env::var_os("FORCE_COLOR").is_some() {
true
} else {
std::io::stdout().is_terminal()
};
PRINT_COLOR.store(color, Ordering::Relaxed);
}
const DEFAULT: &str = "\x1b[0m";
const FG_BLUE: &str = "\x1b[0;34m";
const FG_CYAN: &str = "\x1b[0;36m";
const FG_GRAY: &str = "\x1b[0;90m";
const FG_GREEN: &str = "\x1b[0;32m";
const FG_LIGHT_MAGENTA: &str = "\x1b[0;95m";
const FG_LIGHT_RED: &str = "\x1b[1;31m";
const FG_LIGHT_YELLOW: &str = "\x1b[0;93m";
const FG_YELLOW: &str = "\x1b[0;33m";
pub enum Color {
Default,
Success,
Error,
Glue,
Deemphasize,
Match,
Upgrade,
Downgrade,
Create,
Remove,
PkgName,
PkgVer,
Arch,
Field,
File,
Url,
Warn,
Timestamp,
}
impl std::fmt::Display for Color {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if !PRINT_COLOR.load(Ordering::Relaxed) {
return Ok(());
}
write!(
f,
"{}",
match self {
Color::Default => DEFAULT,
Color::Success => FG_GREEN,
Color::Error => FG_LIGHT_RED,
Color::Glue => FG_GRAY,
Color::Deemphasize => FG_GRAY,
Color::Match => FG_GREEN,
Color::Upgrade => FG_CYAN,
Color::Downgrade => FG_YELLOW,
Color::Create => FG_GREEN,
Color::Remove => FG_LIGHT_RED,
Color::PkgName => FG_CYAN,
Color::PkgVer => FG_YELLOW,
Color::Arch => FG_LIGHT_MAGENTA,
Color::Field => FG_GREEN,
Color::File => FG_GREEN,
Color::Url => FG_BLUE,
Color::Warn => FG_LIGHT_YELLOW,
Color::Timestamp => FG_GRAY,
}
)
}
}
#[macro_export]
macro_rules! make_display_color {
($type:ident, $color_fmt:expr) => {
paste::paste! {
pub struct [<Color $type>]<'a>(&'a $type);
impl $type {
pub fn color(&self) -> [<Color $type>]<'_> {
[<Color $type>](self)
}
}
impl [<Color $type>]<'_> {
fn inner(&self) -> &$type {
&self.0
}
}
impl std::fmt::Display for [<Color $type>]<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let display_fn: &dyn Fn(&$type, &mut std::fmt::Formatter<'_>) -> std::fmt::Result = &$color_fmt;
display_fn(self.inner(), f)
}
}
}
};
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_color_macro() {
initialize_color();
struct TestStruct;
impl std::fmt::Display for TestStruct {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "test")
}
}
make_display_color!(TestStruct, |s, f| {
write!(f, "{}{}{}", Color::Success, s, Color::Default)
});
let test = TestStruct;
assert_eq!(format!("{}", test), "test");
if PRINT_COLOR.load(Ordering::Relaxed) {
assert_eq!(format!("{}", test.color()), "\x1b[0;32mtest\x1b[0m");
} else {
assert_eq!(format!("{}", test.color()), "test");
}
}
}