From 0b633eb06f99a24f54070fe460068b2fa5ce1e13 Mon Sep 17 00:00:00 2001 From: Pierre-Henri Symoneaux Date: Mon, 29 May 2017 22:10:07 +0200 Subject: Applied rustfmt on code --- src/cell.rs | 659 ++++++++++++------------- src/format.rs | 514 ++++++++++---------- src/lib.rs | 1484 +++++++++++++++++++++++++++++---------------------------- src/main.rs | 91 ++-- src/row.rs | 444 ++++++++--------- src/utils.rs | 245 +++++----- 6 files changed, 1778 insertions(+), 1659 deletions(-) (limited to 'src') diff --git a/src/cell.rs b/src/cell.rs index 40a1141..18aadd0 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -1,114 +1,114 @@ -//! This module contains definition of table/row cells stuff - -use std::io::{Write, Error}; -use std::string::ToString; -use unicode_width::UnicodeWidthStr; -use term::{Attr, Terminal, color}; -use super::format::Alignment; -use super::utils::print_align; - -/// Represent a table cell containing a string. -/// -/// Once created, a cell's content cannot be modified. -/// The cell would have to be replaced by another one -#[derive(Clone, Debug)] -pub struct Cell { - 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 { +//! This module contains definition of table/row cells stuff + +use std::io::{Write, Error}; +use std::string::ToString; +use unicode_width::UnicodeWidthStr; +use term::{Attr, Terminal, color}; +use super::format::Alignment; +use super::utils::print_align; + +/// Represent a table cell containing a string. +/// +/// Once created, a cell's content cannot be modified. +/// The cell would have to be replaced by another one +#[derive(Clone, Debug)] +pub struct Cell { + 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, @@ -126,18 +126,21 @@ impl Cell { '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 { + _ => { + // 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, @@ -147,209 +150,225 @@ impl Cell { '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 { + '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)); + 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 { + 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 { match te { ::term::Error::Io(why) => why, _ => Error::new(::std::io::ErrorKind::Other, te), - } -} - -impl <'a, T: ToString> From<&'a T> for Cell { - fn from(f: &T) -> Cell { - Cell::new(&f.to_string()) - } -} - -impl ToString for Cell { - 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() - } - } -} - -/// This macro simplifies `Cell` creation -/// -/// Support 2 syntax : With and without style specification. -/// # Syntax -/// ```text -/// cell!(value); -/// ``` -/// or -/// -/// ```text -/// cell!(spec:value); -/// ``` -/// Value must implement the `std::string::ToString` trait -/// -/// For details about style specifier syntax, check doc for [Cell::style_spec](cell/struct.Cell.html#method.style_spec) method -/// # Example -/// ``` -/// # #[macro_use] extern crate prettytable; -/// # fn main() { -/// let cell = cell!("value"); -/// // Do something with the cell -/// # drop(cell); -/// // Create a cell with style (Red foreground, Bold, aligned to left); -/// let styled = cell!(Frbl->"value"); -/// # drop(styled); -/// # } -/// ``` -#[macro_export] + } +} + +impl<'a, T: ToString> From<&'a T> for Cell { + fn from(f: &T) -> Cell { + Cell::new(&f.to_string()) + } +} + +impl ToString for Cell { + 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(), + } + } +} + +/// This macro simplifies `Cell` creation +/// +/// Support 2 syntax : With and without style specification. +/// # Syntax +/// ```text +/// cell!(value); +/// ``` +/// or +/// +/// ```text +/// cell!(spec:value); +/// ``` +/// Value must implement the `std::string::ToString` trait +/// +/// For details about style specifier syntax, check doc for [Cell::style_spec](cell/struct.Cell.html#method.style_spec) method +/// # Example +/// ``` +/// # #[macro_use] extern crate prettytable; +/// # fn main() { +/// let cell = cell!("value"); +/// // Do something with the cell +/// # drop(cell); +/// // Create a cell with style (Red foreground, Bold, aligned to left); +/// let styled = cell!(Frbl->"value"); +/// # drop(styled); +/// # } +/// ``` +#[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))); -} - -#[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); - } -} +} + +#[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); + } +} diff --git a/src/format.rs b/src/format.rs index 8ff16cc..0293b67 100644 --- a/src/format.rs +++ b/src/format.rs @@ -1,259 +1,281 @@ -//! Define table formatting utilities - -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 -} - -/// Position of a line separator in a table -#[derive(Clone, Debug, PartialEq, Copy)] -pub enum LinePosition { - Top, - Title, - Intern, - Bottom -} - -/// Position of a column separator in a row -#[derive(Clone, Debug, PartialEq, Copy)] -pub enum ColumnPosition { - 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 -} - -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) - } -} - -impl Default for LineSeparator { - 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 -} - -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) { +//! Define table formatting utilities + +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, +} + +/// Position of a line separator in a table +#[derive(Clone, Debug, PartialEq, Copy)] +pub enum LinePosition { + Top, + Title, + Intern, + Bottom, +} + +/// Position of a column separator in a row +#[derive(Clone, Debug, PartialEq, Copy)] +pub enum ColumnPosition { + 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, +} + +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) + } +} + +impl Default for LineSeparator { + 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, +} + +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 { + 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 + 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> { + } + } + + /// 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 { + 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> { + 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() - } -} - -/// A builder to create a `TableFormat` -pub struct FormatBuilder { - 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 - } -} - -/// Predifined formats. Those constants are lazily evaluated when -/// the corresponding struct is dereferenced -pub mod consts { - use super::{TableFormat, LineSeparator, FormatBuilder, LinePosition}; - + None => Ok(()), + } + } +} + +impl Default for TableFormat { + fn default() -> Self { + TableFormat::new() + } +} + +/// A builder to create a `TableFormat` +pub struct FormatBuilder { + 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 + } +} + +/// 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('-', '+', '+', '+'); @@ -424,5 +446,5 @@ pub mod consts { .separator(LinePosition::Title, *MINUS_PLUS_SEP) .column_separator('|') .build(); - } -} + } +} diff --git a/src/lib.rs b/src/lib.rs index c916dd3..f3111e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,566 +1,591 @@ -//! A formatted and aligned table printer written in rust -extern crate unicode_width; -extern crate term; -extern crate atty; -#[cfg(feature = "csv")] -extern crate csv; -#[macro_use] extern crate lazy_static; -extern crate encode_unicode; - -use std::io::{self, Write, Error}; -#[cfg(feature = "csv")] -use std::io::Read; -use std::fmt; -#[cfg(feature = "csv")] -use std::path::Path; -use std::iter::{FromIterator, IntoIterator}; -use std::slice::{Iter, IterMut}; -use std::ops::{Index, IndexMut}; -use std::mem::transmute; - -use term::{Terminal, stdout}; - -pub mod cell; -pub mod row; -pub mod format; -mod utils; - -use row::Row; -use cell::Cell; -use format::{TableFormat, LinePosition, consts}; -use utils::StringWriter; - -/// An owned printable table -#[derive(Clone, Debug)] -pub struct Table { - format: Box, - titles: Box>, - rows: Vec -} - -/// A borrowed immutable `Table` slice -/// A `TableSlice` is obtained by slicing a `Table` with the `Slice::slice` method. -/// -/// # Examples -/// ```rust -/// # #[macro_use] extern crate prettytable; -/// use prettytable::{Table, Slice}; -/// # fn main() { -/// let table = table![[1, 2, 3], [4, 5, 6], [7, 8, 9]]; -/// let slice = table.slice(1..); -/// slice.printstd(); // Prints only rows 1 and 2 -/// -/// //Also supports other syntax : -/// table.slice(..); -/// table.slice(..2); -/// table.slice(1..3); -/// # } -/// ``` -/// -#[derive(Clone, Debug)] -pub struct TableSlice<'a> { - 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 { +//! A formatted and aligned table printer written in rust +extern crate unicode_width; +extern crate term; +extern crate atty; +#[cfg(feature = "csv")] +extern crate csv; +#[macro_use] +extern crate lazy_static; +extern crate encode_unicode; + +use std::io::{self, Write, Error}; +#[cfg(feature = "csv")] +use std::io::Read; +use std::fmt; +#[cfg(feature = "csv")] +use std::path::Path; +use std::iter::{FromIterator, IntoIterator}; +use std::slice::{Iter, IterMut}; +use std::ops::{Index, IndexMut}; +use std::mem::transmute; + +use term::{Terminal, stdout}; + +pub mod cell; +pub mod row; +pub mod format; +mod utils; + +use row::Row; +use cell::Cell; +use format::{TableFormat, LinePosition, consts}; +use utils::StringWriter; + +/// An owned printable table +#[derive(Clone, Debug)] +pub struct Table { + format: Box, + titles: Box>, + rows: Vec, +} + +/// A borrowed immutable `Table` slice +/// A `TableSlice` is obtained by slicing a `Table` with the `Slice::slice` method. +/// +/// # Examples +/// ```rust +/// # #[macro_use] extern crate prettytable; +/// use prettytable::{Table, Slice}; +/// # fn main() { +/// let table = table![[1, 2, 3], [4, 5, 6], [7, 8, 9]]; +/// let slice = table.slice(1..); +/// slice.printstd(); // Prints only rows 1 and 2 +/// +/// //Also supports other syntax : +/// table.slice(..); +/// table.slice(..2); +/// table.slice(1..3); +/// # } +/// ``` +/// +#[derive(Clone, Debug)] +pub struct TableSlice<'a> { + 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) { + 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() - } -} - -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 - pub fn row_iter<'a>(&'a self) -> Iter<'a, Row> { - self.rows.iter() - } - - /// Returns an iterator over mutable rows - pub fn row_iter_mut<'a>(&'a mut self) -> IterMut<'a, Row> { - self.rows.iter_mut() - } - - /// Print the table to `out` - pub fn print(&self, out: &mut T) -> Result<(), Error> { - self.as_ref().print(out) - } - - /// Print the table to terminal `out`, applying styles when needed - pub fn print_term(&self, out: &mut T) -> Result<(), Error> { - self.as_ref().print_term(out) - } - - /// 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) { - self.as_ref().print_tty(force_colorize); - } - - /// 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.as_ref().printstd(); - } - - /// Write the table to the specified writer. - #[cfg(feature = "csv")] - pub fn to_csv(&self, w: W) -> csv::Result> { - self.as_ref().to_csv(w) - } - - /// Write the table to the specified writer. - /// - /// This allows for format customisation. - #[cfg(feature = "csv")] - pub fn to_csv_writer(&self, writer: csv::Writer) -> csv::Result> { - self.as_ref().to_csv_writer(writer) - } -} - -impl Index for Table { - type Output = Row; - fn index(&self, idx: usize) -> &Self::Output { - &self.rows[idx] - } -} - -impl <'a> Index for TableSlice<'a> { - type Output = Row; - fn index(&self, idx: usize) -> &Self::Output { - &self.rows[idx] - } -} - -impl IndexMut for Table { - fn index_mut(&mut self, idx: usize) -> &mut Self::Output { - &mut self.rows[idx] - } -} - -impl fmt::Display for Table { - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - self.as_ref().fmt(fmt) - } -} - -impl <'a> fmt::Display for TableSlice<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - let mut writer = StringWriter::new(); - if let Err(_) = self.print(&mut writer) { - return Err(fmt::Error) - } - fmt.write_str(writer.as_string()) - } -} - -impl > FromIterator for Table { - fn from_iter(iterator: T) -> Table where T: IntoIterator { - Self::init(iterator.into_iter().map(|r| Row::from(r)).collect()) - } -} - -impl From for Table where B: ToString, A: IntoIterator, T : IntoIterator { - fn from(it: T) -> Table { - Self::from_iter(it) - } -} - -impl <'a> IntoIterator for &'a Table { - type Item=&'a Row; - type IntoIter=Iter<'a, Row>; - fn into_iter(self) -> Self::IntoIter { - self.as_ref().row_iter() - } -} - -impl <'a> IntoIterator for &'a mut Table { - type Item=&'a mut Row; - type IntoIter=IterMut<'a, Row>; - fn into_iter(self) -> Self::IntoIter { - self.row_iter_mut() - } -} - -/// Iterator over immutable cells in a column -pub struct ColumnIter<'a>(std::slice::Iter<'a, Row>, usize); - -impl <'a> std::iter::Iterator for ColumnIter<'a> { - type Item = &'a Cell; - fn next(&mut self) -> Option<&'a Cell> { - self.0.next().and_then(|row| row.get_cell(self.1)) - } -} - -/// Iterator over mutable cells in a column -pub struct ColumnIterMut<'a>(std::slice::IterMut<'a, Row>, usize); - -impl <'a> std::iter::Iterator for ColumnIterMut<'a> { - type Item = &'a mut Cell; - fn next(&mut self) -> Option<&'a mut Cell> { - self.0.next().and_then(|row| row.get_mut_cell(self.1)) - } -} - -impl <'a> AsRef> for TableSlice<'a> { - fn as_ref(&self) -> &TableSlice<'a> { - self - } -} - -impl <'a> AsRef> for Table { - fn as_ref(&self) -> &TableSlice<'a> { - unsafe { - // All this is a bit hacky. Let's try to find something else - let s = &mut *((self as *const Table) as *mut Table); - s.rows.shrink_to_fit(); - transmute(self) - } - } -} - -/// Trait implemented by types which can be sliced -pub trait Slice<'a, E> { - /// Type output after slicing - type Output: 'a; - /// Get a slice from self - fn slice(&'a self, arg: E) -> Self::Output; -} - -impl <'a, T, E> Slice<'a, E> for T where T: AsRef>, [Row]: Index { - type Output = TableSlice<'a>; - fn slice(&'a self, arg: E) -> Self::Output { - let sl = self.as_ref(); - TableSlice { - format: sl.format, - titles: sl.titles, - rows: sl.rows.index(arg) - } - } -} - -/// Create a table filled with some values -/// -/// All the arguments used for elements must implement the `std::string::ToString` trait -/// # Syntax -/// ```text -/// table!([Element1_ row1, Element2_ row1, ...], [Element1_row2, ...], ...); -/// ``` -/// -/// # Example -/// ``` -/// # #[macro_use] extern crate prettytable; -/// # fn main() { -/// // Create a table initialized with some rows : -/// let tab = table!(["Element1", "Element2", "Element3"], -/// [1, 2, 3], -/// ["A", "B", "C"] -/// ); -/// # drop(tab); -/// # } -/// ``` -/// -/// Some style can also be given in table creation -/// -/// ``` -/// # #[macro_use] extern crate prettytable; -/// # fn main() { -/// let tab = table!([FrByl->"Element1", Fgc->"Element2", "Element3"], -/// [FrBy => 1, 2, 3], -/// ["A", "B", "C"] -/// ); -/// # drop(tab); -/// # } -/// ``` -/// -/// For details about style specifier syntax, check doc for [Cell::style_spec](cell/struct.Cell.html#method.style_spec) method -#[macro_export] + }; + 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::Resul