tprint/
tprint.rs

1use std::{fmt, io};
2use std::cell::RefCell;
3use std::rc::Rc;
4
5use crate::column::{TPrintColumn, TPrintAlign};
6use crate::output::{TPrintOutput, TPrintOutputStdout};
7use crate::borders::{TPrintBorders, TPrintBordersASCII, TPrintBordersType};
8use crate::utils::get_string_width;
9
10type TPrintOutputRef = Rc<RefCell<dyn TPrintOutput>>;
11type TPrintBordersRef = Rc<RefCell<dyn TPrintBorders>>;
12
13/// A struct to store the table data
14pub struct TPrint {
15    output: TPrintOutputRef,
16    borders: TPrintBordersRef,
17    show_borders: bool,
18    show_headers: bool,
19    spaces_left: usize,
20    spaces_between: usize,
21    columns: Vec<TPrintColumn>,
22    current_column_id: usize,
23}
24
25impl TPrint {
26    /// Creates a new TPrint object with the default ASCII borders and default output to stdout.
27    pub fn new(show_borders: bool, show_headers: bool, spaces_left: usize, extra_spaces_between: usize) -> Self {
28        TPrint {
29            output: Rc::new(RefCell::new(TPrintOutputStdout {})),
30            borders: Rc::new(RefCell::new(TPrintBordersASCII {})),
31            show_borders,
32            show_headers,
33            spaces_left,
34            spaces_between: extra_spaces_between,
35            columns: Vec::new(),
36            current_column_id: 0,
37        }
38    }
39    /// Creates a new TPrint object with the default ASCII borders and the specified output.
40    pub fn new_with_output(output: TPrintOutputRef, show_borders: bool, show_headers: bool, spaces_left: usize, extra_spaces_between: usize) -> Self {
41        TPrint {
42            output,
43            borders: Rc::new(RefCell::new(TPrintBordersASCII {})),
44            show_borders,
45            show_headers,
46            spaces_left,
47            spaces_between: extra_spaces_between,
48            columns: Vec::new(),
49            current_column_id: 0,
50        }
51    }
52
53    /// Creates a new TPrint object with the specified borders and default output to stdout.
54    pub fn new_with_borders(borders: TPrintBordersRef, show_borders: bool, show_headers: bool, spaces_left: usize, extra_spaces_between: usize) -> Self {
55        TPrint {
56            output: Rc::new(RefCell::new(TPrintOutputStdout {})),
57            borders,
58            show_borders,
59            show_headers,
60            spaces_left,
61            spaces_between: extra_spaces_between,
62            columns: Vec::new(),
63            current_column_id: 0,
64        }
65    }
66
67    /// Creates a new TPrint object with the specified borders and output.
68    pub fn new_with_borders_output(borders: TPrintBordersRef, output: TPrintOutputRef, show_borders: bool, show_headers: bool, spaces_left: usize, extra_spaces_between: usize) -> Self {
69        TPrint {
70            output,
71            borders,
72            show_borders,
73            show_headers,
74            spaces_left,
75            spaces_between: extra_spaces_between,
76            columns: Vec::new(),
77            current_column_id: 0,
78        }
79    }
80
81    /// Adds a new column with the specified caption text, caption alignment and cells alignment.
82    /// Columns must be defined before adding cell data to the table.
83    pub fn column_add(&mut self, caption: &str, caption_align: TPrintAlign, cell_align: TPrintAlign) -> &mut Self {
84        self.columns.push(TPrintColumn::new(caption, caption_align, cell_align));
85        self
86    }
87
88    /// Adds a cell data to the table. The data is added to columns in a consecutive order.
89    ///
90    /// # Examples
91    ///
92    /// ## Basic Usage
93    /// ```
94    /// use tprint::{TPrint, TPrintAlign};
95    /// let mut tp = TPrint::new(true, true, 0, 1);
96    /// tp.column_add("Name", TPrintAlign::Center, TPrintAlign::Left);
97    /// tp.column_add("Count", TPrintAlign::Center, TPrintAlign::Left);
98    /// tp.add_data("test");
99    /// tp.add_data(42);
100    /// tp.print()?;
101    /// ```
102    ///
103    /// ## Chained Usage
104    /// ```
105    /// use tprint::{TPrint, TPrintAlign};
106    /// TPrint::new(true, true, 0, 1).
107    ///     column_add("Name", TPrintAlign::Center, TPrintAlign::Left).
108    ///     column_add("Count", TPrintAlign::Center, TPrintAlign::Left).
109    ///     add_data("test").add_data(42).
110    ///     print()?;
111    /// ```
112    pub fn add_data<T: fmt::Display>(&mut self, value: T) -> &mut Self {
113        let column_id = self.current_column_id;
114        if let Some(column) = self.columns.get_mut(column_id) {
115            column.add_data(value);
116
117            if self.current_column_id + 1 >= self.columns.len() {
118                self.current_column_id = 0;
119            } else {
120                self.current_column_id += 1;
121            }
122        }
123        self
124    }
125
126    fn print_horizonal_border(&self, left: &TPrintBordersType, right: &TPrintBordersType, middle: &TPrintBordersType, line: &TPrintBordersType) -> io::Result<()> {
127        if !self.show_borders {
128            self.output.borrow_mut().print_str(self.borders.borrow().get_border(&TPrintBordersType::NewLine))?;
129            return Ok(());
130        }
131
132        if self.spaces_left > 0 {
133            self.output.borrow_mut().print_str(&self.borders.borrow().get_border(&TPrintBordersType::WhiteSpace).repeat(self.spaces_left))?;
134        }
135
136        self.output.borrow_mut().print_str(self.borders.borrow().get_border(left))?;
137
138        for i in 0..self.columns.len() {
139            let c = &self.columns[i];
140
141            self.output.borrow_mut().print_str(&self.borders.borrow().get_border(line).repeat(c.get_max_width() + 2 * self.spaces_between))?;
142
143            if i < self.columns.len() - 1 {
144                self.output.borrow_mut().print_str(self.borders.borrow().get_border(middle))?;
145            }
146        }
147
148        self.output.borrow_mut().print_str(self.borders.borrow().get_border(right))?;
149        self.output.borrow_mut().print_str(self.borders.borrow().get_border(&TPrintBordersType::NewLine))?;
150
151        Ok(())
152    }
153
154    fn print_left_border(&self, left_border: &TPrintBordersType) -> io::Result<()> {
155        if !self.show_borders {
156            return Ok(());
157        }
158        self.output.borrow_mut().print_str(self.borders.borrow().get_border(left_border))?;
159        Ok(())
160    }
161
162    fn print_right_border(&self, right_border: &TPrintBordersType) -> io::Result<()> {
163        if !self.show_borders {
164            return Ok(());
165        }
166        self.output.borrow_mut().print_str(self.borders.borrow().get_border(right_border))?;
167        self.output.borrow_mut().print_str(self.borders.borrow().get_border(&TPrintBordersType::NewLine))?;
168        Ok(())
169    }
170
171    fn print_internal_border(&self, v_border: &TPrintBordersType) -> io::Result<()> {
172        if !self.show_borders {
173            return Ok(());
174        }
175        self.output.borrow_mut().print_str(self.borders.borrow().get_border(v_border))?;
176        Ok(())
177    }
178
179    fn print_cell(&self, value: &str, max_width: usize, align: &TPrintAlign, whitespace: &str) -> io::Result<()> {
180        let left_spaces;
181        let right_spaces;
182
183        let value_len = get_string_width(value);
184
185        match align {
186            TPrintAlign::Left => {
187                left_spaces = self.spaces_between;
188                right_spaces = (self.spaces_between * 2 + max_width) - (left_spaces + value_len);
189            }
190            TPrintAlign::Center => {
191                left_spaces = self.spaces_between + (max_width - value_len) / 2;
192                right_spaces = (self.spaces_between * 2 + max_width) - (left_spaces + value_len);
193            }
194            TPrintAlign::Right => {
195                right_spaces = self.spaces_between;
196                left_spaces = (self.spaces_between * 2 + max_width) - (right_spaces + value_len);
197            }
198        }
199
200        self.output.borrow_mut().print_str(&whitespace.repeat(left_spaces))?;
201        self.output.borrow_mut().print_str(value)?;
202        self.output.borrow_mut().print_str(&whitespace.repeat(right_spaces))?;
203        Ok(())
204    }
205
206    /// Prints the table to the output.
207    pub fn print(&self) -> io::Result<()> {
208        let total_rows: usize = self.columns.iter().map(|c| c.get_rows_count()).max().unwrap_or(0);
209
210        let border = self.borders.borrow();
211        let whitespace = border.get_border(&TPrintBordersType::WhiteSpace);
212
213        self.output.borrow_mut().print_str(border.get_intro(self.show_borders, self.show_headers))?;
214
215        if self.show_headers {
216            self.print_horizonal_border(&TPrintBordersType::HeaderTopLeft, &TPrintBordersType::HeaderTopRight, &TPrintBordersType::HeaderTopMiddle, &TPrintBordersType::HeaderHLine)?;
217
218            if self.spaces_left > 0 {
219                self.output.borrow_mut().print_str(&whitespace.repeat(self.spaces_left))?;
220            }
221
222            self.print_left_border(&TPrintBordersType::HeaderLeftVLine)?;
223
224            for cid in 0..self.columns.len() {
225                let c = &self.columns[cid];
226
227                if cid > 0 {
228                    self.print_internal_border(&TPrintBordersType::HeaderMiddleVLine)?;
229                }
230
231                self.print_cell(c.get_caption(), c.get_max_width(), c.get_caption_align(), whitespace)?;
232            }
233
234            self.print_right_border(&TPrintBordersType::HeaderRightVLine)?;
235
236            self.print_horizonal_border(&TPrintBordersType::HeaderBottomLeft, &TPrintBordersType::HeaderBottomRight, &TPrintBordersType::HeaderBottomMiddle, &TPrintBordersType::HeaderHLine)?;
237
238        } else {
239            self.print_horizonal_border(&TPrintBordersType::TopLeft, &TPrintBordersType::TopRight, &TPrintBordersType::TopMiddle, &TPrintBordersType::TopHLine)?;
240        }
241
242        for r in 0..total_rows {
243            if self.spaces_left > 0 {
244                self.output.borrow_mut().print_str(&whitespace.repeat(self.spaces_left))?;
245            }
246
247            self.print_left_border(&TPrintBordersType::MiddleLeftVLine)?;
248
249            for cid in 0..self.columns.len() {
250                let c = &self.columns[cid];
251                if cid > 0 {
252                    self.print_internal_border(&TPrintBordersType::MiddleMiddleVLine)?;
253                }
254
255                self.print_cell(c.get_str(r), c.get_max_width(), c.get_cell_align(), whitespace)?;
256            }
257            self.print_right_border(&TPrintBordersType::MiddleRightVLine)?;
258
259            if r < total_rows - 1 {
260                self.print_horizonal_border(&TPrintBordersType::MiddleLeft, &TPrintBordersType::MiddleRight, &TPrintBordersType::MiddleMiddle, &TPrintBordersType::MiddleHLine)?;
261            }
262        }
263
264        self.print_horizonal_border(&TPrintBordersType::BottomLeft, &TPrintBordersType::BottomRight, &TPrintBordersType::BottomMiddle, &TPrintBordersType::BottomHLine)?;
265
266        self.output.borrow_mut().print_str(border.get_closing())
267    }
268}