pub mod log_sanitizer {
use cansi::{Color as CansiColor, Intensity, v3::categorise_text};
use ratatui::{
style::{Color, Modifier, Style},
text::{Line, Span},
};
pub fn colorize_logs<'a>(input: &str) -> Vec<Line<'a>> {
vec![Line::from(
categorise_text(input)
.iter()
.map(|i| {
let mut style = Style::default()
.bg(color_ansi_to_tui(i.bg.unwrap_or(CansiColor::Black)))
.fg(color_ansi_to_tui(i.fg.unwrap_or(CansiColor::White)));
if i.blink.is_some() {
style = style.add_modifier(Modifier::SLOW_BLINK);
}
if i.underline.is_some() {
style = style.add_modifier(Modifier::UNDERLINED);
}
if i.reversed.is_some() {
style = style.add_modifier(Modifier::REVERSED);
}
if i.intensity == Some(Intensity::Bold) {
style = style.add_modifier(Modifier::BOLD);
}
if i.hidden.is_some() {
style = style.add_modifier(Modifier::HIDDEN);
}
if i.strikethrough.is_some() {
style = style.add_modifier(Modifier::CROSSED_OUT);
}
Span::styled(i.text.to_owned(), style)
})
.collect::<Vec<_>>(),
)]
}
pub fn remove_ansi<'a>(input: &str) -> Vec<Line<'a>> {
vec![Line::from(
categorise_text(input)
.into_iter()
.map(|i| i.text)
.collect::<String>()
.trim()
.to_owned(),
)]
}
pub fn raw<'a>(input: &str) -> Vec<Line<'a>> {
vec![Line::from(input.escape_debug().collect::<String>())]
}
const fn color_ansi_to_tui(color: CansiColor) -> Color {
match color {
CansiColor::Black | CansiColor::BrightBlack => Color::Black,
CansiColor::Red => Color::Red,
CansiColor::Green => Color::Green,
CansiColor::Yellow => Color::Yellow,
CansiColor::Blue => Color::Blue,
CansiColor::Magenta => Color::Magenta,
CansiColor::Cyan => Color::Cyan,
CansiColor::White | CansiColor::BrightWhite => Color::Gray,
CansiColor::BrightRed => Color::LightRed,
CansiColor::BrightGreen => Color::LightGreen,
CansiColor::BrightYellow => Color::LightYellow,
CansiColor::BrightBlue => Color::LightBlue,
CansiColor::BrightMagenta => Color::LightMagenta,
CansiColor::BrightCyan => Color::LightCyan,
}
}
}
#[cfg(test)]
mod tests {
use ratatui::{
style::{Color, Style},
text::{Line, Span},
};
use super::log_sanitizer;
const INPUT: &str = "\x1b[31;47mo\x1b[32;40mx\x1b[33;41mk\x1b[34;42me\x1b[35;43mr\x1b[0m";
#[test]
fn test_color_match_raw() {
let result = log_sanitizer::raw(INPUT);
let expected = vec![Line {
spans: [Span {
content: std::borrow::Cow::Borrowed(
"\\u{1b}[31;47mo\\u{1b}[32;40mx\\u{1b}[33;41mk\\u{1b}[34;42me\\u{1b}[35;43mr\\u{1b}[0m",
),
style: Style::default(),
}]
.to_vec(),
alignment: None,
style: Style::default(),
}];
assert_eq!(result, expected);
}
#[test]
fn test_color_match_colorize() {
let result = log_sanitizer::colorize_logs(INPUT);
let expected = vec![Line {
spans: vec![
Span {
content: std::borrow::Cow::Borrowed("o"),
style: Style::default().fg(Color::Red).bg(Color::Gray),
},
Span {
content: std::borrow::Cow::Borrowed("x"),
style: Style::default().fg(Color::Green).bg(Color::Black),
},
Span {
content: std::borrow::Cow::Borrowed("k"),
style: Style::default().fg(Color::Yellow).bg(Color::Red),
},
Span {
content: std::borrow::Cow::Borrowed("e"),
style: Style::default().fg(Color::Blue).bg(Color::Green),
},
Span {
content: std::borrow::Cow::Borrowed("r"),
style: Style::default().fg(Color::Magenta).bg(Color::Yellow),
},
],
alignment: None,
style: Style::default(),
}];
assert_eq!(result, expected);
}
#[test]
fn test_color_match_remove_ansi() {
let result = log_sanitizer::remove_ansi(INPUT);
let expected = vec![Line {
spans: vec![Span {
content: std::borrow::Cow::Borrowed("oxker"),
style: Style::default(),
}],
style: Style::default(),
alignment: None,
}];
assert_eq!(result, expected);
}
}