1use ratatui::style::{Color, Modifier, Style};
2use ratatui::text::{Line, Span, Text};
3
4pub struct PreviewDoc {
5 lines: Vec<Line<'static>>,
6}
7
8impl PreviewDoc {
9 pub fn new() -> Self {
10 Self { lines: Vec::new() }
11 }
12
13 pub fn push_line(&mut self, line: Line<'static>) {
14 self.lines.push(line);
15 }
16
17 pub fn push_section(&mut self, title: &'static str) {
18 self.lines.push(Line::from(vec![Span::styled(
19 title,
20 Style::default()
21 .fg(Color::Yellow)
22 .add_modifier(Modifier::BOLD),
23 )]));
24 }
25
26 pub fn push_field<T: Into<String>>(&mut self, label: &str, value: T, value_color: Color) {
27 self.lines.push(Line::from(vec![
28 Span::styled(
29 format!(" {}: ", label),
30 Style::default().fg(Color::DarkGray),
31 ),
32 Span::styled(value.into(), Style::default().fg(value_color)),
33 ]));
34 }
35
36 pub fn push_indexed(&mut self, index: usize, text: String) {
37 self.lines.push(Line::from(vec![
38 Span::styled(
39 format!(" {:2}: ", index),
40 Style::default().fg(Color::DarkGray),
41 ),
42 Span::styled(text, Style::default().fg(Color::White)),
43 ]));
44 }
45
46 pub fn push_muted_italic<T: Into<String>>(&mut self, text: T) {
47 self.lines.push(Line::from(vec![Span::styled(
48 text.into(),
49 Style::default()
50 .fg(Color::DarkGray)
51 .add_modifier(Modifier::ITALIC),
52 )]));
53 }
54
55 pub fn push_blank_line(&mut self) {
56 self.lines.push(Line::from(""));
57 }
58
59 pub fn into_text(self) -> Text<'static> {
60 Text::from(self.lines)
61 }
62}
63
64pub fn format_file_size(size: u64) -> String {
65 const KB: u64 = 1024;
66 const MB: u64 = KB * 1024;
67 const GB: u64 = MB * 1024;
68
69 if size >= GB {
70 format!("{:.2} GB ({} bytes)", size as f64 / GB as f64, size)
71 } else if size >= MB {
72 format!("{:.2} MB ({} bytes)", size as f64 / MB as f64, size)
73 } else if size >= KB {
74 format!("{:.2} KB ({} bytes)", size as f64 / KB as f64, size)
75 } else {
76 format!("{} bytes", size)
77 }
78}
79
80pub fn format_unix_timestamp(secs: u64) -> String {
81 let days_since_epoch = secs / 86400;
82 let time_of_day = secs % 86400;
83 let hours = time_of_day / 3600;
84 let minutes = (time_of_day % 3600) / 60;
85
86 let mut year = 1970;
87 let mut remaining_days = days_since_epoch as i64;
88
89 loop {
90 let days_in_year = if is_leap_year(year) { 366 } else { 365 };
91 if remaining_days < days_in_year {
92 break;
93 }
94 remaining_days -= days_in_year;
95 year += 1;
96 }
97
98 let days_in_months: [i64; 12] = if is_leap_year(year) {
99 [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
100 } else {
101 [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
102 };
103
104 let mut month = 1;
105 for days in &days_in_months {
106 if remaining_days < *days {
107 break;
108 }
109 remaining_days -= *days;
110 month += 1;
111 }
112
113 let day = remaining_days + 1;
114 format!(
115 "{:04}-{:02}-{:02} {:02}:{:02}",
116 year, month, day, hours, minutes
117 )
118}
119
120fn is_leap_year(year: i64) -> bool {
121 (year % 4 == 0 && year % 100 != 0) || year % 400 == 0
122}