diff --git a/Cargo.lock b/Cargo.lock index 0a7c42f..0ba3f2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -825,6 +825,6 @@ dependencies = [ [[package]] name = "yansi" -version = "0.5.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" diff --git a/Cargo.toml b/Cargo.toml index 9eb1b2d..d61bb38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ edition = "2021" [dependencies] serde_json = "1.0" -yansi = "0.5.1" regex = "1" mlua = { version = "0.9", features = ["lua54", "vendored"] } lazy_static = "1.4.0" @@ -25,3 +24,7 @@ dirs = "5" [dependencies.clap] version = "4" features = ["cargo", "deprecated"] + +[dependencies.yansi] +version = "1" +features = ["detect-env"] diff --git a/src/log.rs b/src/log.rs index bd1c45e..e3b0180 100644 --- a/src/log.rs +++ b/src/log.rs @@ -1,11 +1,10 @@ use crate::log_settings::LogSettings; -use crate::no_color_support::style; use handlebars::Handlebars; use serde_json::{Map, Value}; use std::borrow::ToOwned; use std::collections::BTreeMap; use std::io::Write; -use yansi::Color; +use yansi::Paint; pub fn print_log_line( out: &mut dyn Write, @@ -38,7 +37,7 @@ pub fn print_log_line( let write_result = match handlebars.render("main_line", &handle_bar_input) { Ok(string) => writeln!(out, "{}", string), - Err(e) => writeln!(out, "{} Failed to process line: {}", style(&Color::Red.style().bold(), "??? >"), e), + Err(e) => writeln!(out, "{} Failed to process line: {}", "??? >".red().bold(), e), }; if write_result.is_err() { @@ -135,12 +134,7 @@ fn write_additional_values(out: &mut dyn Write, log_entry: &BTreeMap writeln!(out, "{}", string), - Err(e) => writeln!( - out, - "{} Failed to process additional value: {}", - style(&Color::Red.style().bold(), " ??? >"), - e - ), + Err(e) => writeln!(out, "{} Failed to process additional value: {}", " ??? >".red().bold(), e), }; if write_result.is_err() { // Output end reached @@ -154,7 +148,13 @@ fn write_additional_values(out: &mut dyn Write, log_entry: &BTreeMap String { + use regex::Regex; + let regex = Regex::new("\u{001B}\\[[\\d;]*[^\\d;]").expect("Regex should be valid"); + regex.replace_all(styled, "").into_owned() + } fn fblog_handlebar_registry_default_format() -> Handlebars<'static> { let main_line_format = template::DEFAULT_MAIN_LINE_FORMAT.to_string(); diff --git a/src/main.rs b/src/main.rs index 2029e52..053aee6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,6 @@ mod config; mod filter; mod log; mod log_settings; -mod no_color_support; mod process; mod substitution; mod template; diff --git a/src/no_color_support.rs b/src/no_color_support.rs deleted file mode 100644 index 28028a4..0000000 --- a/src/no_color_support.rs +++ /dev/null @@ -1,39 +0,0 @@ -use lazy_static::lazy_static; -use std::env; -use yansi::{Color, Style}; - -lazy_static! { - static ref NO_COLOR: bool = env::var("NO_COLOR").is_ok(); -} - -pub fn paint(c: Color, t: &str) -> String { - if *NO_COLOR { - t.to_string() - } else { - format!("{}", c.paint(t)) - } -} - -pub fn style(s: &Style, t: &str) -> String { - if *NO_COLOR { - t.to_string() - } else { - format!("{}", s.paint(t)) - } -} - -pub fn stylew(mut target: W, s: &Style, t: &str) { - if *NO_COLOR { - _ = target.write_str(t); - } else { - _ = write!(target, "{}", s.paint(t)); - } -} - -#[cfg(test)] -pub fn without_style(styled: &str) -> String { - use regex::Regex; - - let regex = Regex::new("\u{001B}\\[[\\d;]*[^\\d;]").expect("Regex should be valid"); - regex.replace_all(styled, "").into_owned() -} diff --git a/src/process.rs b/src/process.rs index b9de20b..035904e 100644 --- a/src/process.rs +++ b/src/process.rs @@ -1,16 +1,15 @@ use crate::filter; use crate::log; use crate::log_settings::LogSettings; -use crate::no_color_support::style; use handlebars::Handlebars; use lazy_static::lazy_static; use serde_json::{Map, Value}; use std::io::Write; use std::io::{self, BufRead}; -use yansi::{Color, Style}; +use yansi::{Color, Paint}; lazy_static! { - static ref BOLD_ORANGE: Style = Color::RGB(255, 135, 22).style().bold(); + static ref ORANGE: Color = Color::Rgb(255, 135, 22); } pub fn process_input( @@ -30,7 +29,7 @@ pub fn process_input( } fn print_unknown_line(line: &str) { - let write_result = writeln!(&mut io::stdout(), "{} {}", style(&BOLD_ORANGE, "??? >"), line); + let write_result = writeln!(&mut io::stdout(), "{} {}", "??? >".fg(*ORANGE).bold(), line); if write_result.is_err() { // Output end reached std::process::exit(14); @@ -80,7 +79,7 @@ fn process_json_log_entry( Ok(true) => process_log_entry(log_settings, maybe_prefix, log_entry, handlebars), Ok(false) => (), Err(e) => { - writeln!(io::stderr(), "{}: '{:?}'", Color::Red.paint("Failed to apply filter expression"), e).expect("Should be able to write to stderr"); + writeln!(io::stderr(), "{}: '{:?}'", "Failed to apply filter expression".red(), e).expect("Should be able to write to stderr"); } } } else { diff --git a/src/substitution.rs b/src/substitution.rs index 5c194a3..dc36326 100644 --- a/src/substitution.rs +++ b/src/substitution.rs @@ -1,8 +1,8 @@ +use std::fmt; + use regex::{Captures, Regex}; use serde_json::Value; -use yansi::Color; - -use crate::no_color_support::stylew; +use yansi::Paint; #[derive(Debug)] pub enum Error { @@ -25,9 +25,6 @@ impl std::fmt::Display for Error { } } -/// 7 bytes for color (`\e` `[` `1` `;` `3` `9` `m`) and 4 bytes for reset (`\e` `[` `0` `m`) -const COLOR_OVERHEAD: usize = 7 + 4; - pub struct Substitution { pub context_key: String, placeholder_prefix: String, @@ -63,11 +60,7 @@ impl Substitution { } pub(crate) fn apply(&self, message: &str, log_entry: &serde_json::Map) -> Option { - let Some(context_value) = log_entry.get(&self.context_key) else { - return None; - }; - - let key_format_overhead = self.placeholder_prefix.len() + COLOR_OVERHEAD + self.placeholder_suffix.len() + COLOR_OVERHEAD; + let context_value = log_entry.get(&self.context_key)?; return Some( self.placeholder_regex @@ -80,15 +73,11 @@ impl Substitution { }; match value { None => { - let mut buf = String::with_capacity(key.len() + COLOR_OVERHEAD + key_format_overhead); - stylew(&mut buf, &Color::Default.style().dimmed(), &self.placeholder_prefix); - stylew(&mut buf, &Color::Red.style().bold(), key); - stylew(&mut buf, &Color::Default.style().dimmed(), &self.placeholder_suffix); - buf + format!("{}{}{}", self.placeholder_prefix.dim(), key.red().bold(), self.placeholder_suffix.dim()) } Some(value) => { let mut buf = String::new(); - self.color_format(&mut buf, value); + let _ = self.color_format(&mut buf, value); buf } } @@ -97,40 +86,43 @@ impl Substitution { ); } - fn color_format(&self, buf: &mut String, value: &Value) { + fn color_format(&self, buf: &mut W, value: &Value) -> Result<(), fmt::Error> { match value { - Value::String(s) => stylew(buf, &Color::Yellow.style().bold(), s), - Value::Number(n) => stylew(buf, &Color::Cyan.style().bold(), &n.to_string()), + Value::String(s) => write!(buf, "{}", s.yellow().bold()), + Value::Number(n) => write!(buf, "{}", n.to_string().cyan().bold()), Value::Array(a) => self.color_format_array(buf, a), Value::Object(o) => self.color_format_map(buf, o), - Value::Bool(true) => stylew(buf, &Color::Green.style().bold(), "true"), - Value::Bool(false) => stylew(buf, &Color::Red.style().bold(), "false"), - Value::Null => stylew(buf, &Color::Default.style().bold(), "null"), - } + Value::Bool(true) => write!(buf, "{}", "true".green().bold()), + Value::Bool(false) => write!(buf, "{}", "false".red().bold()), + Value::Null => write!(buf, "{}", "null".bold()), + }?; + Ok(()) } - fn color_format_array(&self, mut buf: &mut String, a: &[Value]) { - stylew(&mut buf, &Color::Default.style().dimmed(), "["); + fn color_format_array(&self, buf: &mut W, a: &[Value]) -> Result<(), fmt::Error> { + write!(buf, "{}", "[".dim())?; for (i, value) in a.iter().enumerate() { if i > 0 { - stylew(&mut buf, &Color::Default.style().dimmed(), ", "); + write!(buf, "{}", ", ".dim())?; } - self.color_format(buf, value); + self.color_format(buf, value)?; } - stylew(buf, &Color::Default.style().dimmed(), "]"); + write!(buf, "{}", "]".dim())?; + Ok(()) } - fn color_format_map(&self, mut buf: &mut String, o: &serde_json::Map) { - stylew(&mut buf, &Color::Default.style().dimmed(), "{"); + fn color_format_map(&self, buf: &mut W, o: &serde_json::Map) -> Result<(), fmt::Error> { + write!(buf, "{}", "{".dim())?; for (i, (key, value)) in o.iter().enumerate() { if i > 0 { - stylew(&mut buf, &Color::Default.style().dimmed(), ", "); + write!(buf, "{}", ", ".dim())?; } - stylew(&mut buf, &Color::Magenta.style(), key); - stylew(&mut buf, &Color::Default.style().dimmed(), ": "); - self.color_format(buf, value); + write!(buf, "{}", key.magenta())?; + write!(buf, "{}", ": ".dim())?; + self.color_format(buf, value)?; } - stylew(buf, &Color::Default.style().dimmed(), "}"); + write!(buf, "{}", "}".dim())?; + Ok(()) } } @@ -142,11 +134,15 @@ impl Default for Substitution { #[cfg(test)] mod tests { - use crate::no_color_support::without_style; use super::*; type JMap = serde_json::Map; + fn without_style(styled: &str) -> String { + let regex = Regex::new("\u{001B}\\[[\\d;]*[^\\d;]").expect("Regex should be valid"); + regex.replace_all(styled, "").into_owned() + } + fn entry_context>(subst: &Substitution, context: V) -> JMap { let mut map = serde_json::Map::new(); map.insert(subst.context_key.clone(), context.into()); diff --git a/src/template.rs b/src/template.rs index e9e6a34..f95344b 100644 --- a/src/template.rs +++ b/src/template.rs @@ -1,57 +1,42 @@ -use crate::no_color_support::{paint, style}; use handlebars::{handlebars_helper, no_escape, Handlebars}; use std::convert::TryInto; -use yansi::{Color, Style}; +use yansi::{Color, Paint}; pub static DEFAULT_MAIN_LINE_FORMAT: &str = "{{bold(fixed_size 19 fblog_timestamp)}} {{level_style (uppercase (fixed_size 5 fblog_level))}}:{{#if fblog_prefix}} {{bold(cyan fblog_prefix)}}{{/if}} {{fblog_message}}"; pub static DEFAULT_ADDITIONAL_VALUE_FORMAT: &str = "{{bold (color_rgb 150 150 150 (fixed_size 25 key))}}: {{value}}"; -fn level_to_style(level: &str) -> Style { - match level.trim().to_lowercase().as_ref() { - "trace" => Color::Cyan, - "debug" => Color::Blue, - "info" => Color::Green, - "warn" | "warning" => Color::Yellow, - "error" | "err" => Color::Red, - "fatal" => Color::Magenta, - _ => Color::Magenta, - } - .style() - .bold() -} - pub fn fblog_handlebar_registry(main_line_format: String, additional_value_format: String) -> Handlebars<'static> { handlebars_helper!(bold: |t: str| { - style(&Color::Default.style().bold(), t) + format!("{}", t.bold()) }); handlebars_helper!(cyan: |t: str| { - paint(Color::Cyan, t) + format!("{}", t.cyan()) }); handlebars_helper!(yellow: |t: str| { - paint(Color::Yellow, t) + format!("{}", t.yellow()) }); handlebars_helper!(red: |t: str| { - paint(Color::Red, t) + format!("{}", t.red()) }); handlebars_helper!(blue: |t: str| { - paint(Color::Blue, t) + format!("{}", t.blue()) }); handlebars_helper!(purple: |t: str| { - paint(Color::Magenta, t) + format!("{}", t.magenta()) }); handlebars_helper!(green: |t: str| { - paint(Color::Green, t) + format!("{}", t.green()) }); handlebars_helper!(color_rgb: |r: u64, g: u64, b: u64, t: str| { - paint(Color::RGB(r.try_into().unwrap(), g.try_into().unwrap(), b.try_into().unwrap()), t) + format!("{}", t.rgb(r.try_into().unwrap(), g.try_into().unwrap(), b.try_into().unwrap())) }); handlebars_helper!(uppercase: |t: str| { @@ -59,8 +44,16 @@ pub fn fblog_handlebar_registry(main_line_format: String, additional_value_forma }); handlebars_helper!(level_style: |level: str| { - let s = level_to_style(level); - style(&s, level) + let color = match level.trim().to_lowercase().as_ref() { + "trace" => Color::Cyan, + "debug" => Color::Blue, + "info" => Color::Green, + "warn" | "warning" => Color::Yellow, + "error" | "err" => Color::Red, + "fatal" => Color::Magenta, + _ => Color::Magenta, + }; + format!("{}", level.fg(color).bold()) }); handlebars_helper!(fixed_size: |isize: u64, t: str| {