use std::fmt;
pub use colored::Color;
use log::Level;
trait ColoredLogLevel {
fn colored(&self, color: Color) -> WithFgColor<Level>;
}
pub struct WithFgColor<T>
where
T: fmt::Display,
{
text: T,
color: Color,
}
impl<T> fmt::Display for WithFgColor<T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\x1B[{}m", self.color.to_fg_str())?;
fmt::Display::fmt(&self.text, f)?;
write!(f, "\x1B[0m")?;
Ok(())
}
}
#[derive(Copy, Clone)]
#[must_use = "builder methods take config by value and thus must be reassigned to variable"]
pub struct ColoredLevelConfig {
pub error: Color,
pub warn: Color,
pub info: Color,
pub debug: Color,
pub trace: Color,
}
impl ColoredLevelConfig {
#[inline]
pub fn new() -> Self {
#[cfg(windows)]
{
let _ = colored::control::set_virtual_terminal(true);
}
Self::default()
}
pub fn error(mut self, error: Color) -> Self {
self.error = error;
self
}
pub fn warn(mut self, warn: Color) -> Self {
self.warn = warn;
self
}
pub fn info(mut self, info: Color) -> Self {
self.info = info;
self
}
pub fn debug(mut self, debug: Color) -> Self {
self.debug = debug;
self
}
pub fn trace(mut self, trace: Color) -> Self {
self.trace = trace;
self
}
pub fn color(&self, level: Level) -> WithFgColor<Level> {
level.colored(self.get_color(&level))
}
pub fn get_color(&self, level: &Level) -> Color {
match *level {
Level::Error => self.error,
Level::Warn => self.warn,
Level::Info => self.info,
Level::Debug => self.debug,
Level::Trace => self.trace,
}
}
}
impl Default for ColoredLevelConfig {
fn default() -> Self {
ColoredLevelConfig {
error: Color::Red,
warn: Color::Yellow,
debug: Color::White,
info: Color::White,
trace: Color::White,
}
}
}
impl ColoredLogLevel for Level {
fn colored(&self, color: Color) -> WithFgColor<Level> {
WithFgColor { text: *self, color }
}
}
#[cfg(test)]
#[cfg(not(windows))]
mod test {
use colored::{Color::*, Colorize};
use super::WithFgColor;
#[test]
fn fg_color_matches_colored_behavior() {
for &color in &[
Black,
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
White,
BrightBlack,
BrightRed,
BrightGreen,
BrightYellow,
BrightBlue,
BrightMagenta,
BrightCyan,
BrightWhite,
] {
colored::control::SHOULD_COLORIZE.set_override(true);
assert_eq!(
format!("{}", "test".color(color)),
format!(
"{}",
WithFgColor {
text: "test",
color,
}
)
);
}
}
#[test]
fn fg_color_respects_formatting_flags() {
let s = format!(
"{:^8}",
WithFgColor {
text: "test",
color: Yellow,
}
);
assert!(s.contains(" test "));
assert!(!s.contains(" test "));
assert!(!s.contains(" test "));
}
}