//! 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, } impl Row { /// Create a new `Row` backed with `cells` vector pub fn new(cells: Vec) -> 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() > 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 { self.cells.iter() } /// Returns an mutable iterator over cells pub fn iter_mut(&mut self) -> IterMut { self.cells.iter_mut() } /// Internal only fn __print(&self, out: &mut T, format: &TableFormat, col_width: &[usize], f: F) -> Result 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(&self, out: &mut T, format: &TableFormat, col_width: &[usize]) -> Result { 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(&self, out: &mut T, format: &TableFormat, col_width: &[usize]) -> Result { self.__print(out, format, col_width, Cell::print_term) } /// Print the row in HTML format to `out`. /// /// If the row is has fewer columns than `col_num`, the row is padded with empty cells. pub fn print_html(&self, out: &mut T, col_num: usize) -> Result<(), Error> { let mut printed_columns = 0; for cell in self.iter() { printed_columns += cell.print_html(out)?; } // Pad with empty cells, if target width is not reached for _ in 0..col_num - printed_columns { Cell::default().print_html(out)?; } Ok(()) } } impl Default for Row { fn default() -> Row { Row::empty() } } impl Index for Row { type Output = Cell; fn index(&self, idx: usize) -> &Self::Output { &self.cells[idx] } } impl IndexMut for Row { fn index_mut(&mut self, idx: usize) -> &mut Self::Output { &mut self.cells[idx] } } impl FromIterator for Row { fn from_iter(iterator: T) -> Row where T: IntoIterator { Self::new(iterator.into_iter().map(|ref e| Cell::from(e)).collect()) } } impl From for Row where A: ToString, T: IntoIterator { 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; // 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 Extend for Row { fn extend>(&mut self, iter: T) { self.cells.extend(iter.into_iter().map(|s| Cell::new(&s.to_string()))); } } // impl > Extend for Row { // fn extend>(&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)* $crate::cell!($value)]); (($($out:tt)*); $value:expr, $($n:tt)*) => ($crate::row!(($($out)* $crate::cell!($value),); $($n)*)); (($($out:tt)*); $style:ident -> $value:expr) => (vec![$($out)* $crate::cell!($style -> $value)]); (($($out:tt)*); $style:ident -> $value:expr, $($n: tt)*) => ($crate::row!(($($out)* $crate::cell!($style -> $value),); $($n)*)); ($($content:expr), *) => ($crate::Row::new(vec![$($crate::cell!($content)), *])); // This line may not be needed starting from Rust 1.20 ($style:ident => $($content:expr), *) => ($crate::Row::new(vec![$($crate::cell!($style -> $content)), *])); ($style:ident => $($content:expr,) *) => ($crate::Row::new(vec![$($crate::cell!($style -> $content)), *])); ($($content:tt)*) => ($crate::Row::new($crate::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"); } }