From c771c45b69de6b1c432e070cf6ac4d58c885488c Mon Sep 17 00:00:00 2001 From: Pierre-Henri Symoneaux Date: Mon, 29 May 2017 20:35:04 +0200 Subject: Converted all indentation tabs to 4 spaces Fixes #53 --- src/cell.rs | 570 +++++++++++++++--------------- src/format.rs | 746 +++++++++++++++++++-------------------- src/lib.rs | 1084 ++++++++++++++++++++++++++++----------------------------- src/main.rs | 22 +- src/row.rs | 294 ++++++++-------- src/utils.rs | 180 +++++----- 6 files changed, 1448 insertions(+), 1448 deletions(-) (limited to 'src') diff --git a/src/cell.rs b/src/cell.rs index 7b57106..40a1141 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -13,186 +13,186 @@ use super::utils::print_align; /// The cell would have to be replaced by another one #[derive(Clone, Debug)] pub struct Cell { - content: Vec, - width: usize, - align: Alignment, - style: Vec + content: Vec, + width: usize, + align: Alignment, + style: Vec } impl Cell { - /// Create a new `Cell` initialized with content from `string`. - /// Text alignment in cell is configurable with the `align` argument - pub fn new_align(string: &str, align: Alignment) -> Cell { - let content: Vec = string.lines().map(|ref x| x.to_string()).collect(); - let mut width = 0; - for cont in &content { - let l = UnicodeWidthStr::width(&cont[..]); - if l > width { - width = l; - } - } - Cell { - content: content, - width: width, - align: align, - style: Vec::new() - } - } - - /// Create a new `Cell` initialized with content from `string`. - /// By default, content is align to `LEFT` - pub fn new(string: &str) -> Cell { - Cell::new_align(string, Alignment::LEFT) - } - - /// Set text alignment in the cell - pub fn align(&mut self, align: Alignment) { - self.align = align; - } - - /// Add a style attribute to the cell - pub fn style(&mut self, attr: Attr) { - self.style.push(attr); - } - - /// Add a style attribute to the cell. Can be chained - pub fn with_style(mut self, attr: Attr) -> Cell { - self.style(attr); - self - } - - /// Remove all style attributes and reset alignment to default (LEFT) - pub fn reset_style(&mut self) { - self.style.clear(); - self.align(Alignment::LEFT); - } - - /// Set the cell's style by applying the given specifier string - /// - /// # Style spec syntax - /// - /// The syntax for the style specifier looks like this : - /// **FrBybl** which means **F**oreground **r**ed **B**ackground **y**ellow **b**old **l**eft - /// - /// ### List of supported specifiers : - /// - /// * **F** : **F**oreground (must be followed by a color specifier) - /// * **B** : **B**ackground (must be followed by a color specifier) - /// * **b** : **b**old - /// * **i** : **i**talic - /// * **u** : **u**nderline - /// * **c** : Align **c**enter - /// * **l** : Align **l**eft - /// * **r** : Align **r**ight - /// * **d** : **d**efault style - /// - /// ### List of color specifiers : - /// - /// * **r** : Red - /// * **b** : Blue - /// * **g** : Green - /// * **y** : Yellow - /// * **c** : Cyan - /// * **m** : Magenta - /// * **w** : White - /// * **d** : Black - /// - /// And capital letters are for **bright** colors. - /// Eg : - /// - /// * **R** : Bright Red - /// * **B** : Bright Blue - /// * ... and so on ... - pub fn style_spec(mut self, spec: &str) -> Cell { - self.reset_style(); - let mut foreground = false; - let mut background = false; - for c in spec.chars() { - if foreground || background { - let color = match c { - 'r' => color::RED, - 'R' => color::BRIGHT_RED, - 'b' => color::BLUE, - 'B' => color::BRIGHT_BLUE, - 'g' => color::GREEN, - 'G' => color::BRIGHT_GREEN, - 'y' => color::YELLOW, - 'Y' => color::BRIGHT_YELLOW, - 'c' => color::CYAN, - 'C' => color::BRIGHT_CYAN, - 'm' => color::MAGENTA, - 'M' => color::BRIGHT_MAGENTA, - 'w' => color::WHITE, - 'W' => color::BRIGHT_WHITE, - 'd' => color::BLACK, - 'D' => color::BRIGHT_BLACK, - _ => { // Silently ignore unknown tags - foreground = false; - background = false; - continue; - } - }; - if foreground { self.style(Attr::ForegroundColor(color)); } - else if background { self.style(Attr::BackgroundColor(color)); } - foreground = false; - background = false; - } - else { - match c { - 'F' => foreground = true, - 'B' => background = true, - 'b' => self.style(Attr::Bold), - 'i' => self.style(Attr::Italic(true)), - 'u' => self.style(Attr::Underline(true)), - 'c' => self.align(Alignment::CENTER), - 'l' => self.align(Alignment::LEFT), - 'r' => self.align(Alignment::RIGHT), - 'd' => {/* Default : do nothing */} - _ => {/* Silently ignore unknown tags */} - } - } - } - return self; - } - - /// Return the height of the cell - pub fn get_height(&self) -> usize { - self.content.len() - } - - /// Return the width of the cell - pub fn get_width(&self) -> usize { - self.width - } - - /// Return a copy of the full string contained in the cell - pub fn get_content(&self) -> String { - self.content.join("\n") - } - - /// Print a partial cell to `out`. Since the cell may be multi-lined, - /// `idx` is the line index to print. `col_width` is the column width used to - /// fill the cells with blanks so it fits in the table. - /// If `ìdx` is higher than this cell's height, it will print empty content - pub fn print(&self, out: &mut T, idx: usize, col_width: usize, skip_right_fill: bool) -> Result<(), Error> { - let c = self.content.get(idx).map(|s| s.as_ref()).unwrap_or(""); - print_align(out, self.align, c, ' ', col_width, skip_right_fill) - } - - /// Apply style then call `print` to print the cell into a terminal - pub fn print_term(&self, out: &mut T, idx: usize, col_width: usize, skip_right_fill: bool) -> Result<(), Error> { - for a in &self.style { - match out.attr(a.clone()) { - Ok(..) | Err(::term::Error::NotSupported) | Err(::term::Error::ColorOutOfRange) => (), // Ignore unsupported atrributes - Err(e) => return Err(term_error_to_io_error(e)) - }; - } - try!(self.print(out, idx, col_width, skip_right_fill)); - match out.reset() { - Ok(..) | Err(::term::Error::NotSupported) | Err(::term::Error::ColorOutOfRange) => Ok(()), - Err(e) => Err(term_error_to_io_error(e)) - } - } + /// Create a new `Cell` initialized with content from `string`. + /// Text alignment in cell is configurable with the `align` argument + pub fn new_align(string: &str, align: Alignment) -> Cell { + let content: Vec = string.lines().map(|ref x| x.to_string()).collect(); + let mut width = 0; + for cont in &content { + let l = UnicodeWidthStr::width(&cont[..]); + if l > width { + width = l; + } + } + Cell { + content: content, + width: width, + align: align, + style: Vec::new() + } + } + + /// Create a new `Cell` initialized with content from `string`. + /// By default, content is align to `LEFT` + pub fn new(string: &str) -> Cell { + Cell::new_align(string, Alignment::LEFT) + } + + /// Set text alignment in the cell + pub fn align(&mut self, align: Alignment) { + self.align = align; + } + + /// Add a style attribute to the cell + pub fn style(&mut self, attr: Attr) { + self.style.push(attr); + } + + /// Add a style attribute to the cell. Can be chained + pub fn with_style(mut self, attr: Attr) -> Cell { + self.style(attr); + self + } + + /// Remove all style attributes and reset alignment to default (LEFT) + pub fn reset_style(&mut self) { + self.style.clear(); + self.align(Alignment::LEFT); + } + + /// Set the cell's style by applying the given specifier string + /// + /// # Style spec syntax + /// + /// The syntax for the style specifier looks like this : + /// **FrBybl** which means **F**oreground **r**ed **B**ackground **y**ellow **b**old **l**eft + /// + /// ### List of supported specifiers : + /// + /// * **F** : **F**oreground (must be followed by a color specifier) + /// * **B** : **B**ackground (must be followed by a color specifier) + /// * **b** : **b**old + /// * **i** : **i**talic + /// * **u** : **u**nderline + /// * **c** : Align **c**enter + /// * **l** : Align **l**eft + /// * **r** : Align **r**ight + /// * **d** : **d**efault style + /// + /// ### List of color specifiers : + /// + /// * **r** : Red + /// * **b** : Blue + /// * **g** : Green + /// * **y** : Yellow + /// * **c** : Cyan + /// * **m** : Magenta + /// * **w** : White + /// * **d** : Black + /// + /// And capital letters are for **bright** colors. + /// Eg : + /// + /// * **R** : Bright Red + /// * **B** : Bright Blue + /// * ... and so on ... + pub fn style_spec(mut self, spec: &str) -> Cell { + self.reset_style(); + let mut foreground = false; + let mut background = false; + for c in spec.chars() { + if foreground || background { + let color = match c { + 'r' => color::RED, + 'R' => color::BRIGHT_RED, + 'b' => color::BLUE, + 'B' => color::BRIGHT_BLUE, + 'g' => color::GREEN, + 'G' => color::BRIGHT_GREEN, + 'y' => color::YELLOW, + 'Y' => color::BRIGHT_YELLOW, + 'c' => color::CYAN, + 'C' => color::BRIGHT_CYAN, + 'm' => color::MAGENTA, + 'M' => color::BRIGHT_MAGENTA, + 'w' => color::WHITE, + 'W' => color::BRIGHT_WHITE, + 'd' => color::BLACK, + 'D' => color::BRIGHT_BLACK, + _ => { // Silently ignore unknown tags + foreground = false; + background = false; + continue; + } + }; + if foreground { self.style(Attr::ForegroundColor(color)); } + else if background { self.style(Attr::BackgroundColor(color)); } + foreground = false; + background = false; + } + else { + match c { + 'F' => foreground = true, + 'B' => background = true, + 'b' => self.style(Attr::Bold), + 'i' => self.style(Attr::Italic(true)), + 'u' => self.style(Attr::Underline(true)), + 'c' => self.align(Alignment::CENTER), + 'l' => self.align(Alignment::LEFT), + 'r' => self.align(Alignment::RIGHT), + 'd' => {/* Default : do nothing */} + _ => {/* Silently ignore unknown tags */} + } + } + } + return self; + } + + /// Return the height of the cell + pub fn get_height(&self) -> usize { + self.content.len() + } + + /// Return the width of the cell + pub fn get_width(&self) -> usize { + self.width + } + + /// Return a copy of the full string contained in the cell + pub fn get_content(&self) -> String { + self.content.join("\n") + } + + /// Print a partial cell to `out`. Since the cell may be multi-lined, + /// `idx` is the line index to print. `col_width` is the column width used to + /// fill the cells with blanks so it fits in the table. + /// If `ìdx` is higher than this cell's height, it will print empty content + pub fn print(&self, out: &mut T, idx: usize, col_width: usize, skip_right_fill: bool) -> Result<(), Error> { + let c = self.content.get(idx).map(|s| s.as_ref()).unwrap_or(""); + print_align(out, self.align, c, ' ', col_width, skip_right_fill) + } + + /// Apply style then call `print` to print the cell into a terminal + pub fn print_term(&self, out: &mut T, idx: usize, col_width: usize, skip_right_fill: bool) -> Result<(), Error> { + for a in &self.style { + match out.attr(a.clone()) { + Ok(..) | Err(::term::Error::NotSupported) | Err(::term::Error::ColorOutOfRange) => (), // Ignore unsupported atrributes + Err(e) => return Err(term_error_to_io_error(e)) + }; + } + try!(self.print(out, idx, col_width, skip_right_fill)); + match out.reset() { + Ok(..) | Err(::term::Error::NotSupported) | Err(::term::Error::ColorOutOfRange) => Ok(()), + Err(e) => Err(term_error_to_io_error(e)) + } + } } fn term_error_to_io_error(te: ::term::Error) -> Error { @@ -203,27 +203,27 @@ fn term_error_to_io_error(te: ::term::Error) -> Error { } impl <'a, T: ToString> From<&'a T> for Cell { - fn from(f: &T) -> Cell { - Cell::new(&f.to_string()) - } + fn from(f: &T) -> Cell { + Cell::new(&f.to_string()) + } } impl ToString for Cell { - fn to_string(&self) -> String { - self.get_content() - } + fn to_string(&self) -> String { + self.get_content() + } } impl Default for Cell { - /// Return a cell initialized with a single empty `String`, with LEFT alignment - fn default() -> Cell { - Cell { - content: vec!["".to_string(); 1], - width: 0, - align: Alignment::LEFT, - style: Vec::new() - } - } + /// Return a cell initialized with a single empty `String`, with LEFT alignment + fn default() -> Cell { + Cell { + content: vec!["".to_string(); 1], + width: 0, + align: Alignment::LEFT, + style: Vec::new() + } + } } /// This macro simplifies `Cell` creation @@ -255,101 +255,101 @@ impl Default for Cell { /// ``` #[macro_export] macro_rules! cell { - () => ($crate::cell::Cell::default()); - ($value:expr) => ($crate::cell::Cell::new(&$value.to_string())); - ($style:ident -> $value:expr) => (cell!($value).style_spec(stringify!($style))); + () => ($crate::cell::Cell::default()); + ($value:expr) => ($crate::cell::Cell::new(&$value.to_string())); + ($style:ident -> $value:expr) => (cell!($value).style_spec(stringify!($style))); } #[cfg(test)] mod tests { - use cell::Cell; - use utils::StringWriter; - use format::Alignment; - use term::{Attr, color}; - - #[test] - fn get_content() { - let cell = Cell::new("test"); - assert_eq!(cell.get_content(), "test"); - } - - #[test] - fn print_ascii() { - let ascii_cell = Cell::new("hello"); - assert_eq!(ascii_cell.get_width(), 5); - - let mut out = StringWriter::new(); - let _ = ascii_cell.print(&mut out, 0, 10, false); - assert_eq!(out.as_string(), "hello "); - } - - #[test] - fn print_unicode() { - let unicode_cell = Cell::new("привет"); - assert_eq!(unicode_cell.get_width(), 6); - - let mut out = StringWriter::new(); - let _ = unicode_cell.print(&mut out, 0, 10, false); - assert_eq!(out.as_string(), "привет "); - } - - #[test] - fn print_cjk() { - let unicode_cell = Cell::new("由系统自动更新"); - assert_eq!(unicode_cell.get_width(), 14); - let mut out = StringWriter::new(); - let _ = unicode_cell.print(&mut out, 0, 20, false); - assert_eq!(out.as_string(), "由系统自动更新 "); - } - - #[test] - fn align_left() { - let cell = Cell::new_align("test", Alignment::LEFT); - let mut out = StringWriter::new(); - let _ = cell.print(&mut out, 0, 10, false); - assert_eq!(out.as_string(), "test "); - } - - #[test] - fn align_center() { - let cell = Cell::new_align("test", Alignment::CENTER); - let mut out = StringWriter::new(); - let _ = cell.print(&mut out, 0, 10, false); - assert_eq!(out.as_string(), " test "); - } - - #[test] - fn align_right() { - let cell = Cell::new_align("test", Alignment::RIGHT); - let mut out = StringWriter::new(); - let _ = cell.print(&mut out, 0, 10, false); - assert_eq!(out.as_string(), " test"); - } - - #[test] - fn style_spec() { - let mut cell = Cell::new("test").style_spec("FrBBbuic"); - assert_eq!(cell.style.len(), 5); - assert!(cell.style.contains(&Attr::Underline(true))); - assert!(cell.style.contains(&Attr::Italic(true))); - assert!(cell.style.contains(&Attr::Bold)); - assert!(cell.style.contains(&Attr::ForegroundColor(color::RED))); - assert!(cell.style.contains(&Attr::BackgroundColor(color::BRIGHT_BLUE))); - assert_eq!(cell.align, Alignment::CENTER); - - cell = cell.style_spec("FDBwr"); - assert_eq!(cell.style.len(), 2); - assert!(cell.style.contains(&Attr::ForegroundColor(color::BRIGHT_BLACK))); - assert!(cell.style.contains(&Attr::BackgroundColor(color::WHITE))); - assert_eq!(cell.align, Alignment::RIGHT) - } - - #[test] - fn reset_style() { - let mut cell = Cell::new("test").style_spec("FDBwr"); - assert_eq!(cell.style.len(), 2); - cell.reset_style(); - assert_eq!(cell.style.len(), 0); - assert_eq!(cell.align, Alignment::LEFT); - } + use cell::Cell; + use utils::StringWriter; + use format::Alignment; + use term::{Attr, color}; + + #[test] + fn get_content() { + let cell = Cell::new("test"); + assert_eq!(cell.get_content(), "test"); + } + + #[test] + fn print_ascii() { + let ascii_cell = Cell::new("hello"); + assert_eq!(ascii_cell.get_width(), 5); + + let mut out = StringWriter::new(); + let _ = ascii_cell.print(&mut out, 0, 10, false); + assert_eq!(out.as_string(), "hello "); + } + + #[test] + fn print_unicode() { + let unicode_cell = Cell::new("привет"); + assert_eq!(unicode_cell.get_width(), 6); + + let mut out = StringWriter::new(); + let _ = unicode_cell.print(&mut out, 0, 10, false); + assert_eq!(out.as_string(), "привет "); + } + + #[test] + fn print_cjk() { + let unicode_cell = Cell::new("由系统自动更新"); + assert_eq!(unicode_cell.get_width(), 14); + let mut out = StringWriter::new(); + let _ = unicode_cell.print(&mut out, 0, 20, false); + assert_eq!(out.as_string(), "由系统自动更新 "); + } + + #[test] + fn align_left() { + let cell = Cell::new_align("test", Alignment::LEFT); + let mut out = StringWriter::new(); + let _ = cell.print(&mut out, 0, 10, false); + assert_eq!(out.as_string(), "test "); + } + + #[test] + fn align_center() { + let cell = Cell::new_align("test", Alignment::CENTER); + let mut out = StringWriter::new(); + let _ = cell.print(&mut out, 0, 10, false); + assert_eq!(out.as_string(), " test "); + } + + #[test] + fn align_right() { + let cell = Cell::new_align("test", Alignment::RIGHT); + let mut out = StringWriter::new(); + let _ = cell.print(&mut out, 0, 10, false); + assert_eq!(out.as_string(), " test"); + } + + #[test] + fn style_spec() { + let mut cell = Cell::new("test").style_spec("FrBBbuic"); + assert_eq!(cell.style.len(), 5); + assert!(cell.style.contains(&Attr::Underline(true))); + assert!(cell.style.contains(&Attr::Italic(true))); + assert!(cell.style.contains(&Attr::Bold)); + assert!(cell.style.contains(&Attr::ForegroundColor(color::RED))); + assert!(cell.style.contains(&Attr::BackgroundColor(color::BRIGHT_BLUE))); + assert_eq!(cell.align, Alignment::CENTER); + + cell = cell.style_spec("FDBwr"); + assert_eq!(cell.style.len(), 2); + assert!(cell.style.contains(&Attr::ForegroundColor(color::BRIGHT_BLACK))); + assert!(cell.style.contains(&Attr::BackgroundColor(color::WHITE))); + assert_eq!(cell.align, Alignment::RIGHT) + } + + #[test] + fn reset_style() { + let mut cell = Cell::new("test").style_spec("FDBwr"); + assert_eq!(cell.style.len(), 2); + cell.reset_style(); + assert_eq!(cell.style.len(), 0); + assert_eq!(cell.align, Alignment::LEFT); + } } diff --git a/src/format.rs b/src/format.rs index bc093ac..8ff16cc 100644 --- a/src/format.rs +++ b/src/format.rs @@ -1,428 +1,428 @@ //! Define table formatting utilities -use std::io::{Write, Error}; - -use encode_unicode::Utf8Char; +use std::io::{Write, Error}; + +use encode_unicode::Utf8Char; use super::utils::NEWLINE; /// Alignment for cell's content #[derive(Clone, Debug, PartialEq, Copy)] pub enum Alignment { - LEFT, - CENTER, - RIGHT + LEFT, + CENTER, + RIGHT } /// Position of a line separator in a table #[derive(Clone, Debug, PartialEq, Copy)] pub enum LinePosition { - Top, - Title, - Intern, - Bottom + Top, + Title, + Intern, + Bottom } /// Position of a column separator in a row #[derive(Clone, Debug, PartialEq, Copy)] pub enum ColumnPosition { - Left, - Intern, - Right + Left, + Intern, + Right } /// Contains the character used for printing a line separator #[derive(Clone, Debug, Copy)] pub struct LineSeparator { - /// Line separator - line: char, - /// Internal junction separator - junc: char, - /// Left junction separator - ljunc: char, - /// Right junction separator - rjunc: char + /// Line separator + line: char, + /// Internal junction separator + junc: char, + /// Left junction separator + ljunc: char, + /// Right junction separator + rjunc: char } impl LineSeparator { - /// Create a new line separator instance where `line` is the character used to separate 2 lines - /// and `junc` is the one used for junctions between columns and lines - pub fn new(line: char, junc: char, ljunc: char, rjunc: char) -> LineSeparator { - LineSeparator{line: line, junc: junc, ljunc: ljunc, rjunc: rjunc} - } - - /// Print a full line separator to `out`. `col_width` is a slice containing the width of each column - pub fn print(&self, out: &mut T, col_width: &[usize], colsep: bool, lborder: bool, rborder: bool) -> Result<(), Error> { - if lborder { - try!(out.write_all(Utf8Char::from(self.ljunc).as_bytes())); - } - let mut iter = col_width.into_iter().peekable(); - while let Some(width) = iter.next() { - for _ in 0..width+2 { - try!(out.write_all(Utf8Char::from(self.line).as_bytes())); - } - if colsep && iter.peek().is_some() { - try!(out.write_all(Utf8Char::from(self.junc).as_bytes())); - } - } - if rborder { - try!(out.write_all(Utf8Char::from(self.rjunc).as_bytes())); - } - out.write_all(NEWLINE) - } + /// Create a new line separator instance where `line` is the character used to separate 2 lines + /// and `junc` is the one used for junctions between columns and lines + pub fn new(line: char, junc: char, ljunc: char, rjunc: char) -> LineSeparator { + LineSeparator{line: line, junc: junc, ljunc: ljunc, rjunc: rjunc} + } + + /// Print a full line separator to `out`. `col_width` is a slice containing the width of each column + pub fn print(&self, out: &mut T, col_width: &[usize], colsep: bool, lborder: bool, rborder: bool) -> Result<(), Error> { + if lborder { + try!(out.write_all(Utf8Char::from(self.ljunc).as_bytes())); + } + let mut iter = col_width.into_iter().peekable(); + while let Some(width) = iter.next() { + for _ in 0..width+2 { + try!(out.write_all(Utf8Char::from(self.line).as_bytes())); + } + if colsep && iter.peek().is_some() { + try!(out.write_all(Utf8Char::from(self.junc).as_bytes())); + } + } + if rborder { + try!(out.write_all(Utf8Char::from(self.rjunc).as_bytes())); + } + out.write_all(NEWLINE) + } } impl Default for LineSeparator { - fn default() -> Self { - LineSeparator::new('-', '+', '+', '+') - } + fn default() -> Self { + LineSeparator::new('-', '+', '+', '+') + } } /// Contains the table formatting rules #[derive(Clone, Debug, Copy)] pub struct TableFormat { - /// Optional column separator character - csep: Option, - /// Optional left border character - lborder: Option, - /// Optional right border character - rborder: Option, - /// Optional internal line separator - lsep: Option, - /// Optional title line separator - tsep: Option, - /// Optional top line separator - top_sep: Option, - /// Optional bottom line separator - bottom_sep: Option, - /// Left padding - pad_left: usize, - /// Right padding - pad_right: usize + /// Optional column separator character + csep: Option, + /// Optional left border character + lborder: Option, + /// Optional right border character + rborder: Option, + /// Optional internal line separator + lsep: Option, + /// Optional title line separator + tsep: Option, + /// Optional top line separator + top_sep: Option, + /// Optional bottom line separator + bottom_sep: Option, + /// Left padding + pad_left: usize, + /// Right padding + pad_right: usize } impl TableFormat { - /// Create a new empty TableFormat. - pub fn new() -> TableFormat { - TableFormat{ - csep: None, - lborder: None, - rborder: None, - lsep: None, - tsep: None, - top_sep: None, - bottom_sep: None, - pad_left: 0, - pad_right: 0 - } - } - - /// Return a tuple with left and right padding - pub fn get_padding(&self) -> (usize, usize) { - (self.pad_left, self.pad_right) - } - - /// Set left and right padding - pub fn padding(&mut self, left: usize, right: usize) { - self.pad_left = left; - self.pad_right = right; - } - - /// Set the character used for internal column separation - pub fn column_separator(&mut self, separator: char) { - self.csep = Some(separator); - } - - /// Set the character used for table borders - pub fn borders(&mut self, border: char) { - self.lborder = Some(border); - self.rborder = Some(border); - } - - /// Set a line separator - pub fn separator(&mut self, what: LinePosition, separator: LineSeparator) { - *match what { - LinePosition::Top => &mut self.top_sep, - LinePosition::Bottom => &mut self.bottom_sep, - LinePosition::Title => &mut self.tsep, - LinePosition::Intern => &mut self.lsep - } = Some(separator); - } - - /// Set format for multiple kind of line separator - pub fn separators(&mut self, what: &[LinePosition], separator: LineSeparator) { - for pos in what { - self.separator(*pos, separator); - } - } - - fn get_sep_for_line(&self, pos: LinePosition) -> &Option { - match pos { - LinePosition::Intern => return &self.lsep, - LinePosition::Top => return &self.top_sep, - LinePosition::Bottom => return &self.bottom_sep, - LinePosition::Title => match &self.tsep { - s @ &Some(_) => s, - &None => &self.lsep - } - } - } - - /// Print a full line separator to `out`. `col_width` is a slice containing the width of each column - pub fn print_line_separator(&self, out: &mut T, col_width: &[usize], pos: LinePosition) -> Result<(), Error> { - match *self.get_sep_for_line(pos) { - Some(ref l) => l.print(out, col_width, self.csep.is_some(), self.lborder.is_some(), self.rborder.is_some()), - None => Ok(()) - } - } - - pub fn get_column_separator(&self, pos: ColumnPosition) -> Option { - match pos { - ColumnPosition::Left => self.lborder, - ColumnPosition::Intern => self.csep, - ColumnPosition::Right => self.rborder - } - } - - /// Print a column separator or a table border - pub fn print_column_separator(&self, out: &mut T, pos: ColumnPosition) -> Result<(), Error> { - match self.get_column_separator(pos) { - Some(s) => out.write_all(Utf8Char::from(s).as_bytes()), - None => Ok(()) - } - } + /// Create a new empty TableFormat. + pub fn new() -> TableFormat { + TableFormat{ + csep: None, + lborder: None, + rborder: None, + lsep: None, + tsep: None, + top_sep: None, + bottom_sep: None, + pad_left: 0, + pad_right: 0 + } + } + + /// Return a tuple with left and right padding + pub fn get_padding(&self) -> (usize, usize) { + (self.pad_left, self.pad_right) + } + + /// Set left and right padding + pub fn padding(&mut self, left: usize, right: usize) { + self.pad_left = left; + self.pad_right = right; + } + + /// Set the character used for internal column separation + pub fn column_separator(&mut self, separator: char) { + self.csep = Some(separator); + } + + /// Set the character used for table borders + pub fn borders(&mut self, border: char) { + self.lborder = Some(border); + self.rborder = Some(border); + } + + /// Set a line separator + pub fn separator(&mut self, what: LinePosition, separator: LineSeparator) { + *match what { + LinePosition::Top => &mut self.top_sep, + LinePosition::Bottom => &mut self.bottom_sep, + LinePosition::Title => &mut self.tsep, + LinePosition::Intern => &mut self.lsep + } = Some(separator); + } + + /// Set format for multiple kind of line separator + pub fn separators(&mut self, what: &[LinePosition], separator: LineSeparator) { + for pos in what { + self.separator(*pos, separator); + } + } + + fn get_sep_for_line(&self, pos: LinePosition) -> &Option { + match pos { + LinePosition::Intern => return &self.lsep, + LinePosition::Top => return &self.top_sep, + LinePosition::Bottom => return &self.bottom_sep, + LinePosition::Title => match &self.tsep { + s @ &Some(_) => s, + &None => &self.lsep + } + } + } + + /// Print a full line separator to `out`. `col_width` is a slice containing the width of each column + pub fn print_line_separator(&self, out: &mut T, col_width: &[usize], pos: LinePosition) -> Result<(), Error> { + match *self.get_sep_for_line(pos) { + Some(ref l) => l.print(out, col_width, self.csep.is_some(), self.lborder.is_some(), self.rborder.is_some()), + None => Ok(()) + } + } + + pub fn get_column_separator(&self, pos: ColumnPosition) -> Option { + match pos { + ColumnPosition::Left => self.lborder, + ColumnPosition::Intern => self.csep, + ColumnPosition::Right => self.rborder + } + } + + /// Print a column separator or a table border + pub fn print_column_separator(&self, out: &mut T, pos: ColumnPosition) -> Result<(), Error> { + match self.get_column_separator(pos) { + Some(s) => out.write_all(Utf8Char::from(s).as_bytes()), + None => Ok(()) + } + } } impl Default for TableFormat { - fn default() -> Self { - TableFormat::new() - } + fn default() -> Self { + TableFormat::new() + } } /// A builder to create a `TableFormat` pub struct FormatBuilder { - format: Box + format: Box } impl FormatBuilder { - pub fn new() -> FormatBuilder { - FormatBuilder { - format: Box::new(TableFormat::new()) - } - } - - /// Set left and right padding - pub fn padding(mut self, left: usize, right: usize) -> Self { - self.format.padding(left, right); - self - } - - /// Set the character used for internal column separation - pub fn column_separator(mut self, separator: char) -> Self { - self.format.column_separator(separator); - self - } - - /// Set the character used for table borders - pub fn borders(mut self, border: char) -> Self { - self.format.borders(border); - self - } - - /// Set a line separator format - pub fn separator(mut self, what: LinePosition, separator: LineSeparator) -> Self { - self.format.separator(what, separator); - self - } - - /// Set separator format for multiple kind of line separators - pub fn separators(mut self, what: &[LinePosition], separator: LineSeparator) -> Self { - self.format.separators(what, separator); - self - } - - /// Consume this builder and return the generated `TableFormat` - pub fn build(self) -> TableFormat { - *self.format - } + pub fn new() -> FormatBuilder { + FormatBuilder { + format: Box::new(TableFormat::new()) + } + } + + /// Set left and right padding + pub fn padding(mut self, left: usize, right: usize) -> Self { + self.format.padding(left, right); + self + } + + /// Set the character used for internal column separation + pub fn column_separator(mut self, separator: char) -> Self { + self.format.column_separator(separator); + self + } + + /// Set the character used for table borders + pub fn borders(mut self, border: char) -> Self { + self.format.borders(border); + self + } + + /// Set a line separator format + pub fn separator(mut self, what: LinePosition, separator: LineSeparator) -> Self { + self.format.separator(what, separator); + self + } + + /// Set separator format for multiple kind of line separators + pub fn separators(mut self, what: &[LinePosition], separator: LineSeparator) -> Self { + self.format.separators(what, separator); + self + } + + /// Consume this builder and return the generated `TableFormat` + pub fn build(self) -> TableFormat { + *self.format + } } /// Predifined formats. Those constants are lazily evaluated when /// the corresponding struct is dereferenced pub mod consts { - use super::{TableFormat, LineSeparator, FormatBuilder, LinePosition}; - - lazy_static! { - /// A line separator made of `-` and `+` - static ref MINUS_PLUS_SEP: LineSeparator = LineSeparator::new('-', '+', '+', '+'); - /// A line separator made of `=` and `+` - static ref EQU_PLUS_SEP: LineSeparator = LineSeparator::new('=', '+', '+', '+'); - - /// Default table format - /// - /// # Example - /// ```text - /// +----+----+ - /// | T1 | T2 | - /// +====+====+ - /// | a | b | - /// +----+----+ - /// | d | c | - /// +----+----+ - /// ``` - pub static ref FORMAT_DEFAULT: TableFormat = FormatBuilder::new() - .column_separator('|') - .borders('|') - .separator(LinePosition::Intern, *MINUS_PLUS_SEP) - .separator(LinePosition::Title, *EQU_PLUS_SEP) - .separator(LinePosition::Bottom, *MINUS_PLUS_SEP) - .separator(LinePosition::Top, *MINUS_PLUS_SEP) - .padding(1, 1) - .build(); - - /// Similar to `FORMAT_DEFAULT` but without special separator after title line - /// - /// # Example - /// ```text - /// +----+----+ - /// | T1 | T2 | - /// +----+----+ - /// | a | b | - /// +----+----+ - /// | c | d | - /// +----+----+ - /// ``` - pub static ref FORMAT_NO_TITLE: TableFormat = FormatBuilder::new() - .column_separator('|') - .borders('|') - .separator(LinePosition::Intern, *MINUS_PLUS_SEP) - .separator(LinePosition::Title, *MINUS_PLUS_SEP) - .separator(LinePosition::Bottom, *MINUS_PLUS_SEP) - .separator(LinePosition::Top, *MINUS_PLUS_SEP) - .padding(1, 1) - .build(); - - /// With no line separator, but with title separator - /// - /// # Example - /// ```text - /// +----+----+ - /// | T1 | T2 | - /// +----+----+ - /// | a | b | - /// | c | d | - /// +----+----+ - /// ``` - pub static ref FORMAT_NO_LINESEP_WITH_TITLE: TableFormat = FormatBuilder::new() - .column_separator('|') - .borders('|') - .separator(LinePosition::Title, *MINUS_PLUS_SEP) - .separator(LinePosition::Bottom, *MINUS_PLUS_SEP) - .separator(LinePosition::Top, *MINUS_PLUS_SEP) - .padding(1, 1) - .build(); - - /// With no line or title separator - /// - /// # Example - /// ```text - /// +----+----+ - /// | T1 | T2 | - /// | a | b | - /// | c | d | - /// +----+----+ - /// ``` - pub static ref FORMAT_NO_LINESEP: TableFormat = FormatBuilder::new() - .column_separator('|') - .borders('|') - .separator(LinePosition::Bottom, *MINUS_PLUS_SEP) - .separator(LinePosition::Top, *MINUS_PLUS_SEP) - .padding(1, 1) - .build(); - - /// No column separator - /// - /// # Example - /// ```text - /// -------- - /// T1 T2 - /// ======== - /// a b - /// -------- - /// d c - /// -------- - /// ``` - pub static ref FORMAT_NO_COLSEP: TableFormat = FormatBuilder::new() - .separator(LinePosition::Intern, *MINUS_PLUS_SEP) - .separator(LinePosition::Title, *EQU_PLUS_SEP) - .separator(LinePosition::Bottom, *MINUS_PLUS_SEP) - .separator(LinePosition::Top, *MINUS_PLUS_SEP) - .padding(1, 1) - .build(); - - /// Format for printing a table without any separators (only alignment) - /// - /// # Example - /// ```text - /// T1 T2 - /// a b - /// d c - /// ``` - pub static ref FORMAT_CLEAN: TableFormat = FormatBuilder::new() - .padding(1, 1) - .build(); - - /// Format for a table with only external borders and title separator - /// - /// # Example - /// ```text - /// +--------+ - /// | T1 T2 | - /// +========+ - /// | a b | - /// | c d | - /// +--------+ - /// ``` - pub static ref FORMAT_BORDERS_ONLY: TableFormat = FormatBuilder::new() - .padding(1, 1) - .separator(LinePosition::Title, *EQU_PLUS_SEP) - .separator(LinePosition::Bottom, *MINUS_PLUS_SEP) - .separator(LinePosition::Top, *MINUS_PLUS_SEP) - .borders('|') - .build(); - - /// A table with no external border - /// - /// # Example - /// ```text - /// T1 | T2 - /// ====+==== - /// a | b - /// ----+---- - /// c | d - /// ``` - pub static ref FORMAT_NO_BORDER: TableFormat = FormatBuilder::new() - .padding(1, 1) - .separator(LinePosition::Intern, *MINUS_PLUS_SEP) - .separator(LinePosition::Title, *EQU_PLUS_SEP) - .column_separator('|') - .build(); - - /// A table with no external border and no line separation - /// - /// # Example - /// ```text - /// T1 | T2 - /// ----+---- - /// a | b - /// c | d - /// ``` - pub static ref FORMAT_NO_BORDER_LINE_SEPARATOR: TableFormat = FormatBuilder::new() - .padding(1, 1) - .separator(LinePosition::Title, *MINUS_PLUS_SEP) - .column_separator('|') - .build(); - } + use super::{TableFormat, LineSeparator, FormatBuilder, LinePosition}; + + lazy_static! { + /// A line separator made of `-` and `+` + static ref MINUS_PLUS_SEP: LineSeparator = LineSeparator::new('-', '+', '+', '+'); + /// A line separator made of `=` and `+` + static ref EQU_PLUS_SEP: LineSeparator = LineSeparator::new('=', '+', '+', '+'); + + /// Default table format + /// + /// # Example + /// ```text + /// +----+----+ + /// | T1 | T2 | + /// +====+====+ + /// | a | b | + /// +----+----+ + /// | d | c | + /// +----+----+ + /// ``` + pub static ref FORMAT_DEFAULT: TableFormat = FormatBuilder::new() + .column_separator('|') + .borders('|') + .separator(LinePosition::Intern, *MINUS_PLUS_SEP) + .separator(LinePosition::Title, *EQU_PLUS_SEP) + .separator(LinePosition::Bottom, *MINUS_PLUS_SEP) + .separator(LinePosition::Top, *MINUS_PLUS_SEP) + .padding(1, 1) + .build(); + + /// Similar to `FORMAT_DEFAULT` but without special separator after title line + /// + /// # Example + /// ```text + /// +----+----+ + /// | T1 | T2 | + /// +----+----+ + /// | a | b | + /// +----+----+ + /// | c | d | + /// +----+----+ + /// ``` + pub static ref FORMAT_NO_TITLE: TableFormat = FormatBuilder::new() + .column_separator('|') + .borders('|') + .separator(LinePosition::Intern, *MINUS_PLUS_SEP) + .separator(LinePosition::Title, *MINUS_PLUS_SEP) + .separator(LinePosition::Bottom, *MINUS_PLUS_SEP) + .separator(LinePosition::Top, *MINUS_PLUS_SEP) + .padding(1, 1) + .build(); + + /// With no line separator, but with title separator + /// + /// # Example + /// ```text + /// +----+----+ + /// | T1 | T2 | + /// +----+----+ + /// | a | b | + /// | c | d | + /// +----+----+ + /// ``` + pub static ref FORMAT_NO_LINESEP_WITH_TITLE: TableFormat = FormatBuilder::new() + .column_separator('|') + .borders('|') + .separator(LinePosition::Title, *MINUS_PLUS_SEP) + .separator(LinePosition::Bottom, *MINUS_PLUS_SEP) + .separator(LinePosition::Top, *MINUS_PLUS_SEP) + .padding(1, 1) + .build(); + + /// With no line or title separator + /// + /// # Example + /// ```text + /// +----+----+ + /// | T1 | T2 | + /// | a | b | + /// | c | d | + /// +----+----+ + /// ``` + pub static ref FORMAT_NO_LINESEP: TableFormat = FormatBuilder::new() + .column_separator('|') + .borders('|') + .separator(LinePosition::Bottom, *MINUS_PLUS_SEP) + .separator(LinePosition::Top, *MINUS_PLUS_SEP) + .padding(1, 1) + .build(); + + /// No column separator + /// + /// # Example + /// ```text + /// -------- + /// T1 T2 + /// ======== + /// a b + /// -------- + /// d c + /// -------- + /// ``` + pub static ref FORMAT_NO_COLSEP: TableFormat = FormatBuilder::new() + .separator(LinePosition::Intern, *MINUS_PLUS_SEP) + .separator(LinePosition::Title, *EQU_PLUS_SEP) + .separator(LinePosition::Bottom, *MINUS_PLUS_SEP) + .separator(LinePosition::Top, *MINUS_PLUS_SEP) + .padding(1, 1) + .build(); + + /// Format for printing a table without any separators (only alignment) + /// + /// # Example + /// ```text + /// T1 T2 + /// a b + /// d c + /// ``` + pub static ref FORMAT_CLEAN: TableFormat = FormatBuilder::new() + .padding(1, 1) + .build(); + + /// Format for a table with only external borders and title separator + /// + /// # Example + /// ```text + /// +--------+ + /// | T1 T2 | + /// +========+ + /// | a b | + /// | c d | + /// +--------+ + /// ``` + pub static ref FORMAT_BORDERS_ONLY: TableFormat = FormatBuilder::new() + .padding(1, 1) + .separator(LinePosition::Title, *EQU_PLUS_SEP) + .separator(LinePosition::Bottom, *MINUS_PLUS_SEP) + .separator(LinePosition::Top, *MINUS_PLUS_SEP) + .borders('|') + .build(); + + /// A table with no external border + /// + /// # Example + /// ```text + /// T1 | T2 + /// ====+==== + /// a | b + /// ----+---- + /// c | d + /// ``` + pub static ref FORMAT_NO_BORDER: TableFormat = FormatBuilder::new() + .padding(1, 1) + .separator(LinePosition::Intern, *MINUS_PLUS_SEP) + .separator(LinePosition::Title, *EQU_PLUS_SEP) + .column_separator('|') + .build(); + + /// A table with no external border and no line separation + /// + /// # Example + /// ```text + /// T1 | T2 + /// ----+---- + /// a | b + /// c | d + /// ``` + pub static ref FORMAT_NO_BORDER_LINE_SEPARATOR: TableFormat = FormatBuilder::new() + .padding(1, 1) + .separator(LinePosition::Title, *MINUS_PLUS_SEP) + .column_separator('|') + .build(); + } } diff --git a/src/lib.rs b/src/lib.rs index 940a630..c916dd3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,9 +33,9 @@ use utils::StringWriter; /// An owned printable table #[derive(Clone, Debug)] pub struct Table { - format: Box, - titles: Box>, - rows: Vec + format: Box, + titles: Box>, + rows: Vec } /// A borrowed immutable `Table` slice @@ -59,460 +59,460 @@ pub struct Table { /// #[derive(Clone, Debug)] pub struct TableSlice<'a> { - format: &'a TableFormat, - titles: &'a Option, - rows: &'a [Row] + format: &'a TableFormat, + titles: &'a Option, + rows: &'a [Row] } impl <'a> TableSlice<'a> { - /// Compute and return the number of column - pub fn get_column_num(&self) -> usize { - let mut cnum = 0; - for r in self.rows { - let l = r.len(); - if l > cnum { - cnum = l; - } - } - cnum - } - - /// Get the number of rows - pub fn len(&self) -> usize { - self.rows.len() - } - - /// Get an immutable reference to a row - pub fn get_row(&self, row: usize) -> Option<&Row> { - self.rows.get(row) - } - - /// Get the width of the column at position `col_idx`. - /// Return 0 if the column does not exists; - fn get_column_width(&self, col_idx: usize) -> usize { - let mut width = match *self.titles { - Some(ref t) => t.get_cell_width(col_idx), - None => 0 - }; - for r in self.rows { - let l = r.get_cell_width(col_idx); - if l > width { - width = l; - } - } - width - } - - /// Get the width of all columns, and return a slice - /// with the result for each column - fn get_all_column_width(&self) -> Vec { - let colnum = self.get_column_num(); - let mut col_width = vec![0usize; colnum]; - for i in 0..colnum { - col_width[i] = self.get_column_width(i); - } - col_width - } - - /// Returns an iterator over the immutable cells of the column specified by `column` - pub fn column_iter(&self, column: usize) -> ColumnIter { - ColumnIter(self.rows.iter(), column) - } - - /// Returns an iterator over immutable rows - pub fn row_iter(&self) -> Iter { - self.rows.iter() - } - - /// Internal only - fn __print(&self, out: &mut T, f: F) -> Result<(), Error> - where F: Fn(&Row, &mut T, &TableFormat, &[usize]) -> Result<(), Error> { - // Compute columns width - let col_width = self.get_all_column_width(); - try!(self.format.print_line_separator(out, &col_width, LinePosition::Top)); - if let Some(ref t) = *self.titles { - try!(f(t, out, &self.format, &col_width)); - try!(self.format.print_line_separator(out, &col_width, LinePosition::Title)); - } - // Print rows - let mut iter = self.rows.into_iter().peekable(); - while let Some(r) = iter.next() { - try!(f(r, out, &self.format, &col_width)); - if iter.peek().is_some() { - try!(self.format.print_line_separator(out, &col_width, LinePosition::Intern)); - } - } - try!(self.format.print_line_separator(out, &col_width, LinePosition::Bottom)); - out.flush() - } - - /// Print the table to `out` - pub fn print(&self, out: &mut T) -> Result<(), Error> { - self.__print(out, Row::print) - } - - /// Print the table to terminal `out`, applying styles when needed - pub fn print_term(&self, out: &mut T) -> Result<(), Error> { - self.__print(out, Row::print_term) - } - - /// Print the table to standard output. Colors won't be displayed unless - /// stdout is a tty terminal, or `force_colorize` is set to `true`. - /// In ANSI terminals, colors are displayed using ANSI escape characters. When for example the - /// output is redirected to a file, or piped to another program, the output is considered - /// as not beeing tty, and ANSI escape characters won't be displayed unless `force colorize` - /// is set to `true`. - /// # Panic - /// Panic if writing to standard output fails - pub fn print_tty(&self, force_colorize: bool) { - let r = match (stdout(), atty::is(atty::Stream::Stdout) || force_colorize) { - (Some(mut o), true) => self.print_term(&mut *o), - _ => self.print(&mut io::stdout()), - }; - if let Err(e) = r { - panic!("Cannot print table to standard output : {}", e); - } - } - - /// Print the table to standard output. Colors won't be displayed unless - /// stdout is a tty terminal. This means that if stdout is redirected to a file, or piped - /// to another program, no color will be displayed. - /// To force colors rendering, use `print_tty()` method. - /// Calling `printstd()` is equivalent to calling `print_tty(false)` - /// # Panic - /// Panic if writing to standard output fails - pub fn printstd(&self) { - self.print_tty(false); - } - - /// Write the table to the specified writer. - #[cfg(feature = "csv")] - pub fn to_csv(&self, w: W) -> csv::Result> { - self.to_csv_writer(csv::Writer::from_writer(w)) - } - - /// Write the table to the specified writer. - /// - /// This allows for format customisation. - #[cfg(feature = "csv")] - pub fn to_csv_writer(&self, mut writer: csv::Writer) -> csv::Result> { - for title in self.titles { - try!(writer.write(title.iter().map(|c| c.get_content()))); - } - for row in self.rows { - try!(writer.write(row.iter().map(|c| c.get_content()))); - } - - try!(writer.flush()); - Ok(writer) - } + /// Compute and return the number of column + pub fn get_column_num(&self) -> usize { + let mut cnum = 0; + for r in self.rows { + let l = r.len(); + if l > cnum { + cnum = l; + } + } + cnum + } + + /// Get the number of rows + pub fn len(&self) -> usize { + self.rows.len() + } + + /// Get an immutable reference to a row + pub fn get_row(&self, row: usize) -> Option<&Row> { + self.rows.get(row) + } + + /// Get the width of the column at position `col_idx`. + /// Return 0 if the column does not exists; + fn get_column_width(&self, col_idx: usize) -> usize { + let mut width = match *self.titles { + Some(ref t) => t.get_cell_width(col_idx), + None => 0 + }; + for r in self.rows { + let l = r.get_cell_width(col_idx); + if l > width { + width = l; + } + } + width + } + + /// Get the width of all columns, and return a slice + /// with the result for each column + fn get_all_column_width(&self) -> Vec { + let colnum = self.get_column_num(); + let mut col_width = vec![0usize; colnum]; + for i in 0..colnum { + col_width[i] = self.get_column_width(i); + } + col_width + } + + /// Returns an iterator over the immutable cells of the column specified by `column` + pub fn column_iter(&self, column: usize) -> ColumnIter { + ColumnIter(self.rows.iter(), column) + } + + /// Returns an iterator over immutable rows + pub fn row_iter(&self) -> Iter { + self.rows.iter() + } + + /// Internal only + fn __print(&self, out: &mut T, f: F) -> Result<(), Error> + where F: Fn(&Row, &mut T, &TableFormat, &[usize]) -> Result<(), Error> { + // Compute columns width + let col_width = self.get_all_column_width(); + try!(self.format.print_line_separator(out, &col_width, LinePosition::Top)); + if let Some(ref t) = *self.titles { + try!(f(t, out, &self.format, &col_width)); + try!(self.format.print_line_separator(out, &col_width, LinePosition::Title)); + } + // Print rows + let mut iter = self.rows.into_iter().peekable(); + while let Some(r) = iter.next() { + try!(f(r, out, &self.format, &col_width)); + if iter.peek().is_some() { + try!(self.format.print_line_separator(out, &col_width, LinePosition::Intern)); + } + } + try!(self.format.print_line_separator(out, &col_width, LinePosition::Bottom)); + out.flush() + } + + /// Print the table to `out` + pub fn print(&self, out: &mut T) -> Result<(), Error> { + self.__print(out, Row::print) + } + + /// Print the table to terminal `out`, applying styles when needed + pub fn print_term(&self, out: &mut T) -> Result<(), Error> { + self.__print(out, Row::print_term) + } + + /// Print the table to standard output. Colors won't be displayed unless + /// stdout is a tty terminal, or `force_colorize` is set to `true`. + /// In ANSI terminals, colors are displayed using ANSI escape characters. When for example the + /// output is redirected to a file, or piped to another program, the output is considered + /// as not beeing tty, and ANSI escape characters won't be displayed unless `force colorize` + /// is set to `true`. + /// # Panic + /// Panic if writing to standard output fails + pub fn print_tty(&self, force_colorize: bool) { + let r = match (stdout(), atty::is(atty::Stream::Stdout) || force_colorize) { + (Some(mut o), true) => self.print_term(&mut *o), + _ => self.print(&mut io::stdout()), + }; + if let Err(e) = r { + panic!("Cannot print table to standard output : {}", e); + } + } + + /// Print the table to standard output. Colors won't be displayed unless + /// stdout is a tty terminal. This means that if stdout is redirected to a file, or piped + /// to another program, no color will be displayed. + /// To force colors rendering, use `print_tty()` method. + /// Calling `printstd()` is equivalent to calling `print_tty(false)` + /// # Panic + /// Panic if writing to standard output fails + pub fn printstd(&self) { + self.print_tty(false); + } + + /// Write the table to the specified writer. + #[cfg(feature = "csv")] + pub fn to_csv(&self, w: W) -> csv::Result> { + self.to_csv_writer(csv::Writer::from_writer(w)) + } + + /// Write the table to the specified writer. + /// + /// This allows for format customisation. + #[cfg(feature = "csv")] + pub fn to_csv_writer(&self, mut writer: csv::Writer) -> csv::Result> { + for title in self.titles { + try!(writer.write(title.iter().map(|c| c.get_content()))); + } + for row in self.rows { + try!(writer.write(row.iter().map(|c| c.get_content()))); + } + + try!(writer.flush()); + Ok(writer) + } } impl <'a> IntoIterator for &'a TableSlice<'a> { - type Item=&'a Row; - type IntoIter=Iter<'a, Row>; - fn into_iter(self) -> Self::IntoIter { - self.row_iter() - } + type Item=&'a Row; + type IntoIter=Iter<'a, Row>; + fn into_iter(self) -> Self::IntoIter { + self.row_iter() + } } impl Table { - /// Create an empty table - pub fn new() -> Table { - Self::init(Vec::new()) - } - - /// Create a table initialized with `rows` - pub fn init(rows: Vec) -> Table { - Table { - rows: rows, - titles: Box::new(None), - format: Box::new(*consts::FORMAT_DEFAULT) - } - } - - /// Create a table from a CSV string - /// - /// For more customisability use `from_csv()` - #[cfg(feature = "csv")] - pub fn from_csv_string(csv_s: &str) -> csv::Result { - Ok(Table::from_csv(&mut csv::Reader::from_string(csv_s).has_headers(false))) - } - - /// Create a table from a CSV file - /// - /// For more customisability use `from_csv()` - #[cfg(feature = "csv")] - pub fn from_csv_file>(csv_p: P) -> csv::Result
{ - Ok(Table::from_csv(&mut try!(csv::Reader::from_file(csv_p)).has_headers(false))) - } - - /// Create a table from a CSV reader - #[cfg(feature = "csv")] - pub fn from_csv(reader: &mut csv::Reader) -> Table { - Table::init(reader.records().map(|row| Row::new(row.unwrap().into_iter().map(|cell| Cell::new(&cell)).collect())).collect()) - } - - /// Change the table format. Eg : Separators - pub fn set_format(&mut self, format: TableFormat) { - *self.format = format; - } - - /// Compute and return the number of column - pub fn get_column_num(&self) -> usize { - self.as_ref().get_column_num() - } - - /// Get the number of rows - pub fn len(&self) -> usize { - self.rows.len() - } - - /// Set the optional title lines - pub fn set_titles(&mut self, titles: Row) { - *self.titles = Some(titles); - } - - /// Unset the title line - pub fn unset_titles(&mut self) { - *self.titles = None; - } - - /// Get a mutable reference to a row - pub fn get_mut_row(&mut self, row: usize) -> Option<&mut Row> { - self.rows.get_mut(row) - } - - /// Get an immutable reference to a row - pub fn get_row(&self, row: usize) -> Option<&Row> { - self.rows.get(row) - } - - /// Append a row in the table, transferring ownership of this row to the table - /// and returning a mutable reference to the row - pub fn add_row(&mut self, row: Row) -> &mut Row { - self.rows.push(row); - let l = self.rows.len()-1; - &mut self.rows[l] - } - - /// Append an empty row in the table. Return a mutable reference to this new row. - pub fn add_empty_row(&mut self) -> &mut Row { - self.add_row(Row::default()) - } - - /// Insert `row` at the position `index`, and return a mutable reference to this row. - /// If index is higher than current numbers of rows, `row` is appended at the end of the table - pub fn insert_row(&mut self, index: usize, row: Row) -> &mut Row { - if index < self.rows.len() { - self.rows.insert(index, row); - &mut self.rows[index] - } else { - self.add_row(row) - } - } - - /// Modify a single element in the table - pub fn set_element(&mut self, element: &str, column: usize, row: usize) -> Result<(), &str> { - let rowline = try!(self.get_mut_row(row).ok_or("Cannot find row")); - // TODO: If a cell already exist, copy it's alignment parameter - rowline.set_cell(Cell::new(element), column) - } - - /// Remove the row at position `index`. Silently skip if the row does not exist - pub fn remove_row(&mut self, index: usize) { - if index < self.rows.len() { - self.rows.remove(index); - } - } - - /// Return an iterator over the immutable cells of the column specified by `column` - pub fn column_iter(&self, column: usize) -> ColumnIter { - ColumnIter(self.rows.iter(), column) - } - - /// Return an iterator over the mutable cells of the column specified by `column` - pub fn column_iter_mut(&mut self, column: usize) -> ColumnIterMut { - ColumnIterMut(self.rows.iter_mut(), column) - } - - /// Returns an iterator over immutable rows + /// Create an empty table + pub fn new() -> Table { + Self::init(Vec::new()) + } + + /// Create a table initialized with `rows` + pub fn init(rows: Vec) -> Table { + Table { + rows: rows, + titles: Box::new(None), + format: Box::new(*consts::FORMAT_DEFAULT) + } + } + + /// Create a table from a CSV string + /// + /// For more customisability use `from_csv()` + #[cfg(feature = "csv")] + pub fn from_csv_string(csv_s: &str) -> csv::Result
{ + Ok(Table::from_csv(&mut