prettytable/
lib.rs

1#![warn(
2    missing_docs,
3    unused_extern_crates,
4    unused_import_braces,
5    unused_qualifications
6)]
7//! A formatted and aligned table printer written in rust
8
9#[macro_use]
10extern crate lazy_static;
11
12use std::fmt;
13use std::io::{self, Error, Write};
14use std::iter::{FromIterator, IntoIterator};
15use std::ops::{Index, IndexMut};
16use std::slice::{Iter, IterMut};
17
18pub use term::{color, Attr};
19pub(crate) use term::{stdout, Terminal};
20
21mod cell;
22pub mod format;
23mod row;
24mod utils;
25
26#[cfg(feature = "csv")]
27pub mod csv;
28
29#[cfg(feature = "evcxr")]
30pub mod evcxr;
31
32pub use cell::Cell;
33use format::{consts, LinePosition, TableFormat};
34pub use row::Row;
35use utils::StringWriter;
36
37/// An owned printable table
38#[derive(Default, Clone, Debug, Hash, PartialEq, Eq)]
39pub struct Table {
40    format: Box<TableFormat>,
41    titles: Box<Option<Row>>,
42    rows: Vec<Row>,
43}
44
45/// A borrowed immutable `Table` slice
46/// A `TableSlice` is obtained by slicing a `Table` with the `Slice::slice` method.
47///
48/// # Examples
49/// ```rust
50/// # #[macro_use] extern crate prettytable;
51/// use prettytable::{Table, Slice};
52/// # fn main() {
53/// let table = table![[1, 2, 3], [4, 5, 6], [7, 8, 9]];
54/// let slice = table.slice(1..);
55/// slice.printstd(); // Prints only rows 1 and 2
56///
57/// //Also supports other syntax :
58/// table.slice(..);
59/// table.slice(..2);
60/// table.slice(1..3);
61/// # }
62/// ```
63///
64#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
65pub struct TableSlice<'a> {
66    format: &'a TableFormat,
67    titles: &'a Option<Row>,
68    rows: &'a [Row],
69}
70
71impl<'a> TableSlice<'a> {
72    /// Compute and return the number of column
73    // #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")]
74    fn get_column_num(&self) -> usize {
75        let mut cnum = match *self.titles {
76            Some(ref t) => t.column_count(),
77            None => 0,
78        };
79        for r in self.rows {
80            let l = r.column_count();
81            if l > cnum {
82                cnum = l;
83            }
84        }
85        cnum
86    }
87
88    /// Get the number of rows
89    pub fn len(&self) -> usize {
90        self.rows.len()
91    }
92
93    /// Check if the table slice is empty
94    pub fn is_empty(&self) -> bool {
95        self.rows.is_empty()
96    }
97
98    /// Get an immutable reference to a row
99    pub fn get_row(&self, row: usize) -> Option<&Row> {
100        self.rows.get(row)
101    }
102
103    /// Get the width of the column at position `col_idx`.
104    /// Return 0 if the column does not exists;
105    fn get_column_width(&self, col_idx: usize) -> usize {
106        let mut width = match *self.titles {
107            Some(ref t) => t.get_column_width(col_idx, self.format),
108            None => 0,
109        };
110        for r in self.rows {
111            let l = r.get_column_width(col_idx, self.format);
112            if l > width {
113                width = l;
114            }
115        }
116        width
117    }
118
119    /// Get the width of all columns, and return a slice
120    /// with the result for each column
121    fn get_all_column_width(&self) -> Vec<usize> {
122        let colnum = self.get_column_num();
123        let mut col_width = vec![0usize; colnum];
124        #[allow(clippy::needless_range_loop)]
125        for i in 0..colnum {
126            // TODO: calling "get_column_width()" in a loop is inefficient
127            col_width[i] = self.get_column_width(i);
128        }
129        col_width
130    }
131
132    /// Returns an iterator over the immutable cells of the column specified by `column`
133    pub fn column_iter(&self, column: usize) -> ColumnIter {
134        ColumnIter(self.rows.iter(), column)
135    }
136
137    /// Returns an iterator over immutable rows
138    pub fn row_iter(&self) -> Iter<Row> {
139        self.rows.iter()
140    }
141
142    /// Internal only
143    fn __print<T: Write + ?Sized, F>(&self, out: &mut T, f: F) -> Result<usize, Error>
144    where
145        F: Fn(&Row, &mut T, &TableFormat, &[usize]) -> Result<usize, Error>,
146    {
147        let mut height = 0;
148        // Compute columns width
149        let col_width = self.get_all_column_width();
150        height += self
151            .format
152            .print_line_separator(out, &col_width, LinePosition::Top)?;
153        if let Some(ref t) = *self.titles {
154            height += f(t, out, self.format, &col_width)?;
155            height += self
156                .format
157                .print_line_separator(out, &col_width, LinePosition::Title)?;
158        }
159        // Print rows
160        let mut iter = self.rows.iter().peekable();
161        while let Some(r) = iter.next() {
162            height += f(r, out, self.format, &col_width)?;
163            if iter.peek().is_some() {
164                height +=
165                    self.format
166                        .print_line_separator(out, &col_width, LinePosition::Intern)?;
167            }
168        }
169        height += self
170            .format
171            .print_line_separator(out, &col_width, LinePosition::Bottom)?;
172        out.flush()?;
173        Ok(height)
174    }
175
176    /// Print the table to `out` and returns the number of
177    /// line printed, or an error
178    pub fn print<T: Write + ?Sized>(&self, out: &mut T) -> Result<usize, Error> {
179        self.__print(out, Row::print)
180    }
181
182    /// Print the table to terminal `out`, applying styles when needed and returns the number of
183    /// line printed, or an error
184    pub fn print_term<T: Terminal + ?Sized>(&self, out: &mut T) -> Result<usize, Error> {
185        self.__print(out, Row::print_term)
186    }
187
188    /// Print the table to standard output. Colors won't be displayed unless
189    /// stdout is a tty terminal, or `force_colorize` is set to `true`.
190    /// In ANSI terminals, colors are displayed using ANSI escape characters. When for example the
191    /// output is redirected to a file, or piped to another program, the output is considered
192    /// as not beeing tty, and ANSI escape characters won't be displayed unless `force colorize`
193    /// is set to `true`.
194    /// # Returns
195    /// A `Result` holding the number of lines printed, or an `io::Error` if any failure happens
196    pub fn print_tty(&self, force_colorize: bool) -> Result<usize, Error> {
197        use is_terminal::IsTerminal;
198        match (stdout(), io::stdout().is_terminal() || force_colorize) {
199            (Some(mut o), true) => self.print_term(&mut *o),
200            _ => self.print(&mut io::stdout()),
201        }
202    }
203
204    /// Print the table to standard output. Colors won't be displayed unless
205    /// stdout is a tty terminal. This means that if stdout is redirected to a file, or piped
206    /// to another program, no color will be displayed.
207    /// To force colors rendering, use `print_tty()` method.
208    /// Any failure to print is ignored. For better control, use `print_tty()`.
209    /// Calling `printstd()` is equivalent to calling `print_tty(false)` and ignoring the result.
210    pub fn printstd(&self) {
211        let _ = self.print_tty(false); // Ignore result
212    }
213
214    /// Print table in HTML format to `out`.
215    pub fn print_html<T: Write + ?Sized>(&self, out: &mut T) -> Result<(), Error> {
216        // Compute column width
217        let column_num = self.get_column_num();
218        out.write_all(b"<table>")?;
219        // Print titles / table header
220        if let Some(ref t) = *self.titles {
221            out.write_all(b"<th>")?;
222            t.print_html(out, column_num)?;
223            out.write_all(b"</th>")?;
224        }
225        // Print rows
226        for r in self.rows {
227            out.write_all(b"<tr>")?;
228            r.print_html(out, column_num)?;
229            out.write_all(b"</tr>")?;
230        }
231        out.write_all(b"</table>")?;
232        out.flush()?;
233        Ok(())
234    }
235}
236
237impl<'a> IntoIterator for &'a TableSlice<'a> {
238    type Item = &'a Row;
239    type IntoIter = Iter<'a, Row>;
240    fn into_iter(self) -> Self::IntoIter {
241        self.row_iter()
242    }
243}
244
245impl Table {
246    /// Create an empty table
247    pub fn new() -> Table {
248        Self::init(Vec::new())
249    }
250
251    /// Create a table initialized with `rows`
252    pub fn init(rows: Vec<Row>) -> Table {
253        Table {
254            rows,
255            titles: Box::new(None),
256            format: Box::new(*consts::FORMAT_DEFAULT),
257        }
258    }
259
260    /// Change the table format. Eg : Separators
261    pub fn set_format(&mut self, format: TableFormat) {
262        *self.format = format;
263    }
264
265    /// Get a mutable reference to the internal format
266    pub fn get_format(&mut self) -> &mut TableFormat {
267        &mut self.format
268    }
269
270    /// Compute and return the number of column
271    // #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")]
272    #[cfg(test)] // Only used for testing for now
273    pub(crate) fn get_column_num(&self) -> usize {
274        self.as_slice().get_column_num()
275    }
276
277    /// Get the number of rows
278    pub fn len(&self) -> usize {
279        self.rows.len()
280    }
281
282    /// Check if the table is empty
283    pub fn is_empty(&self) -> bool {
284        self.rows.is_empty()
285    }
286
287    /// Set the optional title lines
288    pub fn set_titles(&mut self, titles: Row) {
289        *self.titles = Some(titles);
290    }
291
292    /// Unset the title line
293    pub fn unset_titles(&mut self) {
294        *self.titles = None;
295    }
296
297    /// Get a mutable reference to a row
298    pub fn get_mut_row(&mut self, row: usize) -> Option<&mut Row> {
299        self.rows.get_mut(row)
300    }
301
302    /// Get an immutable reference to a row
303    pub fn get_row(&self, row: usize) -> Option<&Row> {
304        self.rows.get(row)
305    }
306
307    /// Append a row in the table, transferring ownership of this row to the table
308    /// and returning a mutable reference to the row
309    pub fn add_row(&mut self, row: Row) -> &mut Row {
310        self.rows.push(row);
311        let l = self.rows.len() - 1;
312        &mut self.rows[l]
313    }
314
315    /// Append an empty row in the table. Return a mutable reference to this new row.
316    pub fn add_empty_row(&mut self) -> &mut Row {
317        self.add_row(Row::default())
318    }
319
320    /// Insert `row` at the position `index`, and return a mutable reference to this row.
321    /// If index is higher than current numbers of rows, `row` is appended at the end of the table
322    pub fn insert_row(&mut self, index: usize, row: Row) -> &mut Row {
323        if index < self.rows.len() {
324            self.rows.insert(index, row);
325            &mut self.rows[index]
326        } else {
327            self.add_row(row)
328        }
329    }
330
331    /// Modify a single element in the table
332    pub fn set_element(&mut self, element: &str, column: usize, row: usize) -> Result<(), &str> {
333        let rowline = self.get_mut_row(row).ok_or("Cannot find row")?;
334        // TODO: If a cell already exist, copy it's alignment parameter
335        rowline.set_cell(Cell::new(element), column)
336    }
337
338    /// Remove the row at position `index`. Silently skip if the row does not exist
339    pub fn remove_row(&mut self, index: usize) {
340        if index < self.rows.len() {
341            self.rows.remove(index);
342        }
343    }
344
345    /// Return an iterator over the immutable cells of the column specified by `column`
346    pub fn column_iter(&self, column: usize) -> ColumnIter {
347        ColumnIter(self.rows.iter(), column)
348    }
349
350    /// Return an iterator over the mutable cells of the column specified by `column`
351    pub fn column_iter_mut(&mut self, column: usize) -> ColumnIterMut {
352        ColumnIterMut(self.rows.iter_mut(), column)
353    }
354
355    /// Returns an iterator over immutable rows
356    pub fn row_iter(&self) -> Iter<Row> {
357        self.rows.iter()
358    }
359
360    /// Returns an iterator over mutable rows
361    pub fn row_iter_mut(&mut self) -> IterMut<Row> {
362        self.rows.iter_mut()
363    }
364
365    /// Print the table to `out` and returns the number
366    /// of lines printed, or an error
367    pub fn print<T: Write + ?Sized>(&self, out: &mut T) -> Result<usize, Error> {
368        self.as_slice().print(out)
369    }
370
371    /// Print the table to terminal `out`, applying styles when needed and returns the number
372    /// of lines printed, or an error
373    pub fn print_term<T: Terminal + ?Sized>(&self, out: &mut T) -> Result<usize, Error> {
374        self.as_slice().print_term(out)
375    }
376
377    /// Print the table to standard output. Colors won't be displayed unless
378    /// stdout is a tty terminal, or `force_colorize` is set to `true`.
379    /// In ANSI terminals, colors are displayed using ANSI escape characters. When for example the
380    /// output is redirected to a file, or piped to another program, the output is considered
381    /// as not beeing tty, and ANSI escape characters won't be displayed unless `force colorize`
382    /// is set to `true`.
383    /// # Returns
384    /// A `Result` holding the number of lines printed, or an `io::Error` if any failure happens
385    pub fn print_tty(&self, force_colorize: bool) -> Result<usize, Error> {
386        self.as_slice().print_tty(force_colorize)
387    }
388
389    /// Print the table to standard output. Colors won't be displayed unless
390    /// stdout is a tty terminal. This means that if stdout is redirected to a file, or piped
391    /// to another program, no color will be displayed.
392    /// To force colors rendering, use `print_tty()` method.
393    /// Any failure to print is ignored. For better control, use `print_tty()`.
394    /// Calling `printstd()` is equivalent to calling `print_tty(false)` and ignoring the result.
395    pub fn printstd(&self) {
396        self.as_slice().printstd()
397    }
398
399    /// Print table in HTML format to `out`.
400    pub fn print_html<T: Write + ?Sized>(&self, out: &mut T) -> Result<(), Error> {
401        self.as_slice().print_html(out)
402    }
403}
404
405/// Trait implemented by types which can be sliced
406pub trait AsTableSlice {
407    /// Get a slice from self
408    fn as_slice(&self) -> TableSlice<'_>;
409}
410
411impl AsTableSlice for Table {
412    fn as_slice(&self) -> TableSlice<'_> {
413        TableSlice {
414            format: &self.format,
415            titles: &self.titles,
416            rows: &self.rows,
417        }
418    }
419}
420
421impl<T> AsTableSlice for T
422where
423    T: AsRef<Table>,
424{
425    fn as_slice(&self) -> TableSlice<'_> {
426        self.as_ref().as_slice()
427    }
428}
429
430impl Index<usize> for Table {
431    type Output = Row;
432    fn index(&self, idx: usize) -> &Self::Output {
433        &self.rows[idx]
434    }
435}
436
437impl<'a> Index<usize> for TableSlice<'a> {
438    type Output = Row;
439    fn index(&self, idx: usize) -> &Self::Output {
440        &self.rows[idx]
441    }
442}
443
444impl IndexMut<usize> for Table {
445    fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
446        &mut self.rows[idx]
447    }
448}
449
450impl fmt::Display for Table {
451    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
452        self.as_slice().fmt(fmt)
453    }
454}
455
456impl<'a> fmt::Display for TableSlice<'a> {
457    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
458        let mut writer = StringWriter::new();
459        if self.print(&mut writer).is_err() {
460            return Err(fmt::Error);
461        }
462        fmt.write_str(writer.as_string())
463    }
464}
465
466impl<B: ToString, A: IntoIterator<Item = B>> FromIterator<A> for Table {
467    fn from_iter<T>(iterator: T) -> Table
468    where
469        T: IntoIterator<Item = A>,
470    {
471        Self::init(iterator.into_iter().map(Row::from).collect())
472    }
473}
474
475impl FromIterator<Row> for Table {
476    fn from_iter<T>(iterator: T) -> Table
477    where
478        T: IntoIterator<Item = Row>,
479    {
480        Self::init(iterator.into_iter().collect())
481    }
482}
483
484impl<T, A, B> From<T> for Table
485where
486    B: ToString,
487    A: IntoIterator<Item = B>,
488    T: IntoIterator<Item = A>,
489{
490    fn from(it: T) -> Table {
491        Self::from_iter(it)
492    }
493}
494
495impl<'a> IntoIterator for &'a Table {
496    type Item = &'a Row;
497    type IntoIter = Iter<'a, Row>;
498    fn into_iter(self) -> Self::IntoIter {
499        self.row_iter()
500    }
501}
502
503impl<'a> IntoIterator for &'a mut Table {
504    type Item = &'a mut Row;
505    type IntoIter = IterMut<'a, Row>;
506    fn into_iter(self) -> Self::IntoIter {
507        self.row_iter_mut()
508    }
509}
510
511// impl IntoIterator for Table {
512//     type Item = Row;
513//     type IntoIter = std::vec::IntoIter<Self::Item>;
514//     fn into_iter(self) -> Self::IntoIter {
515//         self.rows.into_iter()
516//     }
517// }
518
519impl<A: Into<Row>> Extend<A> for Table {
520    fn extend<T: IntoIterator<Item = A>>(&mut self, iter: T) {
521        self.rows.extend(iter.into_iter().map(|r| r.into()));
522    }
523}
524
525/// Iterator over immutable cells in a column
526pub struct ColumnIter<'a>(Iter<'a, Row>, usize);
527
528impl<'a> Iterator for ColumnIter<'a> {
529    type Item = &'a Cell;
530    fn next(&mut self) -> Option<&'a Cell> {
531        self.0.next().and_then(|row| row.get_cell(self.1))
532    }
533}
534
535/// Iterator over mutable cells in a column
536pub struct ColumnIterMut<'a>(IterMut<'a, Row>, usize);
537
538impl<'a> Iterator for ColumnIterMut<'a> {
539    type Item = &'a mut Cell;
540    fn next(&mut self) -> Option<&'a mut Cell> {
541        self.0.next().and_then(|row| row.get_mut_cell(self.1))
542    }
543}
544
545impl<'a> AsTableSlice for TableSlice<'a> {
546    fn as_slice(&self) -> TableSlice<'_> {
547        *self
548    }
549}
550
551impl<'a> AsRef<TableSlice<'a>> for TableSlice<'a> {
552    fn as_ref(&self) -> &TableSlice<'a> {
553        self
554    }
555}
556
557/// Trait implemented by types which can be sliced
558pub trait Slice<'a, E> {
559    /// Type output after slicing
560    type Output: 'a;
561    /// Get a slice from self
562    fn slice(&'a self, arg: E) -> Self::Output;
563}
564
565impl<'a, T, E> Slice<'a, E> for T
566where
567    T: AsTableSlice,
568    [Row]: Index<E, Output = [Row]>,
569{
570    type Output = TableSlice<'a>;
571    fn slice(&'a self, arg: E) -> Self::Output {
572        let mut sl = self.as_slice();
573        sl.rows = sl.rows.index(arg);
574        sl
575    }
576}
577
578/// Create a table filled with some values
579///
580/// All the arguments used for elements must implement the `std::string::ToString` trait
581/// # Syntax
582/// ```text
583/// table!([Element1_ row1, Element2_ row1, ...], [Element1_row2, ...], ...);
584/// ```
585///
586/// # Example
587/// ```
588/// # #[macro_use] extern crate prettytable;
589/// # fn main() {
590/// // Create a table initialized with some rows :
591/// let tab = table!(["Element1", "Element2", "Element3"],
592///                  [1, 2, 3],
593///                  ["A", "B", "C"]
594///                 );
595/// # drop(tab);
596/// # }
597/// ```
598///
599/// Some style can also be given in table creation
600///
601/// ```
602/// # #[macro_use] extern crate prettytable;
603/// # fn main() {
604/// let tab = table!([FrByl->"Element1", Fgc->"Element2", "Element3"],
605///                  [FrBy => 1, 2, 3],
606///                  ["A", "B", "C"]
607///                 );
608/// # drop(tab);
609/// # }
610/// ```
611///
612/// For details about style specifier syntax, check doc for [`Cell::style_spec`](cell/struct.Cell.html#method.style_spec) method
613#[macro_export]
614macro_rules! table {
615    ($([$($content:tt)*]), *) => (
616        $crate::Table::init(vec![$($crate::row![$($content)*]), *])
617    );
618}
619
620/// Create a table with `table!` macro, print it to standard output, then return this table for future usage.
621///
622/// The syntax is the same that the one for the `table!` macro
623#[macro_export]
624macro_rules! ptable {
625    ($($content:tt)*) => (
626        {
627            let tab = $crate::table!($($content)*);
628            tab.printstd();
629            tab
630        }
631    );
632}
633
634#[cfg(test)]
635mod tests {
636    use crate::utils::StringWriter;
637    use crate::{format, AsTableSlice, Cell, Row, Slice, Table};
638    use format::consts::{
639        FORMAT_BOX_CHARS, FORMAT_CLEAN, FORMAT_DEFAULT, FORMAT_NO_COLSEP, FORMAT_NO_LINESEP,
640    };
641
642    #[test]
643    fn table() {
644        let mut table = Table::new();
645        table.add_row(Row::new(vec![
646            Cell::new("a"),
647            Cell::new("bc"),
648            Cell::new("def"),
649        ]));
650        table.add_row(Row::new(vec![
651            Cell::new("def"),
652            Cell::new("bc"),
653            Cell::new("a"),
654        ]));
655        table.set_titles(Row::new(vec![
656            Cell::new("t1"),
657            Cell::new("t2"),
658            Cell::new("t3"),
659        ]));
660        let out = "\
661+-----+----+-----+
662| t1  | t2 | t3  |
663+=====+====+=====+
664| a   | bc | def |
665+-----+----+-----+
666| def | bc | a   |
667+-----+----+-----+
668";
669        assert_eq!(table.to_string().replace("\r\n", "\n"), out);
670        assert_eq!(7, table.print(&mut StringWriter::new()).unwrap());
671        table.unset_titles();
672        let out = "\
673+-----+----+-----+
674| a   | bc | def |
675+-----+----+-----+
676| def | bc | a   |
677+-----+----+-----+
678";
679        assert_eq!(table.to_string().replace("\r\n", "\n"), out);
680        assert_eq!(5, table.print(&mut StringWriter::new()).unwrap());
681    }
682
683    #[test]
684    fn index() {
685        let mut table = Table::new();
686        table.add_row(Row::new(vec![
687            Cell::new("a"),
688            Cell::new("bc"),
689            Cell::new("def"),
690        ]));
691        table.add_row(Row::new(vec![
692            Cell::new("def"),
693            Cell::new("bc"),
694            Cell::new("a"),
695        ]));
696        table.set_titles(Row::new(vec![
697            Cell::new("t1"),
698            Cell::new("t2"),
699            Cell::new("t3"),
700        ]));
701        assert_eq!(table[1][1].get_content(), "bc");
702
703        table[1][1] = Cell::new("newval");
704        assert_eq!(table[1][1].get_content(), "newval");
705
706        let out = "\
707+-----+--------+-----+
708| t1  | t2     | t3  |
709+=====+========+=====+
710| a   | bc     | def |
711+-----+--------+-----+
712| def | newval | a   |
713+-----+--------+-----+
714";
715        assert_eq!(table.to_string().replace("\r\n", "\n"), out);
716        assert_eq!(7, table.print(&mut StringWriter::new()).unwrap());
717    }
718
719    #[test]
720    fn table_size() {
721        let mut table = Table::new();
722        assert!(table.is_empty());
723        assert!(table.as_slice().is_empty());
724        assert_eq!(table.len(), 0);
725        assert_eq!(table.as_slice().len(), 0);
726        assert_eq!(table.get_column_num(), 0);
727        assert_eq!(table.as_slice().get_column_num(), 0);
728        table.add_empty_row();
729        assert!(!table.is_empty());
730        assert!(!table.as_slice().is_empty());
731        assert_eq!(table.len(), 1);
732        assert_eq!(table.as_slice().len(), 1);
733        assert_eq!(table.get_column_num(), 0);
734        assert_eq!(table.as_slice().get_column_num(), 0);
735        table[0].add_cell(Cell::default());
736        assert_eq!(table.get_column_num(), 1);
737        assert_eq!(table.as_slice().get_column_num(), 1);
738    }
739
740    #[test]
741    fn get_row() {
742        let mut table = Table::new();
743        table.add_row(Row::new(vec![
744            Cell::new("a"),
745            Cell::new("bc"),
746            Cell::new("def"),
747        ]));
748        table.add_row(Row::new(vec![
749            Cell::new("def"),
750            Cell::new("bc"),
751            Cell::new("a"),
752        ]));
753        assert!(table.get_row(12).is_none());
754        assert!(table.get_row(1).is_some());
755        assert_eq!(table.get_row(1).unwrap()[0].get_content(), "def");
756        assert!(table.get_mut_row(12).is_none());
757        assert!(table.get_mut_row(1).is_some());
758        table.get_mut_row(1).unwrap().add_cell(Cell::new("z"));
759        assert_eq!(table.get_row(1).unwrap()[3].get_content(), "z");
760    }
761
762    #[test]
763    fn add_empty_row() {
764        let mut table = Table::new();
765        assert_eq!(table.len(), 0);
766        table.add_empty_row();
767        assert_eq!(table.len(), 1);
768        assert_eq!(table[0].len(), 0);
769    }
770
771    #[test]
772    fn remove_row() {
773        let mut table = Table::new();
774        table.add_row(Row::new(vec![
775            Cell::new("a"),
776            Cell::new("bc"),
777            Cell::new("def"),
778        ]));
779        table.add_row(Row::new(vec![
780            Cell::new("def"),
781            Cell::new("bc"),
782            Cell::new("a"),
783        ]));
784        table.remove_row(12);
785        assert_eq!(table.len(), 2);
786        table.remove_row(0);
787        assert_eq!(table.len(), 1);
788        assert_eq!(table[0][0].get_content(), "def");
789    }
790
791    #[test]
792    fn insert_row() {
793        let mut table = Table::new();
794        table.add_row(Row::new(vec![
795            Cell::new("a"),
796            Cell::new("bc"),
797            Cell::new("def"),
798        ]));
799        table.add_row(Row::new(vec![
800            Cell::new("def"),
801            Cell::new("bc"),
802            Cell::new("a"),
803        ]));
804        table.insert_row(
805            12,
806            Row::new(vec![Cell::new("1"), Cell::new("2"), Cell::new("3")]),
807        );
808        assert_eq!(table.len(), 3);
809        assert_eq!(table[2][1].get_content(), "2");
810        table.insert_row(
811            1,
812            Row::new(vec![Cell::new("3"), Cell::new("4"), Cell::new("5")]),
813        );
814        assert_eq!(table.len(), 4);
815        assert_eq!(table[1][1].get_content(), "4");
816        assert_eq!(table[2][1].get_content(), "bc");
817    }
818
819    #[test]
820    fn set_element() {
821        let mut table = Table::new();
822        table.add_row(Row::new(vec![
823            Cell::new("a"),
824            Cell::new("bc"),
825            Cell::new("def"),
826        ]));
827        table.add_row(Row::new(vec![
828            Cell::new("def"),
829            Cell::new("bc"),
830            Cell::new("a"),
831        ]));
832        assert!(table.set_element("foo", 12, 12).is_err());
833        assert!(table.set_element("foo", 1, 1).is_ok());
834        assert_eq!(table[1][1].get_content(), "foo");
835    }
836
837    #[test]
838    fn no_linesep() {
839        let mut table = Table::new();
840        table.set_format(*FORMAT_NO_LINESEP);
841        table.add_row(Row::new(vec![
842            Cell::new("a"),
843            Cell::new("bc"),
844            Cell::new("def"),
845        ]));
846        table.add_row(Row::new(vec![
847            Cell::new("def"),
848            Cell::new("bc"),
849            Cell::new("a"),
850        ]));
851        table.set_titles(Row::new(vec![
852            Cell::new("t1"),
853            Cell::new("t2"),
854            Cell::new("t3"),
855        ]));
856        assert_eq!(table[1][1].get_content(), "bc");
857
858        table[1][1] = Cell::new("newval");
859        assert_eq!(table[1][1].get_content(), "newval");
860
861        let out = "\
862+-----+--------+-----+
863| t1  | t2     | t3  |
864| a   | bc     | def |
865| def | newval | a   |
866+-----+--------+-----+
867";
868        assert_eq!(table.to_string().replace("\r\n", "\n"), out);
869        assert_eq!(5, table.print(&mut StringWriter::new()).unwrap());
870    }
871
872    #[test]
873    fn no_colsep() {
874        let mut table = Table::new();
875        table.set_format(*FORMAT_NO_COLSEP);
876        table.add_row(Row::new(vec![
877            Cell::new("a"),
878            Cell::new("bc"),
879            Cell::new("def"),
880        ]));
881        table.add_row(Row::new(vec![
882            Cell::new("def"),
883            Cell::new("bc"),
884            Cell::new("a"),
885        ]));
886        table.set_titles(Row::new(vec![
887            Cell::new("t1"),
888            Cell::new("t2"),
889            Cell::new("t3"),
890        ]));
891        assert_eq!(table[1][1].get_content(), "bc");
892
893        table[1][1] = Cell::new("newval");
894        assert_eq!(table[1][1].get_content(), "newval");
895
896        let out = "\
897------------------
898 t1   t2      t3 \n\
899==================
900 a    bc      def \n\
901------------------
902 def  newval  a \n\
903------------------
904";
905        println!("{}", out);
906        println!("____");
907        println!("{}", table.to_string().replace("\r\n", "\n"));
908        assert_eq!(table.to_string().replace("\r\n", "\n"), out);
909        assert_eq!(7, table.print(&mut StringWriter::new()).unwrap());
910    }
911
912    #[test]
913    fn clean() {
914        let mut table = Table::new();
915        table.set_format(*FORMAT_CLEAN);
916        table.add_row(Row::new(vec![
917            Cell::new("a"),
918            Cell::new("bc"),
919            Cell::new("def"),
920        ]));
921        table.add_row(Row::new(vec![
922            Cell::new("def"),
923            Cell::new("bc"),
924            Cell::new("a"),
925        ]));
926        table.set_titles(Row::new(vec![
927            Cell::new("t1"),
928            Cell::new("t2"),
929            Cell::new("t3"),
930        ]));
931        assert_eq!(table[1][1].get_content(), "bc");
932
933        table[1][1] = Cell::new("newval");
934        assert_eq!(table[1][1].get_content(), "newval");
935
936        let out = "\
937\u{0020}t1   t2      t3 \n\
938\u{0020}a    bc      def \n\
939\u{0020}def  newval  a \n\
940";
941        println!("{}", out);
942        println!("____");
943        println!("{}", table.to_string().replace("\r\n", "\n"));
944        assert_eq!(out, table.to_string().replace("\r\n", "\n"));
945        assert_eq!(3, table.print(&mut StringWriter::new()).unwrap());
946    }
947
948    #[test]
949    fn padding() {
950        let mut table = Table::new();
951        let mut format = *FORMAT_DEFAULT;
952        format.padding(2, 2);
953        table.set_format(format);
954        table.add_row(Row::new(vec![
955            Cell::new("a"),
956            Cell::new("bc"),
957            Cell::new("def"),
958        ]));
959        table.add_row(Row::new(vec![
960            Cell::new("def"),
961            Cell::new("bc"),
962            Cell::new("a"),
963        ]));
964        table.set_titles(Row::new(vec![
965            Cell::new("t1"),
966            Cell::new("t2"),
967            Cell::new("t3"),
968        ]));
969        assert_eq!(table[1][1].get_content(), "bc");
970
971        table[1][1] = Cell::new("newval");
972        assert_eq!(table[1][1].get_content(), "newval");
973
974        let out = "\
975+-------+----------+-------+
976|  t1   |  t2      |  t3   |
977+=======+==========+=======+
978|  a    |  bc      |  def  |
979+-------+----------+-------+
980|  def  |  newval  |  a    |
981+-------+----------+-------+
982";
983        println!("{}", out);
984        println!("____");
985        println!("{}", table.to_string().replace("\r\n", "\n"));
986        assert_eq!(out, table.to_string().replace("\r\n", "\n"));
987        assert_eq!(7, table.print(&mut StringWriter::new()).unwrap());
988    }
989
990    #[test]
991    fn indent() {
992        let mut table = Table::new();
993        table.add_row(Row::new(vec![
994            Cell::new("a"),
995            Cell::new("bc"),
996            Cell::new("def"),
997        ]));
998        table.add_row(Row::new(vec![
999            Cell::new("def"),
1000            Cell::new("bc"),
1001            Cell::new("a"),
1002        ]));
1003        table.set_titles(Row::new(vec![
1004            Cell::new("t1"),
1005            Cell::new("t2"),
1006            Cell::new("t3"),
1007        ]));
1008        table.get_format().indent(8);
1009        let out = "        +-----+----+-----+
1010        | t1  | t2 | t3  |
1011        +=====+====+=====+
1012        | a   | bc | def |
1013        +-----+----+-----+
1014        | def | bc | a   |
1015        +-----+----+-----+
1016";
1017        assert_eq!(table.to_string().replace("\r\n", "\n"), out);
1018        assert_eq!(7, table.print(&mut StringWriter::new()).unwrap());
1019    }
1020
1021    #[test]
1022    fn slices() {
1023        let mut table = Table::new();
1024        table.set_titles(Row::new(vec![
1025            Cell::new("t1"),
1026            Cell::new("t2"),
1027            Cell::new("t3"),
1028        ]));
1029        table.add_row(Row::new(vec![
1030            Cell::new("0"),
1031            Cell::new("0"),
1032            Cell::new("0"),
1033        ]));
1034        table.add_row(Row::new(vec![
1035            Cell::new("1"),
1036            Cell::new("1"),
1037            Cell::new("1"),
1038        ]));
1039        table.add_row(Row::new(vec![
1040            Cell::new("2"),
1041            Cell::new("2"),
1042            Cell::new("2"),
1043        ]));
1044        table.add_row(Row::new(vec![
1045            Cell::new("3"),
1046            Cell::new("3"),
1047            Cell::new("3"),
1048        ]));
1049        table.add_row(Row::new(vec![
1050            Cell::new("4"),
1051            Cell::new("4"),
1052            Cell::new("4"),
1053        ]));
1054        table.add_row(Row::new(vec![
1055            Cell::new("5"),
1056            Cell::new("5"),
1057            Cell::new("5"),
1058        ]));
1059        let out = "\
1060+----+----+----+
1061| t1 | t2 | t3 |
1062+====+====+====+
1063| 1  | 1  | 1  |
1064+----+----+----+
1065| 2  | 2  | 2  |
1066+----+----+----+
1067| 3  | 3  | 3  |
1068+----+----+----+
1069";
1070        let slice = table.slice(..);
1071        let slice = slice.slice(1..);
1072        let slice = slice.slice(..3);
1073        assert_eq!(out, slice.to_string().replace("\r\n", "\n"));
1074        assert_eq!(9, slice.print(&mut StringWriter::new()).unwrap());
1075        assert_eq!(out, table.slice(1..4).to_string().replace("\r\n", "\n"));
1076        assert_eq!(
1077            9,
1078            table.slice(1..4).print(&mut StringWriter::new()).unwrap()
1079        );
1080    }
1081
1082    #[test]
1083    fn test_unicode_separators() {
1084        let mut table = Table::new();
1085        table.set_format(*FORMAT_BOX_CHARS);
1086        table.add_row(Row::new(vec![
1087            Cell::new("1"),
1088            Cell::new("1"),
1089            Cell::new("1"),
1090        ]));
1091        table.add_row(Row::new(vec![
1092            Cell::new("2"),
1093            Cell::new("2"),
1094            Cell::new("2"),
1095        ]));
1096        table.set_titles(Row::new(vec![
1097            Cell::new("t1"),
1098            Cell::new("t2"),
1099            Cell::new("t3"),
1100        ]));
1101        let out = "\
1102┌────┬────┬────┐
1103│ t1 │ t2 │ t3 │
1104├────┼────┼────┤
1105│ 1  │ 1  │ 1  │
1106├────┼────┼────┤
1107│ 2  │ 2  │ 2  │
1108└────┴────┴────┘
1109";
1110        println!("{}", out);
1111        println!("____");
1112        println!("{}", table.to_string().replace("\r\n", "\n"));
1113        assert_eq!(out, table.to_string().replace("\r\n", "\n"));
1114        assert_eq!(7, table.print(&mut StringWriter::new()).unwrap());
1115    }
1116
1117    #[test]
1118    fn test_readme_format() {
1119        // The below is lifted from the README
1120
1121        let mut table = Table::new();
1122        let format = format::FormatBuilder::new()
1123            .column_separator('|')
1124            .borders('|')
1125            .separators(
1126                &[format::LinePosition::Top, format::LinePosition::Bottom],
1127                format::LineSeparator::new('-', '+', '+', '+'),
1128            )
1129            .padding(1, 1)
1130            .build();
1131        table.set_format(format);
1132
1133        table.set_titles(Row::new(vec![Cell::new("Title 1"), Cell::new("Title 2")]));
1134        table.add_row(Row::new(vec![Cell::new("Value 1"), Cell::new("Value 2")]));
1135        table.add_row(Row::new(vec![
1136            Cell::new("Value three"),
1137            Cell::new("Value four"),
1138        ]));
1139
1140        let out = "\
1141+-------------+------------+
1142| Title 1     | Title 2    |
1143| Value 1     | Value 2    |
1144| Value three | Value four |
1145+-------------+------------+
1146";
1147
1148        println!("{}", out);
1149        println!("____");
1150        println!("{}", table.to_string().replace("\r\n", "\n"));
1151        assert_eq!(out, table.to_string().replace("\r\n", "\n"));
1152        assert_eq!(5, table.print(&mut StringWriter::new()).unwrap());
1153    }
1154
1155    #[test]
1156    fn test_readme_format_with_title() {
1157        let mut table = Table::new();
1158        table.set_format(*format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
1159
1160        table.set_titles(Row::new(vec![Cell::new("Title 1"), Cell::new("Title 2")]));
1161        table.add_row(Row::new(vec![Cell::new("Value 1"), Cell::new("Value 2")]));
1162        table.add_row(Row::new(vec![
1163            Cell::new("Value three"),
1164            Cell::new("Value four"),
1165        ]));
1166
1167        let out = "\
1168+-------------+------------+
1169| Title 1     | Title 2    |
1170+-------------+------------+
1171| Value 1     | Value 2    |
1172| Value three | Value four |
1173+-------------+------------+
1174";
1175        println!("{}", out);
1176        println!("____");
1177        println!("{}", table.to_string().replace("\r\n", "\n"));
1178        assert_eq!(out, table.to_string().replace("\r\n", "\n"));
1179        assert_eq!(6, table.print(&mut StringWriter::new()).unwrap());
1180    }
1181
1182    #[test]
1183    fn test_empty_table_with_title() {
1184        let mut table = Table::new();
1185        table.set_format(*format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
1186
1187        table.set_titles(Row::new(vec![Cell::new("Title 1"), Cell::new("Title 2")]));
1188
1189        let out = "\
1190+---------+---------+
1191| Title 1 | Title 2 |
1192+---------+---------+
1193+---------+---------+
1194";
1195        println!("{}", out);
1196        println!("____");
1197        println!("{}", table.to_string().replace("\r\n", "\n"));
1198        assert_eq!(out, table.to_string().replace("\r\n", "\n"));
1199        assert_eq!(4, table.print(&mut StringWriter::new()).unwrap());
1200    }
1201
1202    #[test]
1203    fn test_horizontal_span() {
1204        let mut table = Table::new();
1205        table.set_titles(Row::new(vec![
1206            Cell::new("t1"),
1207            Cell::new("t2").with_hspan(2),
1208        ]));
1209        table.add_row(Row::new(vec![
1210            Cell::new("a"),
1211            Cell::new("bc"),
1212            Cell::new("def"),
1213        ]));
1214        table.add_row(Row::new(vec![
1215            Cell::new("def").style_spec("H02c"),
1216            Cell::new("a"),
1217        ]));
1218        let out = "\
1219+----+----+-----+
1220| t1 | t2       |
1221+====+====+=====+
1222| a  | bc | def |
1223+----+----+-----+
1224|   def   | a   |
1225+----+----+-----+
1226";
1227        println!("{}", out);
1228        println!("____");
1229        println!("{}", table.to_string().replace("\r\n", "\n"));
1230        assert_eq!(out, table.to_string().replace("\r\n", "\n"));
1231        assert_eq!(7, table.print(&mut StringWriter::new()).unwrap());
1232    }
1233
1234    #[test]
1235    fn table_html() {
1236        let mut table = Table::new();
1237        table.add_row(Row::new(vec![
1238            Cell::new("a"),
1239            Cell::new("bc"),
1240            Cell::new("def"),
1241        ]));
1242        table.add_row(Row::new(vec![
1243            Cell::new("def"),
1244            Cell::new("bc"),
1245            Cell::new("a"),
1246        ]));
1247        table.set_titles(Row::new(vec![
1248            Cell::new("t1"),
1249            Cell::new("t2"),
1250            Cell::new("t3"),
1251        ]));
1252        let out = "\
1253<table>\
1254<th><td style=\"text-align: left;\">t1</td><td style=\"text-align: left;\">t2</td><td style=\"text-align: left;\">t3</td></th>\
1255<tr><td style=\"text-align: left;\">a</td><td style=\"text-align: left;\">bc</td><td style=\"text-align: left;\">def</td></tr>\
1256<tr><td style=\"text-align: left;\">def</td><td style=\"text-align: left;\">bc</td><td style=\"text-align: left;\">a</td></tr>\
1257</table>";
1258        let mut writer = StringWriter::new();
1259        assert!(table.print_html(&mut writer).is_ok());
1260        assert_eq!(writer.as_string().replace("\r\n", "\n"), out);
1261        table.unset_titles();
1262        let out = "\
1263<table>\
1264<tr><td style=\"text-align: left;\">a</td><td style=\"text-align: left;\">bc</td><td style=\"text-align: left;\">def</td></tr>\
1265<tr><td style=\"text-align: left;\">def</td><td style=\"text-align: left;\">bc</td><td style=\"text-align: left;\">a</td></tr>\
1266</table>";
1267        let mut writer = StringWriter::new();
1268        assert!(table.print_html(&mut writer).is_ok());
1269        assert_eq!(writer.as_string().replace("\r\n", "\n"), out);
1270    }
1271
1272    #[test]
1273    fn table_html_colors() {
1274        let mut table = Table::new();
1275        table.add_row(Row::new(vec![
1276            Cell::new("bold").style_spec("b"),
1277            Cell::new("italic").style_spec("i"),
1278            Cell::new("underline").style_spec("u"),
1279        ]));
1280        table.add_row(Row::new(vec![
1281            Cell::new("left").style_spec("l"),
1282            Cell::new("center").style_spec("c"),
1283            Cell::new("right").style_spec("r"),
1284        ]));
1285        table.add_row(Row::new(vec![
1286            Cell::new("red").style_spec("Fr"),
1287            Cell::new("black").style_spec("Fd"),
1288            Cell::new("yellow").style_spec("Fy"),
1289        ]));
1290        table.add_row(Row::new(vec![
1291            Cell::new("bright magenta on cyan").style_spec("FMBc"),
1292            Cell::new("white on bright green").style_spec("FwBG"),
1293            Cell::new("default on blue").style_spec("Bb"),
1294        ]));
1295        table.set_titles(Row::new(
1296            vec![Cell::new("span horizontal").style_spec("H3")],
1297        ));
1298        let out = "\
1299<table>\
1300<th><td colspan=\"3\" style=\"text-align: left;\">span horizontal</td></th>\
1301<tr><td style=\"font-weight: bold;text-align: left;\">bold</td><td style=\"font-style: italic;text-align: left;\">italic</td><td style=\"text-decoration: underline;text-align: left;\">underline</td></tr>\
1302<tr><td style=\"text-align: left;\">left</td><td style=\"text-align: center;\">center</td><td style=\"text-align: right;\">right</td></tr>\
1303<tr><td style=\"color: #aa0000;text-align: left;\">red</td><td style=\"color: #000000;text-align: left;\">black</td><td style=\"color: #aa5500;text-align: left;\">yellow</td></tr>\
1304<tr><td style=\"color: #ff55ff;background-color: #00aaaa;text-align: left;\">bright magenta on cyan</td><td style=\"color: #aaaaaa;background-color: #55ff55;text-align: left;\">white on bright green</td><td style=\"background-color: #0000aa;text-align: left;\">default on blue</td></tr>\
1305</table>";
1306        let mut writer = StringWriter::new();
1307        assert!(table.print_html(&mut writer).is_ok());
1308        assert_eq!(writer.as_string().replace("\r\n", "\n"), out);
1309    }
1310
1311    #[test]
1312    fn test_panic() {
1313        let mut table = Table::new();
1314
1315        table.add_row(Row::new(vec![Cell::new("\u{1b}[\u{1b}\u{0}\u{0}")]));
1316
1317        let out = "+--+
1318| \u{1b}[\u{1b}\u{0}\u{0} |
1319+--+
1320";
1321
1322        assert_eq!(table.to_string().replace("\r\n", "\n"), out);
1323        assert_eq!(3, table.print(&mut StringWriter::new()).unwrap());
1324    }
1325}