prettytable/
row.rs

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