term_data_table/
lib.rs

1//! The purpose of term-table is to make it easy for CLI apps to display data in a table format
2//!# Example
3//! Here is an example of how to create a simple table
4//!```
5//! use term_data_table::{ Table, Cell, TableStyle, Alignment, Row };
6//!
7//! let table = Table::new()
8//!     .with_style(TableStyle::EXTENDED)
9//!     .with_row(Row::new().with_cell(
10//!         Cell::from("This is some centered text")
11//!             .with_alignment(Alignment::Center)
12//!             .with_col_span(2)
13//!     ))
14//!     .with_row(Row::new().with_cell(
15//!         Cell::from("This is left aligned text")
16//!     ).with_cell(
17//!         Cell::from("This is right aligned text")
18//!             .with_alignment(Alignment::Right)
19//!     ))
20//!     .with_row(Row::new().with_cell(
21//!         Cell::from("This is left aligned text")
22//!     ).with_cell(
23//!         Cell::from("This is right aligned text")
24//!             .with_alignment(Alignment::Right)
25//!     ))
26//!     .with_row(Row::new().with_cell(
27//!         Cell::from("This is some really really really really really really really really really that is going to wrap to the next line")
28//!             .with_col_span(2)
29//!     ));
30//!println!("{}", table.fixed_width(80));
31//!```
32//!
33//!### This is the result
34//!
35//!<pre>
36//! ╔═════════════════════════════════════════════════════════════════════════════════╗
37//! ║                            This is some centered text                           ║
38//! ╠════════════════════════════════════════╦════════════════════════════════════════╣
39//! ║ This is left aligned text              ║             This is right aligned text ║
40//! ╠════════════════════════════════════════╬════════════════════════════════════════╣
41//! ║ This is left aligned text              ║             This is right aligned text ║
42//! ╠════════════════════════════════════════╩════════════════════════════════════════╣
43//! ║ This is some really really really really really really really really really tha ║
44//! ║ t is going to wrap to the next line                                             ║
45//! ╚═════════════════════════════════════════════════════════════════════════════════╝
46//!</pre>
47
48#[macro_use]
49extern crate lazy_static;
50
51mod cell;
52mod row;
53mod ser;
54mod style;
55
56pub use crate::{
57    cell::{Alignment, Cell},
58    row::{IntoRow, Row},
59    style::TableStyle,
60};
61// TODO just use a serde deserializer.
62#[doc(inline)]
63pub use term_data_table_derive::IntoRow;
64
65use itertools::Itertools;
66use serde::Serialize;
67use std::{cell::RefCell, collections::HashMap, fmt};
68use terminal_size::terminal_size;
69
70thread_local! {
71    /// Used to calculate the maximum width of table cells.
72    static MAX_WIDTHS_CELL_WIDTHS: RefCell<(ColumnWidths, HashMap<usize, usize>)>
73        = RefCell::new((ColumnWidths::new(), HashMap::new()));
74}
75
76/// Represents the vertical position of a row
77#[derive(Eq, PartialEq, Copy, Clone)]
78pub enum RowPosition {
79    First,
80    Mid,
81    Last,
82}
83
84/// A set of rows containing data
85#[derive(Clone, Debug)]
86pub struct Table<'data> {
87    rows: Vec<Row<'data>>,
88    style: TableStyle,
89    /// Whether or not to vertically separate rows in the table.
90    ///
91    /// Defaults to `true`.
92    pub has_separate_rows: bool,
93    /// Whether the table should have a top border.
94    ///
95    /// Setting `has_separator` to false on the first row will have the same effect as setting this
96    /// to false
97    ///
98    /// Defaults to `true`.
99    pub has_top_border: bool,
100    /// Whether the table should have a bottom border
101    ///
102    /// Defaults to `true`.
103    pub has_bottom_border: bool,
104
105    /// Calculated column widths.
106    column_widths: RefCell<ColumnWidths>,
107    /// Calculated row lines
108    row_lines: RefCell<Vec<usize>>,
109}
110
111impl<'data> Default for Table<'data> {
112    fn default() -> Self {
113        Self {
114            rows: Vec::new(),
115            style: TableStyle::EXTENDED,
116            has_separate_rows: true,
117            has_top_border: true,
118            has_bottom_border: true,
119
120            column_widths: RefCell::new(ColumnWidths::new()),
121            row_lines: RefCell::new(vec![]),
122        }
123    }
124}
125
126impl<'data> Table<'data> {
127    pub fn new() -> Table<'data> {
128        Default::default()
129    }
130
131    pub fn from_rows(rows: Vec<Row<'data>>) -> Table<'data> {
132        Self {
133            rows,
134            ..Default::default()
135        }
136    }
137
138    pub fn from_serde(data: impl IntoIterator<Item = impl Serialize>) -> anyhow::Result<Self> {
139        let mut table = Table::new();
140        for row in data {
141            table.add_row(ser::serialize_row(row)?);
142        }
143        Ok(table)
144    }
145
146    /// Add a row
147    pub fn with_row(mut self, row: Row<'data>) -> Self {
148        self.add_row(row);
149        self
150    }
151
152    /// Add a row
153    pub fn add_row(&mut self, row: Row<'data>) -> &mut Self {
154        self.rows.push(row);
155        self
156    }
157
158    pub fn with_style(mut self, style: TableStyle) -> Self {
159        self.set_style(style);
160        self
161    }
162
163    pub fn set_style(&mut self, style: TableStyle) -> &mut Self {
164        self.style = style;
165        self
166    }
167
168    /*
169    pub fn max_column_width(&self) -> usize {
170        self.max_column_width
171    }
172
173    pub fn set_max_column_width(&mut self, max_column_width: usize) -> &mut Self {
174        self.max_column_width = max_column_width;
175        self
176    }
177
178    pub fn with_max_column_width(mut self, max_column_width: usize) -> Self {
179        self.set_max_column_width(max_column_width);
180        self
181    }
182
183    /// Set the max width of a particular column
184    ///
185    /// Overrides any value set for `max_column_width`.
186    pub fn set_max_width_for_column(&mut self, column_index: usize, max_width: usize) -> &mut Self {
187        self.max_column_widths.insert(column_index, max_width);
188        self
189    }
190
191    pub fn with_max_width_for_column(mut self, column_index: usize, max_width: usize) -> Self {
192        self.set_max_width_for_column(column_index, max_width);
193        self
194    }
195    */
196
197    pub fn has_separate_rows(&self) -> bool {
198        self.has_separate_rows
199    }
200
201    pub fn with_separate_rows(mut self, has_separate_rows: bool) -> Self {
202        self.set_separate_rows(has_separate_rows);
203        self
204    }
205
206    pub fn set_separate_rows(&mut self, has_separate_rows: bool) -> &mut Self {
207        self.has_separate_rows = has_separate_rows;
208        self
209    }
210
211    /// Decide how much space to give each cell and layout the rows.
212    ///
213    /// If no width is given, all cells will be the largest of their contents.
214    ///
215    fn layout(&self, width: Option<usize>) {
216        // We need to know the maxiumum number of columns in a row.
217        let cols = self.rows.iter().map(|row| row.columns()).max().unwrap_or(0);
218        let border_width = self.style.border_width();
219        let mut col_widths = self.column_widths.borrow_mut();
220        col_widths.reset(cols);
221
222        // short-circuit when there are no columns
223        if cols == 0 {
224            return;
225        }
226
227        if let Some(width) = width {
228            // total space available for drawing text
229            let cell_width_total = width - (border_width + 1) * cols;
230
231            MAX_WIDTHS_CELL_WIDTHS.with(|lk| {
232                let (ref mut max_widths, ref mut cell_widths) = &mut *lk.borrow_mut();
233                // reset
234                max_widths.reset(cols);
235                cell_widths.clear();
236
237                // first stash the max space each column will need.
238                for row in self.rows.iter() {
239                    max_widths.fit_row_singleline(row, border_width);
240                }
241
242                // Next, calculate the width we would give each cell if we were splitting space
243                // evenly
244                let cell_width = cell_width_total / cols;
245
246                // Next, find all cells with max width less than the cell width we calculated and
247                // give them their max width
248                for (idx, max_width) in max_widths.iter().enumerate() {
249                    if *max_width < cell_width {
250                        cell_widths.insert(idx, *max_width);
251                    }
252                }
253
254                let remaining_cells = cols - cell_widths.len();
255                let remaining_space =
256                    cell_width_total - cell_widths.values().copied().sum::<usize>();
257                if remaining_cells > 0 {
258                    let cell_width = remaining_space / remaining_cells;
259                    for idx in 0..cols {
260                        cell_widths.entry(idx).or_insert(cell_width);
261                    }
262                }
263
264                col_widths.from_map(cell_widths);
265            });
266        } else {
267            // Give all cells all the space they need.
268            for row in self.rows.iter() {
269                col_widths.fit_row_singleline(row, border_width);
270            }
271        }
272        for row in self.rows.iter() {
273            self.row_lines
274                .borrow_mut()
275                .push(row.layout(&*col_widths, border_width));
276        }
277    }
278
279    /// Write the table out to a formatter.
280    ///
281    /// This method calculates stale state that it needs.
282    ///
283    /// # Params
284    ///  - `view_width` - the width of the viewport we are rendering to, if any. If unspecified,
285    ///    we will assume infinite width.
286    fn render(&self, view_width: Option<usize>, f: &mut fmt::Formatter) -> fmt::Result {
287        self.layout(view_width);
288        if self.rows.is_empty() {
289            return writeln!(f, "<empty table>");
290        }
291        let row_lines = self.row_lines.borrow();
292
293        if self.has_top_border {
294            self.rows[0].render_top_separator(&*self.column_widths.borrow(), &self.style, f)?;
295        }
296        self.rows[0].render_content(&*self.column_widths.borrow(), row_lines[0], &self.style, f)?;
297
298        for (idx, (prev_row, row)) in self.rows.iter().tuple_windows().enumerate() {
299            if self.has_separate_rows {
300                row.render_separator(prev_row, &*self.column_widths.borrow(), &self.style, f)?;
301            }
302
303            let row_lines = self.row_lines.borrow();
304            row.render_content(
305                &*self.column_widths.borrow(),
306                row_lines[idx + 1],
307                &self.style,
308                f,
309            )?;
310        }
311        if self.has_bottom_border {
312            self.rows[self.rows.len() - 1].render_bottom_separator(
313                &*self.column_widths.borrow(),
314                &self.style,
315                f,
316            )?;
317        }
318        Ok(())
319    }
320
321    /// Get the terminal width and use this for the table width.
322    ///
323    /// # Panics
324    ///
325    /// Will panic if it cannot get the terminal width (e.g. because we aren't in a terminal).
326    pub fn for_terminal(&self) -> impl fmt::Display + '_ {
327        match terminal_size().and_then(|v| usize::try_from((v.0).0).ok()) {
328            Some(width) => FixedWidth { table: self, width },
329            None => FixedWidth {
330                table: self,
331                width: usize::MAX,
332            },
333        }
334    }
335
336    /// Use a custom value for the table width
337    pub fn fixed_width(&self, width: usize) -> impl fmt::Display + '_ {
338        FixedWidth { table: self, width }
339    }
340}
341
342struct FixedWidth<'a> {
343    table: &'a Table<'a>,
344    width: usize,
345}
346
347impl fmt::Display for FixedWidth<'_> {
348    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
349        self.table.render(Some(self.width), f)
350    }
351}
352
353impl<'data> fmt::Display for Table<'data> {
354    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
355        self.render(None, f)
356    }
357}
358
359pub fn data_table<'a, R: 'a>(input: impl IntoIterator<Item = &'a R>) -> Table<'a>
360where
361    R: IntoRow,
362{
363    let mut table = Table::new();
364    for row in input {
365        table.add_row(row.into_row());
366    }
367    table
368}
369
370#[derive(Debug, Clone)]
371struct ColumnWidths(Vec<usize>);
372
373impl ColumnWidths {
374    fn new() -> Self {
375        ColumnWidths(vec![])
376    }
377
378    /// Reset all columns to 0 and make sure there are the correct number of columns.
379    fn reset(&mut self, num_cols: usize) {
380        self.0.clear();
381        self.0.resize(num_cols, 0);
382    }
383
384    /// Make our widths fit the given row with all text on a single line.
385    ///
386    /// This is for when we are allowed to use as much space as we want.
387    fn fit_row_singleline(&mut self, row: &Row, border_width: usize) {
388        let mut idx = 0;
389        for cell in row.cells.iter() {
390            if cell.col_span == 1 {
391                self.0[idx] = self.0[idx].max(cell.min_width(true));
392            } else {
393                // space required to fit this cell (taking into account we have some borders to
394                // use).
395                let required_width = cell.min_width(true) - border_width * (cell.col_span - 1);
396                let floor_per_cell = required_width / cell.col_span;
397                // space we need to put somewhere
398                let mut to_fit = required_width % cell.col_span;
399                // split space evenly, with remainder in last space.
400                for i in 0..cell.col_span {
401                    let extra = if to_fit > 0 { 1 } else { 0 };
402                    to_fit = to_fit.saturating_sub(1);
403                    self.0[idx + i] = self.0[idx + 1].max(floor_per_cell + extra);
404                }
405            }
406            idx += cell.col_span;
407        }
408    }
409
410    fn from_map(&mut self, map: &HashMap<usize, usize>) {
411        for (idx, slot) in self.0.iter_mut().enumerate() {
412            *slot = *map.get(&idx).unwrap();
413        }
414    }
415}
416
417impl std::ops::Deref for ColumnWidths {
418    type Target = [usize];
419    fn deref(&self) -> &Self::Target {
420        &self.0
421    }
422}
423
424#[cfg(test)]
425mod test {
426
427    use crate::cell::{Alignment, Cell};
428    use crate::row::Row;
429    use crate::Table;
430    use crate::TableStyle;
431    use pretty_assertions::assert_eq;
432
433    #[test]
434    fn correct_default_padding() {
435        let table = Table::new()
436            .with_separate_rows(false)
437            .with_style(TableStyle::SIMPLE)
438            .with_row(
439                Row::new()
440                    .with_cell(Cell::from("A").with_alignment(Alignment::Center))
441                    .with_cell(Cell::from("B").with_alignment(Alignment::Center)),
442            )
443            .with_row(
444                Row::new()
445                    .with_cell(Cell::from(1.to_string()))
446                    .with_cell(Cell::from("1")),
447            )
448            .with_row(
449                Row::new()
450                    .with_cell(Cell::from(2.to_string()))
451                    .with_cell(Cell::from("10")),
452            )
453            .with_row(
454                Row::new()
455                    .with_cell(Cell::from(3.to_string()))
456                    .with_cell(Cell::from("100")),
457            );
458        let expected = r"+---+-----+
459| A |  B  |
460| 1 | 1   |
461| 2 | 10  |
462| 3 | 100 |
463+---+-----+
464";
465        println!("{}", table);
466        assert_eq!(expected, table.to_string());
467    }
468
469    #[test]
470    fn uneven_center_alignment() {
471        let table = Table::new()
472            .with_separate_rows(false)
473            .with_style(TableStyle::SIMPLE)
474            .with_row(Row::new().with_cell(Cell::from("A").with_alignment(Alignment::Center)))
475            .with_row(Row::new().with_cell(Cell::from(11.to_string())))
476            .with_row(Row::new().with_cell(Cell::from(2.to_string())))
477            .with_row(Row::new().with_cell(Cell::from(3.to_string())));
478        let expected = r"+----+
479| A  |
480| 11 |
481| 2  |
482| 3  |
483+----+
484";
485        println!("{}", table);
486        assert_eq!(expected, table.to_string());
487    }
488
489    #[test]
490    fn uneven_center_alignment_2() {
491        let table = Table::new()
492            .with_separate_rows(false)
493            .with_style(TableStyle::SIMPLE)
494            .with_row(
495                Row::new()
496                    .with_cell(Cell::from("A1").with_alignment(Alignment::Center))
497                    .with_cell(Cell::from("B").with_alignment(Alignment::Center)),
498            );
499        println!("{}", table);
500        let expected = r"+----+---+
501| A1 | B |
502+----+---+
503";
504        assert_eq!(expected, table.to_string());
505    }
506
507    #[test]
508    fn simple_table_style() {
509        let mut table = Table::new().with_style(TableStyle::SIMPLE);
510
511        add_data_to_test_table(&mut table);
512
513        let expected = r"+-----------------------------------------------------------------------------+
514|                         This is some centered text                          |
515+--------------------------------------+--------------------------------------+
516| This is left aligned text            |           This is right aligned text |
517+--------------------------------------+--------------------------------------+
518| This is left aligned text            |           This is right aligned text |
519+--------------------------------------+--------------------------------------+
520| This is some really really really really really really really really        |
521| really that is going to wrap to the next line                               |
522+-----------------------------------------------------------------------------+
523| aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
524| aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa                                     |
525+-----------------------------------------------------------------------------+
526";
527        let table = table.fixed_width(80);
528        println!("{}", table.to_string());
529        assert_eq!(expected, table.to_string());
530    }
531
532    #[test]
533    #[ignore]
534    fn uneven_with_varying_col_span() {
535        let table = Table::new()
536            .with_separate_rows(true)
537            .with_style(TableStyle::SIMPLE)
538            .with_row(
539                Row::new()
540                    .with_cell(Cell::from("A1111111").with_alignment(Alignment::Center))
541                    .with_cell(Cell::from("B").with_alignment(Alignment::Center)),
542            )
543            .with_row(
544                Row::new()
545                    .with_cell(Cell::from(1.to_string()))
546                    .with_cell(Cell::from("1")),
547            )
548            .with_row(
549                Row::new()
550                    .with_cell(Cell::from(2.to_string()))
551                    .with_cell(Cell::from("10")),
552            )
553            .with_row(
554                Row::new()
555                    .with_cell(
556                        Cell::from(3.to_string())
557                            .with_alignment(Alignment::Left)
558                            .with_padding(false),
559                    )
560                    .with_cell(Cell::from("100")),
561            )
562            .with_row(Row::new().with_cell(Cell::from("S").with_alignment(Alignment::Center)));
563        let expected = "+----------+-----+
564| A1111111 |  B  |
565+----------+-----+
566| 1        | 1   |
567+----------+-----+
568| 2        | 10  |
569+----------+-----+
570|\03\0         | 100 |
571+----------+-----+
572|        S       |
573+----------------+
574";
575        println!("{}", table);
576        assert_eq!(expected.trim(), table.to_string().trim());
577    }
578
579    // TODO - The output of this test isn't ideal. There is probably a better way to calculate the
580    // the column/row layout that would improve this
581    #[test]
582    fn uneven_with_varying_col_span_2() {
583        let table = Table::new()
584            .with_separate_rows(false)
585            .with_style(TableStyle::SIMPLE)
586            .with_row(
587                Row::new()
588                    .with_cell(Cell::from("A").with_alignment(Alignment::Center))
589                    .with_cell(Cell::from("B").with_alignment(Alignment::Center)),
590            )
591            .with_row(
592                Row::new()
593                    .with_cell(Cell::from(1.to_string()))
594                    .with_cell(Cell::from("1")),
595            )
596            .with_row(
597                Row::new()
598                    .with_cell(Cell::from(2.to_string()))
599                    .with_cell(Cell::from("10")),
600            )
601            .with_row(
602                Row::new()
603                    .with_cell(Cell::from(3.to_string()))
604                    .with_cell(Cell::from("100")),
605            )
606            .with_row(
607                Row::new().with_cell(
608                    Cell::from("Spanner")
609                        .with_col_span(2)
610                        .with_alignment(Alignment::Center),
611                ),
612            );
613        let expected = "+-----+-----+
614|  A  |  B  |
615| 1   | 1   |
616| 2   | 10  |
617| 3   | 100 |
618|  Spanner  |
619+-----------+
620";
621        println!("{}", table);
622        assert_eq!(expected.trim(), table.to_string().trim());
623    }
624
625    /*
626        #[test]
627        fn extended_table_style_wrapped() {
628            let table = Table::new()
629                .with_max_column_width(40)
630                .with_max_widths_for_columns([(0, 1), (1, 1)])
631
632            .with_style ( TableStyle::EXTENDED)
633
634            .with_row(Row::new(vec![Cell::new_with_alignment(
635                "This is some centered text",
636                2,
637                Alignment::Center,
638            )]))
639
640            .with_row(Row::new(vec![
641                Cell::new("This is left aligned text"),
642                Cell::new_with_alignment("This is right aligned text", 1, Alignment::Right),
643            ]))
644
645            .with_row(Row::new(vec![
646                Cell::new("This is left aligned text"),
647                Cell::new_with_alignment("This is right aligned text", 1, Alignment::Right),
648            ]))
649
650            .with_row(Row::new(vec![
651                Cell::new_with_col_span("This is some really really really really really really really really really that is going to wrap to the next line\n1\n2", 2),
652            ]));
653
654            let expected = r"╔═══════╗
655    ║ This  ║
656    ║ is so ║
657    ║ me ce ║
658    ║ ntere ║
659    ║ d tex ║
660    ║   t   ║
661    ╠═══╦═══╣
662    ║ T ║ T ║
663    ║ h ║ h ║
664    ║ i ║ i ║
665    ║ s ║ s ║
666    ║   ║   ║
667    ║ i ║ i ║
668    ║ s ║ s ║
669    ║   ║   ║
670    ║ l ║ r ║
671    ║ e ║ i ║
672    ║ f ║ g ║
673    ║ t ║ h ║
674    ║   ║ t ║
675    ║ a ║   ║
676    ║ l ║ a ║
677    ║ i ║ l ║
678    ║ g ║ i ║
679    ║ n ║ g ║
680    ║ e ║ n ║
681    ║ d ║ e ║
682    ║   ║ d ║
683    ║ t ║   ║
684    ║ e ║ t ║
685    ║ x ║ e ║
686    ║ t ║ x ║
687    ║   ║ t ║
688    ╠═══╬═══╣
689    ║ T ║ T ║
690    ║ h ║ h ║
691    ║ i ║ i ║
692    ║ s ║ s ║
693    ║   ║   ║
694    ║ i ║ i ║
695    ║ s ║ s ║
696    ║   ║   ║
697    ║ l ║ r ║
698    ║ e ║ i ║
699    ║ f ║ g ║
700    ║ t ║ h ║
701    ║   ║ t ║
702    ║ a ║   ║
703    ║ l ║ a ║
704    ║ i ║ l ║
705    ║ g ║ i ║
706    ║ n ║ g ║
707    ║ e ║ n ║
708    ║ d ║ e ║
709    ║   ║ d ║
710    ║ t ║   ║
711    ║ e ║ t ║
712    ║ x ║ e ║
713    ║ t ║ x ║
714    ║   ║ t ║
715    ╠═══╩═══╣
716    ║ This  ║
717    ║ is so ║
718    ║ me re ║
719    ║ ally  ║
720    ║ reall ║
721    ║ y rea ║
722    ║ lly r ║
723    ║ eally ║
724    ║  real ║
725    ║ ly re ║
726    ║ ally  ║
727    ║ reall ║
728    ║ y rea ║
729    ║ lly r ║
730    ║ eally ║
731    ║  that ║
732    ║  is g ║
733    ║ oing  ║
734    ║ to wr ║
735    ║ ap to ║
736    ║  the  ║
737    ║ next  ║
738    ║ line  ║
739    ║ 1     ║
740    ║ 2     ║
741    ╚═══════╝
742    ";
743            println!("{}", table.render());
744            assert_eq!(expected, table.render());
745        }
746
747            #[test]
748            fn elegant_table_style() {
749                let mut table = Table::new();
750                table.style = TableStyle::elegant();
751
752                add_data_to_test_table(&mut table);
753
754                let expected = r"╔─────────────────────────────────────────────────────────────────────────────────╗
755        │                            This is some centered text                           │
756        ╠────────────────────────────────────────╦────────────────────────────────────────╣
757        │ This is left aligned text              │             This is right aligned text │
758        ╠────────────────────────────────────────┼────────────────────────────────────────╣
759        │ This is left aligned text              │             This is right aligned text │
760        ╠────────────────────────────────────────╩────────────────────────────────────────╣
761        │ This is some really really really really really really really really really tha │
762        │ t is going to wrap to the next line                                             │
763        ╚─────────────────────────────────────────────────────────────────────────────────╝
764        ";
765                println!("{}", table.render());
766                assert_eq!(expected, table.render());
767            }
768
769            #[test]
770            fn thin_table_style() {
771                let mut table = Table::new();
772                table.style = TableStyle::thin();
773
774                add_data_to_test_table(&mut table);
775
776                let expected = r"┌─────────────────────────────────────────────────────────────────────────────────┐
777        │                            This is some centered text                           │
778        ├────────────────────────────────────────┬────────────────────────────────────────┤
779        │ This is left aligned text              │             This is right aligned text │
780        ├────────────────────────────────────────┼────────────────────────────────────────┤
781        │ This is left aligned text              │             This is right aligned text │
782        ├────────────────────────────────────────┴────────────────────────────────────────┤
783        │ This is some really really really really really really really really really tha │
784        │ t is going to wrap to the next line                                             │
785        └─────────────────────────────────────────────────────────────────────────────────┘
786        ";
787                println!("{}", table.render());
788                assert_eq!(expected, table.render());
789            }
790
791            #[test]
792            fn rounded_table_style() {
793                let mut table = Table::new();
794
795                table.style = TableStyle::rounded();
796
797                add_data_to_test_table(&mut table);
798
799                let expected = r"╭─────────────────────────────────────────────────────────────────────────────────╮
800        │                            This is some centered text                           │
801        ├────────────────────────────────────────┬────────────────────────────────────────┤
802        │ This is left aligned text              │             This is right aligned text │
803        ├────────────────────────────────────────┼────────────────────────────────────────┤
804        │ This is left aligned text              │             This is right aligned text │
805        ├────────────────────────────────────────┴────────────────────────────────────────┤
806        │ This is some really really really really really really really really really tha │
807        │ t is going to wrap to the next line                                             │
808        ╰─────────────────────────────────────────────────────────────────────────────────╯
809        ";
810                println!("{}", table.render());
811                assert_eq!(expected, table.render());
812            }
813
814            #[test]
815            fn complex_table() {
816                let mut table = Table::new();
817
818                table.add_row(Row::new(vec![
819                    Cell::new_with_col_span("Col*1*Span*2", 2),
820                    Cell::new("Col 2 Span 1"),
821                    Cell::new_with_col_span("Col 3 Span 2", 2),
822                    Cell::new("Col 4 Span 1"),
823                ]));
824                table.add_row(Row::new(vec![
825                    Cell::new("Col 1 Span 1"),
826                    Cell::new("Col 2 Span 1"),
827                    Cell::new("Col 3 Span 1"),
828                    Cell::new_with_col_span("Col 4 Span 1", 2),
829                ]));
830                table.add_row(Row::new(vec![
831                    Cell::new("fasdaff"),
832                    Cell::new("fff"),
833                    Cell::new("fff"),
834                ]));
835                table.add_row(Row::new(vec![
836                    Cell::new_with_alignment("fasdff", 3, Alignment::Right),
837                    Cell::new_with_col_span("fffdff", 4),
838                ]));
839                table.add_row(Row::new(vec![
840                    Cell::new("fasdsaff"),
841                    Cell::new("fff"),
842                    Cell::new("f\nf\nf\nfff\nrrr\n\n\n"),
843                ]));
844                table.add_row(Row::new(vec![Cell::new("fasdsaff")]));
845
846                let s = table.render().clone();
847
848                table.add_row(Row::new(vec![Cell::new_with_alignment(
849                    s,
850                    3,
851                    Alignment::Left,
852                )]));
853
854                let expected = r"╔═════════════════════════════════════════════════════════╦════════════════════════════╦════════════════╦══════════════╦═══╗
855        ║ Col*1*Span*2                                            ║ Col 2 Span 1               ║ Col 3 Span 2   ║ Col 4 Span 1 ║   ║
856        ╠════════════════════════════╦════════════════════════════╬════════════════════════════╬════════════════╬══════════════╬═══╣
857        ║ Col 1 Span 1               ║ Col 2 Span 1               ║ Col 3 Span 1               ║ Col 4 Span 1   ║              ║   ║
858        ╠════════════════════════════╬════════════════════════════╬════════════════════════════╬═══════╦════════╬══════════════╬═══╣
859        ║ fasdaff                    ║ fff                        ║ fff                        ║       ║        ║              ║   ║
860        ╠════════════════════════════╩════════════════════════════╩════════════════════════════╬═══════╩════════╩══════════════╩═══╣
861        ║                                                                               fasdff ║ fffdff                            ║
862        ╠════════════════════════════╦════════════════════════════╦════════════════════════════╬═══════╦════════╦══════════════╦═══╣
863        ║ fasdsaff                   ║ fff                        ║ f                          ║       ║        ║              ║   ║
864        ║                            ║                            ║ f                          ║       ║        ║              ║   ║
865        ║                            ║                            ║ f                          ║       ║        ║              ║   ║
866        ║                            ║                            ║ fff                        ║       ║        ║              ║   ║
867        ║                            ║                            ║ rrr                        ║       ║        ║              ║   ║
868        ║                            ║                            ║                            ║       ║        ║              ║   ║
869        ║                            ║                            ║                            ║       ║        ║              ║   ║
870        ║                            ║                            ║                            ║       ║        ║              ║   ║
871        ╠════════════════════════════╬════════════════════════════╬════════════════════════════╬═══════╬════════╬══════════════╬═══╣
872        ║ fasdsaff                   ║                            ║                            ║       ║        ║              ║   ║
873        ╠════════════════════════════╩════════════════════════════╩════════════════════════════╬═══════╬════════╬══════════════╬═══╣
874        ║ ╔═════════════════════════════╦══════════════╦════════════════╦══════════════╦═══╗   ║       ║        ║              ║   ║
875        ║ ║ Col*1*Span*2                ║ Col 2 Span 1 ║ Col 3 Span 2   ║ Col 4 Span 1 ║   ║   ║       ║        ║              ║   ║
876        ║ ╠══════════════╦══════════════╬══════════════╬════════════════╬══════════════╬═══╣   ║       ║        ║              ║   ║
877        ║ ║ Col 1 Span 1 ║ Col 2 Span 1 ║ Col 3 Span 1 ║ Col 4 Span 1   ║              ║   ║   ║       ║        ║              ║   ║
878        ║ ╠══════════════╬══════════════╬══════════════╬═══════╦════════╬══════════════╬═══╣   ║       ║        ║              ║   ║
879        ║ ║ fasdaff      ║ fff          ║ fff          ║       ║        ║              ║   ║   ║       ║        ║              ║   ║
880        ║ ╠══════════════╩══════════════╩══════════════╬═══════╩════════╩══════════════╩═══╣   ║       ║        ║              ║   ║
881        ║ ║                                     fasdff ║ fffdff                            ║   ║       ║        ║              ║   ║
882        ║ ╠══════════════╦══════════════╦══════════════╬═══════╦════════╦══════════════╦═══╣   ║       ║        ║              ║   ║
883        ║ ║ fasdsaff     ║ fff          ║ f            ║       ║        ║              ║   ║   ║       ║        ║              ║   ║
884        ║ ║              ║              ║ f            ║       ║        ║              ║   ║   ║       ║        ║              ║   ║
885        ║ ║              ║              ║ f            ║       ║        ║              ║   ║   ║       ║        ║              ║   ║
886        ║ ║              ║              ║ fff          ║       ║        ║              ║   ║   ║       ║        ║              ║   ║
887        ║ ║              ║              ║ rrr          ║       ║        ║              ║   ║   ║       ║        ║              ║   ║
888        ║ ║              ║              ║              ║       ║        ║              ║   ║   ║       ║        ║              ║   ║
889        ║ ║              ║              ║              ║       ║        ║              ║   ║   ║       ║        ║              ║   ║
890        ║ ║              ║              ║              ║       ║        ║              ║   ║   ║       ║        ║              ║   ║
891        ║ ╠══════════════╬══════════════╬══════════════╬═══════╬════════╬══════════════╬═══╣   ║       ║        ║              ║   ║
892        ║ ║ fasdsaff     ║              ║              ║       ║        ║              ║   ║   ║       ║        ║              ║   ║
893        ║ ╚══════════════╩══════════════╩══════════════╩═══════╩════════╩══════════════╩═══╝   ║       ║        ║              ║   ║
894        ║                                                                                      ║       ║        ║              ║   ║
895        ╚══════════════════════════════════════════════════════════════════════════════════════╩═══════╩════════╩══════════════╩═══╝
896        ";
897                println!("{}", table.render());
898                assert_eq!(expected, table.render());
899            }
900
901            #[test]
902            fn no_top_border() {
903                let mut table = Table::new();
904                table.style = TableStyle::simple();
905                table.has_top_border = false;
906
907                add_data_to_test_table(&mut table);
908
909                let expected = r"|                            This is some centered text                           |
910        +----------------------------------------+----------------------------------------+
911        | This is left aligned text              |             This is right aligned text |
912        +----------------------------------------+----------------------------------------+
913        | This is left aligned text              |             This is right aligned text |
914        +----------------------------------------+----------------------------------------+
915        | This is some really really really really really really really really really tha |
916        | t is going to wrap to the next line                                             |
917        +---------------------------------------------------------------------------------+
918        ";
919                println!("{}", table.render());
920                assert_eq!(expected, table.render());
921            }
922
923            #[test]
924            fn no_bottom_border() {
925                let mut table = Table::new();
926                table.style = TableStyle::simple();
927                table.has_bottom_border = false;
928
929                add_data_to_test_table(&mut table);
930
931                let expected = r"+---------------------------------------------------------------------------------+
932        |                            This is some centered text                           |
933        +----------------------------------------+----------------------------------------+
934        | This is left aligned text              |             This is right aligned text |
935        +----------------------------------------+----------------------------------------+
936        | This is left aligned text              |             This is right aligned text |
937        +----------------------------------------+----------------------------------------+
938        | This is some really really really really really really really really really tha |
939        | t is going to wrap to the next line                                             |
940        ";
941                println!("{}", table.render());
942                assert_eq!(expected, table.render());
943            }
944
945            #[test]
946            fn no_separators() {
947                let mut table = Table::new();
948                table.style = TableStyle::simple();
949                table.separate_rows = false;
950
951                add_data_to_test_table(&mut table);
952
953                let expected = r"+---------------------------------------------------------------------------------+
954        |                            This is some centered text                           |
955        | This is left aligned text              |             This is right aligned text |
956        | This is left aligned text              |             This is right aligned text |
957        | This is some really really really really really really really really really tha |
958        | t is going to wrap to the next line                                             |
959        +---------------------------------------------------------------------------------+
960        ";
961                println!("{}", table.render());
962                assert_eq!(expected, table.render());
963            }
964
965            #[test]
966            fn some_rows_no_separators() {
967                let mut table = Table::new();
968                table.style = TableStyle::simple();
969
970                add_data_to_test_table(&mut table);
971
972                table.rows[2].has_separator = false;
973
974                let expected = r"+---------------------------------------------------------------------------------+
975        |                            This is some centered text                           |
976        +----------------------------------------+----------------------------------------+
977        | This is left aligned text              |             This is right aligned text |
978        | This is left aligned text              |             This is right aligned text |
979        +----------------------------------------+----------------------------------------+
980        | This is some really really really really really really really really really tha |
981        | t is going to wrap to the next line                                             |
982        +---------------------------------------------------------------------------------+
983        ";
984                println!("{}", table.render());
985                assert_eq!(expected, table.render());
986            }
987
988            #[test]
989            fn colored_data_works() {
990                let mut table = Table::new();
991                table.add_row(Row::new(vec![Cell::new("\u{1b}[31ma\u{1b}[0m")]));
992                let expected = "╔═══╗
993        ║ \u{1b}[31ma\u{1b}[0m ║
994        ╚═══╝
995        ";
996                println!("{}", table.render());
997                assert_eq!(expected, table.render());
998            }
999        */
1000
1001    fn add_data_to_test_table(table: &mut Table) {
1002        table.add_row(
1003            Row::new().with_cell(
1004                Cell::from("This is some centered text")
1005                    .with_col_span(2)
1006                    .with_alignment(Alignment::Center),
1007            ),
1008        );
1009
1010        table.add_row(
1011            Row::new()
1012                .with_cell(Cell::from("This is left aligned text"))
1013                .with_cell(
1014                    Cell::from("This is right aligned text").with_alignment(Alignment::Right),
1015                ),
1016        );
1017
1018        table.add_row(
1019            Row::new()
1020                .with_cell(Cell::from("This is left aligned text"))
1021                .with_cell(
1022                    Cell::from("This is right aligned text").with_alignment(Alignment::Right),
1023                ),
1024        );
1025
1026        table.add_row(
1027            Row::new().with_cell(
1028                Cell::from(
1029                    "This is some really really really really really \
1030                really really really really that is going to wrap to the next line",
1031                )
1032                .with_col_span(2),
1033            ),
1034        );
1035
1036        table.add_row(
1037            Row::new().with_cell(
1038                Cell::from(
1039                    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
1040                    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
1041                )
1042                .with_col_span(2),
1043            ),
1044        );
1045    }
1046}