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