| //! This module contains definition of table rows stuff |
| use std::io::{Write, Error}; |
| use std::iter::FromIterator; |
| use std::slice::{Iter, IterMut}; |
| // use std::vec::IntoIter; |
| use std::ops::{Index, IndexMut}; |
| |
| use super::Terminal; |
| |
| use super::utils::NEWLINE; |
| use super::Cell; |
| use super::format::{TableFormat, ColumnPosition}; |
| |
| /// Represent a table row made of cells |
| #[derive(Clone, Debug, Hash, PartialEq, Eq)] |
| pub struct Row { |
| cells: Vec<Cell>, |
| } |
| |
| impl Row { |
| /// Create a new `Row` backed with `cells` vector |
| pub fn new(cells: Vec<Cell>) -> Row { |
| Row { cells: cells } |
| } |
| |
| /// Create an row of length `size`, with empty strings stored |
| pub fn empty() -> Row { |
| Self::new(vec![Cell::default(); 0]) |
| } |
| |
| /// Count the number of column required in the table grid. |
| /// It takes into account horizontal spanning of cells. For |
| /// example, a cell with an hspan of 3 will add 3 column to the grid |
| #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")] |
| pub fn column_count(&self) -> usize { |
| self.cells.iter().map(|c| c.get_hspan()).sum() |
| } |
| |
| /// Get the number of cells in this row |
| pub fn len(&self) -> usize { |
| self.cells.len() |
| // self.cells.iter().map(|c| c.get_hspan()).sum() |
| } |
| |
| /// Check if the row is empty (has no cell) |
| pub fn is_empty(&self) -> bool { |
| self.cells.is_empty() |
| } |
| |
| /// Get the height of this row |
| #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")] |
| pub fn get_height(&self) -> usize { |
| let mut height = 1; // Minimum height must be 1 to print empty rows |
| for cell in &self.cells { |
| let h = cell.get_height(); |
| if h > height { |
| height = h; |
| } |
| } |
| height |
| } |
| |
| /// Get the minimum width required by the cell in the column `column`. |
| /// Return 0 if the cell does not exist in this row |
| #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")] |
| pub fn get_column_width(&self, column: usize, format: &TableFormat) -> usize { |
| let mut i = 0; |
| for c in &self.cells { |
| if i + c.get_hspan()-1 >= column { |
| if c.get_hspan() == 1 { |
| return c.get_width(); |
| } |
| let (lp, rp) = format.get_padding(); |
| let sep = format.get_column_separator(ColumnPosition::Intern).map(|_| 1).unwrap_or_default(); |
| let rem = lp + rp +sep; |
| let mut w = c.get_width(); |
| if w > rem { |
| w -= rem; |
| } else { |
| w = 0; |
| } |
| return (w as f64 / c.get_hspan() as f64).ceil() as usize; |
| } |
| i += c.get_hspan(); |
| } |
| 0 |
| } |
| |
| /// Get the cell at index `idx` |
| pub fn get_cell(&self, idx: usize) -> Option<&Cell> { |
| self.cells.get(idx) |
| } |
| |
| /// Get the mutable cell at index `idx` |
| pub fn get_mut_cell(&mut self, idx: usize) -> Option<&mut Cell> { |
| self.cells.get_mut(idx) |
| } |
| |
| /// Set the `cell` in the row at the given `idx` index |
| pub fn set_cell(&mut self, cell: Cell, idx: usize) -> Result<(), &str> { |
| if idx >= self.len() { |
| return Err("Cannot find cell"); |
| } |
| self.cells[idx] = cell; |
| Ok(()) |
| } |
| |
| /// Append a `cell` at the end of the row |
| pub fn add_cell(&mut self, cell: Cell) { |
| self.cells.push(cell); |
| } |
| |
| /// Insert `cell` at position `index`. If `index` is higher than the row length, |
| /// the cell will be appended at the end |
| pub fn insert_cell(&mut self, index: usize, cell: Cell) { |
| if index < self.cells.len() { |
| self.cells.insert(index, cell); |
| } else { |
| self.add_cell(cell); |
| } |
| } |
| |
| /// Remove the cell at position `index`. Silently skip if this cell does not exist |
| pub fn remove_cell(&mut self, index: usize) { |
| if index < self.cells.len() { |
| self.cells.remove(index); |
| } |
| } |
| |
| /// Returns an immutable iterator over cells |
| pub fn iter(&self) -> Iter<Cell> { |
| self.cells.iter() |
| } |
| |
| /// Returns an mutable iterator over cells |
| pub fn iter_mut(&mut self) -> IterMut<Cell> { |
| self.cells.iter_mut() |
| } |
| |
| /// Internal only |
| fn __print<T: Write + ?Sized, F>(&self, |
| out: &mut T, |
| format: &TableFormat, |
| col_width: &[usize], |
| f: F) |
| -> Result<usize, Error> |
| where F: Fn(&Cell, &mut T, usize, usize, bool) -> Result<(), Error> |
| { |
| let height = self.get_height(); |
| for i in 0..height { |
| //TODO: Wrap this into dedicated function one day |
| out.write_all(&vec![b' '; format.get_indent()])?; |
| format.print_column_separator(out, ColumnPosition::Left)?; |
| let (lp, rp) = format.get_padding(); |
| let mut j = 0; |
| let mut hspan = 0; // The additional offset caused by cell's horizontal spanning |
| while j+hspan < col_width.len() { |
| out.write_all(&vec![b' '; lp])?; // Left padding |
| // skip_r_fill skip filling the end of the last cell if there's no character |
| // delimiting the end of the table |
| let skip_r_fill = (j == col_width.len() - 1) && |
| format.get_column_separator(ColumnPosition::Right).is_none(); |
| match self.get_cell(j) { |
| Some(c) => { |
| // In case of horizontal spanning, width is the sum of all spanned columns' width |
| let mut w = col_width[j+hspan..j+hspan+c.get_hspan()].iter().sum(); |
| let real_span = c.get_hspan()-1; |
| w += real_span * (lp + rp) + real_span * format.get_column_separator(ColumnPosition::Intern).map(|_| 1).unwrap_or_default(); |
| // Print cell content |
| f(c, out, i, w, skip_r_fill)?; |
| hspan += real_span; // Add span to offset |
| }, |
| None => f(&Cell::default(), out, i, col_width[j+hspan], skip_r_fill)?, |
| }; |
| out.write_all(&vec![b' '; rp])?; // Right padding |
| if j+hspan < col_width.len() - 1 { |
| format.print_column_separator(out, ColumnPosition::Intern)?; |
| } |
| j+=1; |
| } |
| format.print_column_separator(out, ColumnPosition::Right)?; |
| out.write_all(NEWLINE)?; |
| } |
| Ok(height) |
| } |
| |
| /// Print the row to `out`, with `separator` as column separator, and `col_width` |
| /// specifying the width of each columns. Returns the number of printed lines |
| #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")] |
| pub fn print<T: Write + ?Sized>(&self, |
| out: &mut T, |
| format: &TableFormat, |
| col_width: &[usize]) |
| -> Result<usize, Error> { |
| self.__print(out, format, col_width, Cell::print) |
| } |
| |
| /// Print the row to terminal `out`, with `separator` as column separator, and `col_width` |
| /// specifying the width of each columns. Apply style when needed. returns the number of printed lines |
| #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")] |
| pub fn print_term<T: Terminal + ?Sized>(&self, |
| out: &mut T, |
| format: &TableFormat, |
| col_width: &[usize]) |
| -> Result<usize, Error> { |
| self.__print(out, format, col_width, Cell::print_term) |
| } |
| } |
| |
| impl Default for Row { |
| fn default() -> Row { |
| Row::empty() |
| } |
| } |
| |
| impl Index<usize> for Row { |
| type Output = Cell; |
| fn index(&self, idx: usize) -> &Self::Output { |
| &self.cells[idx] |
| } |
| } |
| |
| impl IndexMut<usize> for Row { |
| fn index_mut(&mut self, idx: usize) -> &mut Self::Output { |
| &mut self.cells[idx] |
| } |
| } |
| |
| impl<A: ToString> FromIterator<A> for Row { |
| fn from_iter<T>(iterator: T) -> Row |
| where T: IntoIterator<Item = A> |
| { |
| Self::new(iterator.into_iter().map(|ref e| Cell::from(e)).collect()) |
| } |
| } |
| |
| impl<T, A> From<T> for Row |
| where A: ToString, |
| T: IntoIterator<Item = A> |
| { |
| fn from(it: T) -> Row { |
| Self::from_iter(it) |
| } |
| } |
| |
| impl<'a> IntoIterator for &'a Row { |
| type Item = &'a Cell; |
| type IntoIter = Iter<'a, Cell>; |
| fn into_iter(self) -> Self::IntoIter { |
| self.iter() |
| } |
| } |
| |
| // impl IntoIterator for Row { |
| // type Item = Cell; |
| // type IntoIter = IntoIter<Cell>; |
| // fn into_iter(self) -> Self::IntoIter { |
| // self.cells.into_iter() |
| // } |
| // } |
| |
| impl<'a> IntoIterator for &'a mut Row { |
| type Item = &'a mut Cell; |
| type IntoIter = IterMut<'a, Cell>; |
| fn into_iter(self) -> Self::IntoIter { |
| self.iter_mut() |
| } |
| } |
| |
| impl <S: ToString> Extend<S> for Row { |
| fn extend<T: IntoIterator<Item=S>>(&mut self, iter: T) { |
| self.cells.extend(iter.into_iter().map(|s| Cell::new(&s.to_string()))); |
| } |
| } |
| |
| // impl <S: Into<Cell>> Extend<S> for Row { |
| // fn extend<T: IntoIterator<Item=S>>(&mut self, iter: T) { |
| // self.cells.extend(iter.into_iter().map(|s| s.into())); |
| // } |
| // } |
| |
| /// This macro simplifies `Row` creation |
| /// |
| /// The syntax support style spec |
| /// # Example |
| /// ``` |
| /// # #[macro_use] extern crate prettytable; |
| /// # fn main() { |
| /// // Create a normal row |
| /// let row1 = row!["Element 1", "Element 2", "Element 3"]; |
| /// // Create a row with all cells formatted with red foreground color, yellow background color |
| /// // bold, italic, align in the center of the cell |
| /// let row2 = row![FrBybic => "Element 1", "Element 2", "Element 3"]; |
| /// // Create a row with first cell in blue, second one in red, and last one with default style |
| /// let row3 = row![Fb->"blue", Fr->"red", "normal"]; |
| /// // Do something with rows |
| /// # drop(row1); |
| /// # drop(row2); |
| /// # drop(row3); |
| /// # } |
| /// ``` |
| /// |
| /// For details about style specifier syntax, check doc for [`Cell::style_spec`](cell/struct.Cell.html#method.style_spec) method |
| #[macro_export] |
| macro_rules! row { |
| (($($out:tt)*);) => (vec![$($out)*]); |
| (($($out:tt)*); $value:expr) => (vec![$($out)* cell!($value)]); |
| (($($out:tt)*); $value:expr, $($n:tt)*) => (row!(($($out)* cell!($value),); $($n)*)); |
| (($($out:tt)*); $style:ident -> $value:expr) => (vec![$($out)* cell!($style -> $value)]); |
| (($($out:tt)*); $style:ident -> $value:expr, $($n: tt)*) => (row!(($($out)* cell!($style -> $value),); $($n)*)); |
| |
| ($($content:expr), *) => ($crate::Row::new(vec![$(cell!($content)), *])); // This line may not be needed starting from Rust 1.20 |
| ($style:ident => $($content:expr), *) => ($crate::Row::new(vec![$(cell!($style -> $content)), *])); |
| ($style:ident => $($content:expr,) *) => ($crate::Row::new(vec![$(cell!($style -> $content)), *])); |
| ($($content:tt)*) => ($crate::Row::new(row!((); $($content)*))); |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use Cell; |
| |
| #[test] |
| fn row_default_empty() { |
| let row1 = Row::default(); |
| assert_eq!(row1.len(), 0); |
| assert!(row1.is_empty()); |
| } |
| |
| #[test] |
| fn get_add_set_cell() { |
| let mut row = Row::from(vec!["foo", "bar", "foobar"]); |
| assert_eq!(row.len(), 3); |
| assert!(row.get_mut_cell(12).is_none()); |
| let c1 = row.get_mut_cell(0).unwrap().clone(); |
| assert_eq!(c1.get_content(), "foo"); |
| |
| let c1 = Cell::from(&"baz"); |
| assert!(row.set_cell(c1.clone(), 1000).is_err()); |
| assert!(row.set_cell(c1.clone(), 0).is_ok()); |
| assert_eq!(row.get_cell(0).unwrap().get_content(), "baz"); |
| |
| row.add_cell(c1.clone()); |
| assert_eq!(row.len(), 4); |
| assert_eq!(row.get_cell(3).unwrap().get_content(), "baz"); |
| } |
| |
| #[test] |
| fn insert_cell() { |
| let mut row = Row::from(vec!["foo", "bar", "foobar"]); |
| assert_eq!(row.len(), 3); |
| let cell = Cell::new("baz"); |
| row.insert_cell(1000, cell.clone()); |
| assert_eq!(row.len(), 4); |
| assert_eq!(row.get_cell(3).unwrap().get_content(), "baz"); |
| row.insert_cell(1, cell.clone()); |
| assert_eq!(row.len(), 5); |
| assert_eq!(row.get_cell(1).unwrap().get_content(), "baz"); |
| } |
| |
| #[test] |
| fn remove_cell() { |
| let mut row = Row::from(vec!["foo", "bar", "foobar"]); |
| assert_eq!(row.len(), 3); |
| row.remove_cell(1000); |
| assert_eq!(row.len(), 3); |
| row.remove_cell(1); |
| assert_eq!(row.len(), 2); |
| assert_eq!(row.get_cell(0).unwrap().get_content(), "foo"); |
| assert_eq!(row.get_cell(1).unwrap().get_content(), "foobar"); |
| } |
| |
| #[test] |
| fn extend_row() { |
| let mut row = Row::from(vec!["foo", "bar", "foobar"]); |
| row.extend(vec!["A", "B", "C"]); |
| assert_eq!(row.len(), 6); |
| assert_eq!(row.get_cell(3).unwrap().get_content(), "A"); |
| assert_eq!(row.get_cell(4).unwrap().get_content(), "B"); |
| assert_eq!(row.get_cell(5).unwrap().get_content(), "C"); |
| } |
| } |