use clap::ValueEnum;
use ratatui::style::{Color, Style as TuiStyle};
use ratatui::text::{Line, Span, Text};
use std::fmt::{Display, Formatter, Result as FmtResult};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, ValueEnum)]
pub enum Style {
#[default]
Plain,
Colored,
}
impl Display for Style {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{}", format!("{self:?}").to_lowercase())
}
}
impl Style {
pub fn is_colored(&self) -> bool {
self == &Self::Colored
}
pub fn next(&self) -> Self {
match self {
Self::Plain => Self::Colored,
_ => Self::Plain,
}
}
}
pub fn get_colored_table_row<'a>(
row_data: &[String],
highlighted: bool,
) -> Text<'a> {
let highlight_style = if highlighted {
TuiStyle::default().fg(Color::Reset)
} else {
TuiStyle::default()
};
let mut row = Vec::new();
for line in row_data.iter() {
let (first_bracket, second_bracket) = (
line.find('[').unwrap_or_default(),
line.find(']').unwrap_or_default(),
);
row.push(
if second_bracket > first_bracket + 1 {
let data = line[first_bracket + 1..second_bracket].to_string();
let mut colored_line = vec![Span::styled(
line[..first_bracket + 1].to_string(),
highlight_style,
)];
if [
String::from("exp"),
String::from("rev"),
String::from("d"),
String::from("i"),
]
.contains(&data)
{
colored_line.push(Span::styled(
data,
TuiStyle::default().fg(Color::Red),
))
} else if data.len() == 2 {
let style = match data.as_ref() {
"10" => TuiStyle::default().fg(Color::Yellow),
"11" => TuiStyle::default().fg(Color::Magenta),
"12" => TuiStyle::default().fg(Color::Blue),
"13" => TuiStyle::default().fg(Color::Green),
_ => TuiStyle::default().fg(Color::Red),
};
colored_line.push(Span::styled(data, style))
} else {
for c in data.chars().map(String::from) {
let style = match c.as_ref() {
"?" | "q" | "-" => {
TuiStyle::default().fg(Color::DarkGray)
}
"n" => TuiStyle::default().fg(Color::Red),
"m" => TuiStyle::default().fg(Color::Blue),
"f" => TuiStyle::default().fg(Color::Magenta),
"u" | "h" => TuiStyle::default().fg(Color::Green),
"s" => TuiStyle::default().fg(Color::LightGreen),
"c" => TuiStyle::default().fg(Color::LightBlue),
"e" => TuiStyle::default().fg(Color::Yellow),
"a" | "!" => {
TuiStyle::default().fg(Color::LightRed)
}
_ => TuiStyle::default(),
};
colored_line.push(Span::styled(c, style))
}
}
let data = line[second_bracket..].to_string();
if data.find('/') == Some(9) {
colored_line.push(Span::styled(
data.chars().next().unwrap_or_default().to_string(),
highlight_style,
));
colored_line.push(Span::styled(
data[1..9].to_string(),
TuiStyle::default().fg(Color::Cyan),
));
colored_line.push(Span::styled(
"/",
TuiStyle::default().fg(Color::DarkGray),
));
colored_line.push(Span::styled(
data[10..].to_string(),
highlight_style,
));
} else if let (Some(first_arrow), Some(second_arrow)) =
(data.rfind('<'), data.rfind('>'))
{
colored_line.push(Span::styled(
data[..first_arrow].to_string(),
highlight_style,
));
colored_line.push(Span::styled(
"<",
TuiStyle::default().fg(Color::DarkGray),
));
colored_line.push(Span::styled(
data[first_arrow + 1..second_arrow].to_string(),
TuiStyle::default().fg(Color::Cyan),
));
colored_line.push(Span::styled(
">",
TuiStyle::default().fg(Color::DarkGray),
));
colored_line.push(Span::styled(
data[second_arrow + 1..].to_string(),
highlight_style,
));
} else {
colored_line.push(Span::styled(data, highlight_style));
}
Line::from(colored_line)
} else {
Line::from(vec![Span::styled(
line.to_string(),
highlight_style,
)])
},
)
}
Text::from(row)
}
pub fn get_colored_info(info: &str, color: Color) -> Text<'_> {
Text::from(
info.lines()
.map(|v| {
let mut values = v.split(':').collect::<Vec<&str>>();
Line::from(if values.len() >= 2 && !v.starts_with(' ') {
vec![
Span::styled(
values[0],
TuiStyle::default().fg(Color::Reset),
),
Span::styled(
":",
TuiStyle::default().fg(Color::DarkGray),
),
Span::styled(
values.drain(1..).collect::<Vec<&str>>().join(":"),
TuiStyle::default().fg(color),
),
]
} else {
vec![Span::styled(v, TuiStyle::default().fg(Color::Reset))]
})
})
.collect::<Vec<Line>>(),
)
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use std::borrow::Cow::Borrowed;
#[test]
fn test_app_style() {
let row_data = r#"
[sc--] rsa2048/C4B2D24CF87CD188C79D00BB485B7C52E9EC0DC6
└─(2020-07-29)
"#
.to_string()
.lines()
.map(String::from)
.collect::<Vec<String>>();
assert_eq!(
Text {
lines: vec![
Line {
spans: vec![Span {
content: Borrowed(""),
style: TuiStyle::default(),
}],
..Default::default()
},
Line {
spans: vec![
Span {
content: Borrowed("["),
style: TuiStyle::default(),
},
Span {
content: Borrowed("s"),
style: TuiStyle {
fg: Some(Color::LightGreen),
..TuiStyle::default()
},
},
Span {
content: Borrowed("c"),
style: TuiStyle {
fg: Some(Color::LightBlue),
..TuiStyle::default()
},
},
Span {
content: Borrowed("-"),
style: TuiStyle {
fg: Some(Color::DarkGray),
..TuiStyle::default()
},
},
Span {
content: Borrowed("-"),
style: TuiStyle {
fg: Some(Color::DarkGray),
..TuiStyle::default()
},
},
Span {
content: Borrowed("]"),
style: TuiStyle::default(),
},
Span {
content: Borrowed(" rsa2048"),
style: TuiStyle {
fg: Some(Color::Cyan),
..TuiStyle::default()
},
},
Span {
content: Borrowed("/"),
style: TuiStyle {
fg: Some(Color::DarkGray),
..TuiStyle::default()
},
},
Span {
content: Borrowed(
"C4B2D24CF87CD188C79D00BB485B7C52E9EC0DC6"
),
style: TuiStyle::default(),
},
],
..Default::default()
},
Line {
spans: vec![Span {
content: Borrowed(" └─(2020-07-29)"),
style: TuiStyle::default(),
}],
..Default::default()
},
Line {
spans: vec![Span {
content: Borrowed("\t\t"),
style: TuiStyle::default(),
}],
..Default::default()
},
],
..Default::default()
},
get_colored_table_row(&row_data, false)
);
let row_data = r#"
[u] kmon releases <kmonlinux@protonmail.com>
├─[13] selfsig (2020-07-29)
├─][ test
└─[10] B928720AEC532117 orhun <orhunparmaksiz@gmail.com> (2020-07-29)
"#
.to_string()
.lines()
.map(String::from)
.collect::<Vec<String>>();
assert_eq!(
Text {
lines: vec![
Line {
spans: vec![Span {
content: Borrowed(""),
style: TuiStyle::default(),
}],
..Default::default()
},
Line {
spans: vec![
Span {
content: Borrowed("["),
style: TuiStyle::default(),
},
Span {
content: Borrowed("u"),
style: TuiStyle {
fg: Some(Color::Green),
..TuiStyle::default()
},
},
Span {
content: Borrowed("] kmon releases "),
style: TuiStyle::default(),
},
Span {
content: Borrowed("<"),
style: TuiStyle {
fg: Some(Color::DarkGray),
..TuiStyle::default()
},
},
Span {
content: Borrowed("kmonlinux@protonmail.com"),
style: TuiStyle {
fg: Some(Color::Cyan),
..TuiStyle::default()
},
},
Span {
content: Borrowed(">"),
style: TuiStyle {
fg: Some(Color::DarkGray),
..TuiStyle::default()
},
},
Span {
content: Borrowed(""),
style: TuiStyle::default(),
},
],
..Default::default()
},
Line {
spans: vec![
Span {
content: Borrowed("\t├─["),
style: TuiStyle::default(),
},
Span {
content: Borrowed("13"),
style: TuiStyle {
fg: Some(Color::Green),
..TuiStyle::default()
},
},
Span {
content: Borrowed("] selfsig (2020-07-29)"),
style: TuiStyle::default(),
},
],
..Default::default()
},
Line {
spans: vec![Span {
content: Borrowed("\t├─][ test"),
style: TuiStyle::default(),
}],
..Default::default()
},
Line {
spans: vec![
Span {
content: Borrowed("\t└─["),
style: TuiStyle::default(),
},
Span {
content: Borrowed("10"),
style: TuiStyle {
fg: Some(Color::Yellow),
..TuiStyle::default()
},
},
Span {
content: Borrowed("] B928720AEC532117 orhun "),
style: TuiStyle::default(),
},
Span {
content: Borrowed("<"),
style: TuiStyle {
fg: Some(Color::DarkGray),
..TuiStyle::default()
},
},
Span {
content: Borrowed("orhunparmaksiz@gmail.com"),
style: TuiStyle {
fg: Some(Color::Cyan),
..TuiStyle::default()
},
},
Span {
content: Borrowed(">"),
style: TuiStyle {
fg: Some(Color::DarkGray),
..TuiStyle::default()
},
},
Span {
content: Borrowed(" (2020-07-29)"),
style: TuiStyle::default(),
},
],
..Default::default()
},
Line {
spans: vec![Span {
content: Borrowed("\t\t\t\t"),
style: TuiStyle::default(),
}],
..Default::default()
},
],
..Default::default()
},
get_colored_table_row(&row_data, false)
);
assert_eq!(
Text {
lines: vec![
Line {
spans: vec![
Span {
content: Borrowed("test"),
style: TuiStyle {
fg: Some(Color::Reset),
..TuiStyle::default()
},
},
Span {
content: Borrowed(":"),
style: TuiStyle {
fg: Some(Color::DarkGray),
..TuiStyle::default()
},
},
Span {
content: Borrowed(" xyz "),
style: TuiStyle {
fg: Some(Color::LightRed),
..TuiStyle::default()
},
},
],
..Default::default()
},
Line {
spans: vec![
Span {
content: Borrowed("test2"),
style: TuiStyle {
fg: Some(Color::Reset),
..TuiStyle::default()
},
},
Span {
content: Borrowed(":"),
style: TuiStyle {
fg: Some(Color::DarkGray),
..TuiStyle::default()
},
},
Span {
content: Borrowed(" abc"),
style: TuiStyle {
fg: Some(Color::LightRed),
..TuiStyle::default()
},
},
],
..Default::default()
},
Line {
spans: vec![Span {
content: Borrowed(" skip this line"),
style: TuiStyle {
fg: Some(Color::Reset),
..TuiStyle::default()
},
}],
..Default::default()
},
Line {
spans: vec![Span {
content: Borrowed("reset"),
style: TuiStyle {
fg: Some(Color::Reset),
..TuiStyle::default()
},
}],
..Default::default()
},
],
..Default::default()
},
get_colored_info(
"test: xyz \n\
test2: abc\n \
skip this line\n\
reset",
Color::LightRed
)
)
}
}