1use clap::ValueEnum;
2use ratatui::style::{Color, Style as TuiStyle};
3use ratatui::text::{Line, Span, Text};
4use std::fmt::{Display, Formatter, Result as FmtResult};
5
6#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, ValueEnum)]
8pub enum Style {
9 #[default]
11 Plain,
12 Colored,
14}
15
16impl Display for Style {
17 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
18 write!(f, "{}", format!("{self:?}").to_lowercase())
19 }
20}
21
22impl Style {
23 pub fn is_colored(&self) -> bool {
27 self == &Self::Colored
28 }
29
30 pub fn next(&self) -> Self {
32 match self {
33 Self::Plain => Self::Colored,
34 _ => Self::Plain,
35 }
36 }
37}
38
39pub fn get_colored_table_row<'a>(
46 row_data: &[String],
47 highlighted: bool,
48) -> Text<'a> {
49 let highlight_style = if highlighted {
50 TuiStyle::default().fg(Color::Reset)
51 } else {
52 TuiStyle::default()
53 };
54 let mut row = Vec::new();
55 for line in row_data.iter() {
56 let (first_bracket, second_bracket) = (
57 line.find('[').unwrap_or_default(),
58 line.find(']').unwrap_or_default(),
59 );
60 row.push(
61 if second_bracket > first_bracket + 1 {
63 let data = line[first_bracket + 1..second_bracket].to_string();
64 let mut colored_line = vec![Span::styled(
65 line[..first_bracket + 1].to_string(),
66 highlight_style,
67 )];
68 if [
69 String::from("exp"),
71 String::from("rev"),
73 String::from("d"),
75 String::from("i"),
77 ]
78 .contains(&data)
79 {
80 colored_line.push(Span::styled(
81 data,
82 TuiStyle::default().fg(Color::Red),
83 ))
84 } else if data.len() == 2 {
85 let style = match data.as_ref() {
86 "10" => TuiStyle::default().fg(Color::Yellow),
88 "11" => TuiStyle::default().fg(Color::Magenta),
90 "12" => TuiStyle::default().fg(Color::Blue),
92 "13" => TuiStyle::default().fg(Color::Green),
94 _ => TuiStyle::default().fg(Color::Red),
95 };
96 colored_line.push(Span::styled(data, style))
97 } else {
98 for c in data.chars().map(String::from) {
99 let style = match c.as_ref() {
100 "?" | "q" | "-" => {
102 TuiStyle::default().fg(Color::DarkGray)
103 }
104 "n" => TuiStyle::default().fg(Color::Red),
106 "m" => TuiStyle::default().fg(Color::Blue),
108 "f" => TuiStyle::default().fg(Color::Magenta),
110 "u" | "h" => TuiStyle::default().fg(Color::Green),
112 "s" => TuiStyle::default().fg(Color::LightGreen),
114 "c" => TuiStyle::default().fg(Color::LightBlue),
116 "e" => TuiStyle::default().fg(Color::Yellow),
118 "a" | "!" => {
120 TuiStyle::default().fg(Color::LightRed)
121 }
122 _ => TuiStyle::default(),
123 };
124 colored_line.push(Span::styled(c, style))
125 }
126 }
127 let data = line[second_bracket..].to_string();
128 if data.find('/') == Some(9) {
130 colored_line.push(Span::styled(
131 data.chars().next().unwrap_or_default().to_string(),
132 highlight_style,
133 ));
134 colored_line.push(Span::styled(
135 data[1..9].to_string(),
136 TuiStyle::default().fg(Color::Cyan),
137 ));
138 colored_line.push(Span::styled(
139 "/",
140 TuiStyle::default().fg(Color::DarkGray),
141 ));
142 colored_line.push(Span::styled(
143 data[10..].to_string(),
144 highlight_style,
145 ));
146 } else if let (Some(first_arrow), Some(second_arrow)) =
148 (data.rfind('<'), data.rfind('>'))
149 {
150 colored_line.push(Span::styled(
151 data[..first_arrow].to_string(),
152 highlight_style,
153 ));
154 colored_line.push(Span::styled(
155 "<",
156 TuiStyle::default().fg(Color::DarkGray),
157 ));
158 colored_line.push(Span::styled(
159 data[first_arrow + 1..second_arrow].to_string(),
160 TuiStyle::default().fg(Color::Cyan),
161 ));
162 colored_line.push(Span::styled(
163 ">",
164 TuiStyle::default().fg(Color::DarkGray),
165 ));
166 colored_line.push(Span::styled(
167 data[second_arrow + 1..].to_string(),
168 highlight_style,
169 ));
170 } else {
172 colored_line.push(Span::styled(data, highlight_style));
173 }
174 Line::from(colored_line)
175 } else {
177 Line::from(vec![Span::styled(
178 line.to_string(),
179 highlight_style,
180 )])
181 },
182 )
183 }
184 Text::from(row)
185}
186
187pub fn get_colored_info(info: &str, color: Color) -> Text<'_> {
194 Text::from(
195 info.lines()
196 .map(|v| {
197 let mut values = v.split(':').collect::<Vec<&str>>();
198 Line::from(if values.len() >= 2 && !v.starts_with(' ') {
199 vec![
200 Span::styled(
201 values[0],
202 TuiStyle::default().fg(Color::Reset),
203 ),
204 Span::styled(
205 ":",
206 TuiStyle::default().fg(Color::DarkGray),
207 ),
208 Span::styled(
209 values.drain(1..).collect::<Vec<&str>>().join(":"),
210 TuiStyle::default().fg(color),
211 ),
212 ]
213 } else {
214 vec![Span::styled(v, TuiStyle::default().fg(Color::Reset))]
215 })
216 })
217 .collect::<Vec<Line>>(),
218 )
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224 use pretty_assertions::assert_eq;
225 use std::borrow::Cow::Borrowed;
226 #[test]
227 fn test_app_style() {
228 let row_data = r#"
229[sc--] rsa2048/C4B2D24CF87CD188C79D00BB485B7C52E9EC0DC6
230 └─(2020-07-29)
231 "#
232 .to_string()
233 .lines()
234 .map(String::from)
235 .collect::<Vec<String>>();
236 assert_eq!(
237 Text {
238 lines: vec![
239 Line {
240 spans: vec![Span {
241 content: Borrowed(""),
242 style: TuiStyle::default(),
243 }],
244 ..Default::default()
245 },
246 Line {
247 spans: vec![
248 Span {
249 content: Borrowed("["),
250 style: TuiStyle::default(),
251 },
252 Span {
253 content: Borrowed("s"),
254 style: TuiStyle {
255 fg: Some(Color::LightGreen),
256 ..TuiStyle::default()
257 },
258 },
259 Span {
260 content: Borrowed("c"),
261 style: TuiStyle {
262 fg: Some(Color::LightBlue),
263 ..TuiStyle::default()
264 },
265 },
266 Span {
267 content: Borrowed("-"),
268 style: TuiStyle {
269 fg: Some(Color::DarkGray),
270 ..TuiStyle::default()
271 },
272 },
273 Span {
274 content: Borrowed("-"),
275 style: TuiStyle {
276 fg: Some(Color::DarkGray),
277 ..TuiStyle::default()
278 },
279 },
280 Span {
281 content: Borrowed("]"),
282 style: TuiStyle::default(),
283 },
284 Span {
285 content: Borrowed(" rsa2048"),
286 style: TuiStyle {
287 fg: Some(Color::Cyan),
288 ..TuiStyle::default()
289 },
290 },
291 Span {
292 content: Borrowed("/"),
293 style: TuiStyle {
294 fg: Some(Color::DarkGray),
295 ..TuiStyle::default()
296 },
297 },
298 Span {
299 content: Borrowed(
300 "C4B2D24CF87CD188C79D00BB485B7C52E9EC0DC6"
301 ),
302 style: TuiStyle::default(),
303 },
304 ],
305 ..Default::default()
306 },
307 Line {
308 spans: vec![Span {
309 content: Borrowed(" └─(2020-07-29)"),
310 style: TuiStyle::default(),
311 }],
312 ..Default::default()
313 },
314 Line {
315 spans: vec![Span {
316 content: Borrowed("\t\t"),
317 style: TuiStyle::default(),
318 }],
319 ..Default::default()
320 },
321 ],
322 ..Default::default()
323 },
324 get_colored_table_row(&row_data, false)
325 );
326 let row_data = r#"
327[u] kmon releases <kmonlinux@protonmail.com>
328 ├─[13] selfsig (2020-07-29)
329 ├─][ test
330 └─[10] B928720AEC532117 orhun <orhunparmaksiz@gmail.com> (2020-07-29)
331 "#
332 .to_string()
333 .lines()
334 .map(String::from)
335 .collect::<Vec<String>>();
336 assert_eq!(
337 Text {
338 lines: vec![
339 Line {
340 spans: vec![Span {
341 content: Borrowed(""),
342 style: TuiStyle::default(),
343 }],
344 ..Default::default()
345 },
346 Line {
347 spans: vec![
348 Span {
349 content: Borrowed("["),
350 style: TuiStyle::default(),
351 },
352 Span {
353 content: Borrowed("u"),
354 style: TuiStyle {
355 fg: Some(Color::Green),
356 ..TuiStyle::default()
357 },
358 },
359 Span {
360 content: Borrowed("] kmon releases "),
361 style: TuiStyle::default(),
362 },
363 Span {
364 content: Borrowed("<"),
365 style: TuiStyle {
366 fg: Some(Color::DarkGray),
367 ..TuiStyle::default()
368 },
369 },
370 Span {
371 content: Borrowed("kmonlinux@protonmail.com"),
372 style: TuiStyle {
373 fg: Some(Color::Cyan),
374 ..TuiStyle::default()
375 },
376 },
377 Span {
378 content: Borrowed(">"),
379 style: TuiStyle {
380 fg: Some(Color::DarkGray),
381 ..TuiStyle::default()
382 },
383 },
384 Span {
385 content: Borrowed(""),
386 style: TuiStyle::default(),
387 },
388 ],
389 ..Default::default()
390 },
391 Line {
392 spans: vec![
393 Span {
394 content: Borrowed("\t├─["),
395 style: TuiStyle::default(),
396 },
397 Span {
398 content: Borrowed("13"),
399 style: TuiStyle {
400 fg: Some(Color::Green),
401 ..TuiStyle::default()
402 },
403 },
404 Span {
405 content: Borrowed("] selfsig (2020-07-29)"),
406 style: TuiStyle::default(),
407 },
408 ],
409 ..Default::default()
410 },
411 Line {
412 spans: vec![Span {
413 content: Borrowed("\t├─][ test"),
414 style: TuiStyle::default(),
415 }],
416 ..Default::default()
417 },
418 Line {
419 spans: vec![
420 Span {
421 content: Borrowed("\t└─["),
422 style: TuiStyle::default(),
423 },
424 Span {
425 content: Borrowed("10"),
426 style: TuiStyle {
427 fg: Some(Color::Yellow),
428 ..TuiStyle::default()
429 },
430 },
431 Span {
432 content: Borrowed("] B928720AEC532117 orhun "),
433 style: TuiStyle::default(),
434 },
435 Span {
436 content: Borrowed("<"),
437 style: TuiStyle {
438 fg: Some(Color::DarkGray),
439 ..TuiStyle::default()
440 },
441 },
442 Span {
443 content: Borrowed("orhunparmaksiz@gmail.com"),
444 style: TuiStyle {
445 fg: Some(Color::Cyan),
446 ..TuiStyle::default()
447 },
448 },
449 Span {
450 content: Borrowed(">"),
451 style: TuiStyle {
452 fg: Some(Color::DarkGray),
453 ..TuiStyle::default()
454 },
455 },
456 Span {
457 content: Borrowed(" (2020-07-29)"),
458 style: TuiStyle::default(),
459 },
460 ],
461 ..Default::default()
462 },
463 Line {
464 spans: vec![Span {
465 content: Borrowed("\t\t\t\t"),
466 style: TuiStyle::default(),
467 }],
468 ..Default::default()
469 },
470 ],
471 ..Default::default()
472 },
473 get_colored_table_row(&row_data, false)
474 );
475 assert_eq!(
476 Text {
477 lines: vec![
478 Line {
479 spans: vec![
480 Span {
481 content: Borrowed("test"),
482 style: TuiStyle {
483 fg: Some(Color::Reset),
484 ..TuiStyle::default()
485 },
486 },
487 Span {
488 content: Borrowed(":"),
489 style: TuiStyle {
490 fg: Some(Color::DarkGray),
491 ..TuiStyle::default()
492 },
493 },
494 Span {
495 content: Borrowed(" xyz "),
496 style: TuiStyle {
497 fg: Some(Color::LightRed),
498 ..TuiStyle::default()
499 },
500 },
501 ],
502 ..Default::default()
503 },
504 Line {
505 spans: vec![
506 Span {
507 content: Borrowed("test2"),
508 style: TuiStyle {
509 fg: Some(Color::Reset),
510 ..TuiStyle::default()
511 },
512 },
513 Span {
514 content: Borrowed(":"),
515 style: TuiStyle {
516 fg: Some(Color::DarkGray),
517 ..TuiStyle::default()
518 },
519 },
520 Span {
521 content: Borrowed(" abc"),
522 style: TuiStyle {
523 fg: Some(Color::LightRed),
524 ..TuiStyle::default()
525 },
526 },
527 ],
528 ..Default::default()
529 },
530 Line {
531 spans: vec![Span {
532 content: Borrowed(" skip this line"),
533 style: TuiStyle {
534 fg: Some(Color::Reset),
535 ..TuiStyle::default()
536 },
537 }],
538 ..Default::default()
539 },
540 Line {
541 spans: vec![Span {
542 content: Borrowed("reset"),
543 style: TuiStyle {
544 fg: Some(Color::Reset),
545 ..TuiStyle::default()
546 },
547 }],
548 ..Default::default()
549 },
550 ],
551 ..Default::default()
552 },
553 get_colored_info(
554 "test: xyz \n\
555 test2: abc\n \
556 skip this line\n\
557 reset",
558 Color::LightRed
559 )
560 )
561 }
562}