prettytable/
row.rs

1//! This module contains definition of table rows stuff
2use std::io::{
3  Error,
4  Write,
5};
6use std::iter::FromIterator;
7use std::slice::{
8  Iter,
9  IterMut,
10};
11// use std::vec::IntoIter;
12use std::ops::{
13  Index,
14  IndexMut,
15};
16
17use super::Terminal;
18
19use super::format::{
20  ColumnPosition,
21  TableFormat,
22};
23use super::utils::NEWLINE;
24use super::Cell;
25
26/// Represent a table row made of cells
27#[derive(Clone, Debug, Hash, PartialEq, Eq)]
28pub struct Row {
29  cells: Vec<Cell>,
30}
31
32impl Row {
33  /// Create a new `Row` backed with `cells` vector
34  pub fn new(cells: Vec<Cell>) -> Row {
35    Row { cells }
36  }
37
38  /// Create an row of length `size`, with empty strings stored
39  pub fn empty() -> Row {
40    Self::new({
41      Cell::default();
42      vec![] as Vec<Cell>
43    })
44  }
45
46  /// Count the number of column required in the table grid.
47  /// It takes into account horizontal spanning of cells. For
48  /// example, a cell with an hspan of 3 will add 3 column to the grid
49  // #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")]
50  pub(crate) fn column_count(&self) -> usize {
51    self.cells.iter().map(|c| c.get_hspan()).sum()
52  }
53
54  /// Get the number of cells in this row
55  pub fn len(&self) -> usize {
56    self.cells.len()
57    // self.cells.iter().map(|c| c.get_hspan()).sum()
58  }
59
60  /// Check if the row is empty (has no cell)
61  pub fn is_empty(&self) -> bool {
62    self.cells.is_empty()
63  }
64
65  /// Get the height of this row
66  // #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")]
67  fn get_height(&self) -> usize {
68    let mut height = 1; // Minimum height must be 1 to print empty rows
69    for cell in &self.cells {
70      let h = cell.get_height();
71      if h > height {
72        height = h;
73      }
74    }
75    height
76  }
77
78  /// Get the minimum width required by the cell in the column `column`.
79  /// Return 0 if the cell does not exist in this row
80  // #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")]
81  pub(crate) fn get_column_width(&self, column: usize, format: &TableFormat) -> usize {
82    let mut i = 0;
83    for c in &self.cells {
84      if i + c.get_hspan() > column {
85        if c.get_hspan() == 1 {
86          return c.get_width();
87        }
88        let (lp, rp) = format.get_padding();
89        let sep = format
90          .get_column_separator(ColumnPosition::Intern)
91          .map(|_| 1)
92          .unwrap_or_default();
93        let rem = lp + rp + sep;
94        let mut w = c.get_width();
95        if w > rem {
96          w -= rem;
97        } else {
98          w = 0;
99        }
100        return (w as f64 / c.get_hspan() as f64).ceil() as usize;
101      }
102      i += c.get_hspan();
103    }
104    0
105  }
106
107  /// Get the cell at index `idx`
108  pub fn get_cell(&self, idx: usize) -> Option<&Cell> {
109    self.cells.get(idx)
110  }
111
112  /// Get the mutable cell at index `idx`
113  pub fn get_mut_cell(&mut self, idx: usize) -> Option<&mut Cell> {
114    self.cells.get_mut(idx)
115  }
116
117  /// Set the `cell` in the row at the given `idx` index
118  pub fn set_cell(&mut self, cell: Cell, idx: usize) -> Result<(), &str> {
119    if idx >= self.len() {
120      return Err("Cannot find cell");
121    }
122    self.cells[idx] = cell;
123    Ok(())
124  }
125
126  /// Append a `cell` at the end of the row
127  pub fn add_cell(&mut self, cell: Cell) {
128    self.cells.push(cell);
129  }
130
131  /// Insert `cell` at position `index`. If `index` is higher than the row length,
132  /// the cell will be appended at the end
133  pub fn insert_cell(&mut self, index: usize, cell: Cell) {
134    if index < self.cells.len() {
135      self.cells.insert(index, cell);
136    } else {
137      self.add_cell(cell);
138    }
139  }
140
141  /// Remove the cell at position `index`. Silently skip if this cell does not exist
142  pub fn remove_cell(&mut self, index: usize) {
143    if index < self.cells.len() {
144      self.cells.remove(index);
145    }
146  }
147
148  /// Returns an immutable iterator over cells
149  pub fn iter(&self) -> Iter<Cell> {
150    self.cells.iter()
151  }
152
153  /// Returns an mutable iterator over cells
154  pub fn iter_mut(&mut self) -> IterMut<Cell> {
155    self.cells.iter_mut()
156  }
157
158  /// Internal only
159  fn __print<T: Write + ?Sized, F>(
160    &self,
161    out: &mut T,
162    format: &TableFormat,
163    col_width: &[usize],
164    f: F,
165  ) -> Result<usize, Error>
166  where
167    F: Fn(&Cell, &mut T, usize, usize, bool) -> Result<(), Error>,
168  {
169    let height = self.get_height();
170    for i in 0..height {
171      //TODO: Wrap this into dedicated function one day
172      out.write_all(&vec![b' '; format.get_indent()])?;
173      format.print_column_separator(out, ColumnPosition::Left)?;
174      let (lp, rp) = format.get_padding();
175      let mut j = 0;
176      let mut hspan = 0; // The additional offset caused by cell's horizontal spanning
177      while j + hspan < col_width.len() {
178        out.write_all(&vec![b' '; lp])?; // Left padding
179                                         // skip_r_fill skip filling the end of the last cell if there's no character
180                                         // delimiting the end of the table
181        let skip_r_fill =
182          (j == col_width.len() - 1) && format.get_column_separator(ColumnPosition::Right).is_none();
183        match self.get_cell(j) {
184          Some(c) => {
185            // In case of horizontal spanning, width is the sum of all spanned columns' width
186            let mut w = col_width[j + hspan..j + hspan + c.get_hspan()].iter().sum();
187            let real_span = c.get_hspan() - 1;
188            w += real_span * (lp + rp)
189              + real_span
190                * format
191                  .get_column_separator(ColumnPosition::Intern)
192                  .map(|_| 1)
193                  .unwrap_or_default();
194            // Print cell content
195            f(c, out, i, w, skip_r_fill)?;
196            hspan += real_span; // Add span to offset
197          },
198          None => f(&Cell::default(), out, i, col_width[j + hspan], skip_r_fill)?,
199        };
200        out.write_all(&vec![b' '; rp])?; // Right padding
201        if j + hspan < col_width.len() - 1 {
202          format.print_column_separator(out, ColumnPosition::Intern)?;
203        }
204        j += 1;
205      }
206      format.print_column_separator(out, ColumnPosition::Right)?;
207      out.write_all(NEWLINE)?;
208    }
209    Ok(height)
210  }
211
212  /// Print the row to `out`, with `separator` as column separator, and `col_width`
213  /// specifying the width of each columns. Returns the number of printed lines
214  // #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")]
215  pub(crate) fn print<T: Write + ?Sized>(
216    &self,
217    out: &mut T,
218    format: &TableFormat,
219    col_width: &[usize],
220  ) -> Result<usize, Error> {
221    self.__print(out, format, col_width, Cell::print)
222  }
223
224  /// Print the row to terminal `out`, with `separator` as column separator, and `col_width`
225  /// specifying the width of each columns. Apply style when needed. returns the number of printed lines
226  // #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")]
227  pub(crate) fn print_term<T: Terminal + ?Sized>(
228    &self,
229    out: &mut T,
230    format: &TableFormat,
231    col_width: &[usize],
232  ) -> Result<usize, Error> {
233    self.__print(out, format, col_width, Cell::print_term)
234  }
235
236  /// Print the row in HTML format to `out`.
237  ///
238  /// If the row is has fewer columns than `col_num`, the row is padded with empty cells.
239  pub fn print_html<T: Write + ?Sized>(&self, out: &mut T, col_num: usize) -> Result<(), Error> {
240    let mut printed_columns = 0;
241    for cell in self.iter() {
242      printed_columns += cell.print_html(out)?;
243    }
244    // Pad with empty cells, if target width is not reached
245    for _ in 0..col_num - printed_columns {
246      Cell::default().print_html(out)?;
247    }
248    Ok(())
249  }
250}
251
252impl Default for Row {
253  fn default() -> Row {
254    Row::empty()
255  }
256}
257
258impl Index<usize> for Row {
259  type Output = Cell;
260  fn index(&self, idx: usize) -> &Self::Output {
261    &self.cells[idx]
262  }
263}
264
265impl IndexMut<usize> for Row {
266  fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
267    &mut self.cells[idx]
268  }
269}
270
271impl<A: ToString> FromIterator<A> for Row {
272  fn from_iter<T>(iterator: T) -> Row
273  where
274    T: IntoIterator<Item = A>,
275  {
276    Self::new(iterator.into_iter().map(|ref e| Cell::from(e)).collect())
277  }
278}
279
280impl<T, A> From<T> for Row
281where
282  A: ToString,
283  T: IntoIterator<Item = A>,
284{
285  fn from(it: T) -> Row {
286    Self::from_iter(it)
287  }
288}
289
290impl<'a> IntoIterator for &'a Row {
291  type Item = &'a Cell;
292  type IntoIter = Iter<'a, Cell>;
293  fn into_iter(self) -> Self::IntoIter {
294    self.iter()
295  }
296}
297
298// impl IntoIterator for Row {
299//     type Item = Cell;
300//     type IntoIter = IntoIter<Cell>;
301//     fn into_iter(self) -> Self::IntoIter {
302//         self.cells.into_iter()
303//     }
304// }
305
306impl<'a> IntoIterator for &'a mut Row {
307  type Item = &'a mut Cell;
308  type IntoIter = IterMut<'a, Cell>;
309  fn into_iter(self) -> Self::IntoIter {
310    self.iter_mut()
311  }
312}
313
314impl<S: ToString> Extend<S> for Row {
315  fn extend<T: IntoIterator<Item = S>>(&mut self, iter: T) {
316    self
317      .cells
318      .extend(iter.into_iter().map(|s| Cell::new(&s.to_string())));
319  }
320}
321
322// impl <S: Into<Cell>> Extend<S> for Row {
323//     fn extend<T: IntoIterator<Item=S>>(&mut self, iter: T) {
324//         self.cells.extend(iter.into_iter().map(|s| s.into()));
325//     }
326// }
327
328/// This macro simplifies `Row` creation
329///
330/// The syntax support style spec
331/// # Example
332/// ```
333/// # #[macro_use] extern crate prettytable;
334/// # fn main() {
335/// // Create a normal row
336/// let row1 = row!["Element 1", "Element 2", "Element 3"];
337/// // Create a row with all cells formatted with red foreground color, yellow background color
338/// // bold, italic, align in the center of the cell
339/// let row2 = row![FrBybic => "Element 1", "Element 2", "Element 3"];
340/// // Create a row with first cell in blue, second one in red, and last one with default style
341/// let row3 = row![Fb->"blue", Fr->"red", "normal"];
342/// // Do something with rows
343/// # drop(row1);
344/// # drop(row2);
345/// # drop(row3);
346/// # }
347/// ```
348///
349/// For details about style specifier syntax, check doc for [`Cell::style_spec`](cell/struct.Cell.html#method.style_spec) method
350#[macro_export]
351macro_rules! row {
352    (($($out:tt)*);) => (vec![$($out)*]);
353    (($($out:tt)*); $value:expr) => (vec![$($out)* $crate::cell!($value)]);
354    (($($out:tt)*); $value:expr, $($n:tt)*) => ($crate::row!(($($out)* $crate::cell!($value),); $($n)*));
355    (($($out:tt)*); $style:ident -> $value:expr) => (vec![$($out)* $crate::cell!($style -> $value)]);
356    (($($out:tt)*); $style:ident -> $value:expr, $($n: tt)*) => ($crate::row!(($($out)* $crate::cell!($style -> $value),); $($n)*));
357
358    ($($content:expr), *) => ($crate::Row::new(vec![$($crate::cell!($content)), *])); // This line may not be needed starting from Rust 1.20
359    ($style:ident => $($content:expr), *) => ($crate::Row::new(vec![$($crate::cell!($style -> $content)), *]));
360    ($style:ident => $($content:expr,) *) => ($crate::Row::new(vec![$($crate::cell!($style -> $content)), *]));
361    ($($content:tt)*) => ($crate::Row::new($crate::row!((); $($content)*)));
362}
363
364#[cfg(test)]
365mod tests {
366  use super::*;
367
368  #[test]
369  fn row_default_empty() {
370    let row1 = Row::default();
371    assert_eq!(row1.len(), 0);
372    assert!(row1.is_empty());
373  }
374
375  #[test]
376  fn get_add_set_cell() {
377    let mut row = Row::from(vec!["foo", "bar", "foobar"]);
378    assert_eq!(row.len(), 3);
379    assert!(row.get_mut_cell(12).is_none());
380    let c1 = row.get_mut_cell(0).unwrap().clone();
381    assert_eq!(c1.get_content(), "foo");
382
383    let c1 = Cell::from(&"baz");
384    assert!(row.set_cell(c1.clone(), 1000).is_err());
385    assert!(row.set_cell(c1.clone(), 0).is_ok());
386    assert_eq!(row.get_cell(0).unwrap().get_content(), "baz");
387
388    row.add_cell(c1.clone());
389    assert_eq!(row.len(), 4);
390    assert_eq!(row.get_cell(3).unwrap().get_content(), "baz");
391  }
392
393  #[test]
394  fn insert_cell() {
395    let mut row = Row::from(vec!["foo", "bar", "foobar"]);
396    assert_eq!(row.len(), 3);
397    let cell = Cell::new("baz");
398    row.insert_cell(1000, cell.clone());
399    assert_eq!(row.len(), 4);
400    assert_eq!(row.get_cell(3).unwrap().get_content(), "baz");
401    row.insert_cell(1, cell.clone());
402    assert_eq!(row.len(), 5);
403    assert_eq!(row.get_cell(1).unwrap().get_content(), "baz");
404  }
405
406  #[test]
407  fn remove_cell() {
408    let mut row = Row::from(vec!["foo", "bar", "foobar"]);
409    assert_eq!(row.len(), 3);
410    row.remove_cell(1000);
411    assert_eq!(row.len(), 3);
412    row.remove_cell(1);
413    assert_eq!(row.len(), 2);
414    assert_eq!(row.get_cell(0).unwrap().get_content(), "foo");
415    assert_eq!(row.get_cell(1).unwrap().get_content(), "foobar");
416  }
417
418  #[test]
419  fn extend_row() {
420    let mut row = Row::from(vec!["foo", "bar", "foobar"]);
421    row.extend(vec!["A", "B", "C"]);
422    assert_eq!(row.len(), 6);
423    assert_eq!(row.get_cell(3).unwrap().get_content(), "A");
424    assert_eq!(row.get_cell(4).unwrap().get_content(), "B");
425    assert_eq!(row.get_cell(5).unwrap().get_content(), "C");
426  }
427}