1use crossterm::{
2 cursor, execute,
3 style::{Color, Print, PrintStyledContent, Stylize},
4 terminal::{Clear, ClearType},
5};
6use std::io::{stdout, Write};
7
8pub struct Table {
9 title: String,
10 rows: i16,
11 columns: i16,
12 data: Vec<Vec<String>>,
13 border_color: Color,
14 title_color: Color,
15 data_color: Color,
16}
17
18impl Table {
19 pub fn new(
20 title: String,
21 rows: i16,
22 border_color: Option<Color>,
23 title_color: Option<Color>,
24 data: Option<Vec<Vec<String>>>,
25 ) -> Self {
26 let data = data.unwrap_or_else(|| vec![vec!["".to_string(); 2 as usize]; rows as usize]);
27
28 Table {
29 title,
30 rows,
31 columns: 2,
32 border_color: border_color.unwrap_or(Color::Blue),
33 title_color: title_color.unwrap_or(Color::White),
34 data,
35 data_color: Color::White, }
37 }
38
39 pub fn with_column_count_override(mut self, columns: i16) -> Self {
42 self.columns = columns;
43 for row in &mut self.data {
44 row.resize(columns as usize, "".to_string());
45 }
46 self
47 }
48
49 pub fn set_cell(&mut self, row: i16, column: i16, value: String) {
51 if row >= 0 && row < self.rows && column >= 0 && column < self.columns {
52 self.data[row as usize][column as usize] = value;
53 }
54 }
55
56 fn calculate_column_widths(&self) -> Vec<usize> {
58 let mut column_widths = vec![0; self.columns as usize];
59
60 for col in 0..self.columns {
61 let mut max_len = 0;
62 for row in 0..self.rows {
63 let cell_value = &self.data[row as usize][col as usize];
64 max_len = max_len.max(cell_value.len());
65 }
66 column_widths[col as usize] = max_len;
67 }
68
69 column_widths
70 }
71
72 pub fn render(&self) -> Result<(), Box<dyn std::error::Error>> {
73 let mut stdout = stdout();
74 let column_widths = self.calculate_column_widths(); let total_width: usize = column_widths.iter().sum::<usize>() + (self.columns as usize - 1); execute!(stdout, cursor::MoveToColumn(0))?;
78 execute!(stdout, Clear(ClearType::UntilNewLine))?;
79 execute!(
80 stdout,
81 PrintStyledContent(format!("{: <width$}", self.title, width = total_width).with(self.title_color))
82 )?;
83 execute!(stdout, Print("\n"))?;
84
85 execute!(stdout, PrintStyledContent("┌".with(self.border_color)))?;
87 for (i, width) in column_widths.iter().enumerate() {
88 execute!(stdout, PrintStyledContent(format!("{:─<width$}", "", width = width).with(self.border_color)))?;
89 if i < self.columns as usize - 1 {
90 execute!(stdout, PrintStyledContent("┬".with(self.border_color)))?;
91 }
92 }
93 execute!(stdout, PrintStyledContent("┐\n".with(self.border_color)))?;
94
95 for row in 0..self.rows {
97 execute!(stdout, PrintStyledContent("│".with(self.border_color)))?;
98 for (col, width) in column_widths.iter().enumerate() {
99 let cell_value = &self.data[row as usize][col];
100 let padded_value = format!("{:<width$}", cell_value, width = width);
101 execute!(stdout, PrintStyledContent(padded_value.with(self.data_color)))?;
102 execute!(stdout, PrintStyledContent("│".with(self.border_color)))?;
103 }
104 execute!(stdout, Print("\n"))?;
105
106 if row < self.rows - 1 {
107 execute!(stdout, PrintStyledContent("├".with(self.border_color)))?;
108 for (i, width) in column_widths.iter().enumerate() {
109 execute!(stdout, PrintStyledContent(format!("{:─<width$}", "", width = width).with(self.border_color)))?;
110 if i < self.columns as usize - 1 {
111 execute!(stdout, PrintStyledContent("┼".with(self.border_color)))?;
112 }
113 }
114 execute!(stdout, PrintStyledContent("┤\n".with(self.border_color)))?;
115 }
116 }
117
118 execute!(stdout, PrintStyledContent("└".with(self.border_color)))?;
120 for (i, width) in column_widths.iter().enumerate() {
121 execute!(stdout, PrintStyledContent(format!("{:─<width$}", "", width = width).with(self.border_color)))?;
122 if i < self.columns as usize - 1 {
123 execute!(stdout, PrintStyledContent("┴".with(self.border_color)))?;
124 }
125 }
126 execute!(stdout, PrintStyledContent("┘\n".with(self.border_color)))?;
127
128 stdout.flush()?;
129
130 Ok(())
131 }
132}