Skip to content

Commit

Permalink
feat(widgets): Collect iterator of Row into Table
Browse files Browse the repository at this point in the history
A follow-up from ratatui#755,
allowing any iterator whose item is convertible into `Row` to be
collected into a `Table`.
  • Loading branch information
Lunderberg committed Jan 10, 2024
1 parent 388aa46 commit 1170eba
Showing 1 changed file with 53 additions and 0 deletions.
53 changes: 53 additions & 0 deletions src/widgets/table/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,23 @@ use crate::{
/// Cell::from(Text::from("text"));
/// ```
///
/// Just as rows can be collected from iterators of `Cell`s, tables can be collected from iterators
/// of `Row`s. Note: While `Table::new` requires the column widths to be provided on construction,
/// collection of an iterator into a `Table` does not have this safeguard. If the column widths are
/// not set with the `Table::widths`, the table will appear empty when drawn.
///
/// ```rust
/// use ratatui::{prelude::*, widgets::*};
///
/// let text = "Mary had a\nlittle lamb.";
///
/// let table = text
/// .split("\n")
/// .map(|line: &str| -> Row { line.split_ascii_whitespace().collect() })
/// .collect::<Table>()
/// .widths([Constraint::Length(10); 3]);
/// ```
///
/// `Table` also implements the [`Styled`] trait, which means you can use style shorthands from
/// the [`Stylize`] trait to set the style of the widget more concisely.
///
Expand Down Expand Up @@ -233,6 +250,11 @@ impl<'a> Table<'a> {
/// ```
pub fn new<R, C>(rows: R, widths: C) -> Self
where
// The `Table::new` function could be made more flexible by accepting `R: IntoIterator`,
// with `R::Item: Into<Row<'a>>`. This may be desirable at some point, but would be a
// breaking change, as type inference could no longer use `Table::new` to infer the type of
// elements of `R`. For example, `Table::new(vec![], [Constraint::Min(10)])` would not
// compile, because the type of `vec![]` would be unknown.
R: IntoIterator<Item = Row<'a>>,
C: IntoIterator,
C::Item: Into<Constraint>,
Expand Down Expand Up @@ -799,6 +821,19 @@ impl<'a> Styled for Table<'a> {
}
}

impl<'a, Item> FromIterator<Item> for Table<'a>
where
Item: Into<Row<'a>>,
{
fn from_iter<Iter: IntoIterator<Item = Item>>(rows: Iter) -> Self {
// When collecting from an iterator into a table, the user
// must provide the widths using `Table::widths` after
// construction.
let widths: [Constraint; 0] = [];
Table::new(rows.into_iter().map(Into::into), widths)
}
}

#[cfg(test)]
mod tests {
use std::vec;
Expand All @@ -820,6 +855,24 @@ mod tests {
assert_eq!(table.widths, widths);
}

#[test]
fn collect() {
let table = (0..4)
.map(|i| -> Row { (0..4).map(|j| format!("{i}*{j} = {}", i * j)).collect() })
.collect::<Table>()
.widths([Constraint::Percentage(25); 4]);

let expected_rows: Vec<Row> = vec![
Row::new(["0*0 = 0", "0*1 = 0", "0*2 = 0", "0*3 = 0"]),
Row::new(["1*0 = 0", "1*1 = 1", "1*2 = 2", "1*3 = 3"]),
Row::new(["2*0 = 0", "2*1 = 2", "2*2 = 4", "2*3 = 6"]),
Row::new(["3*0 = 0", "3*1 = 3", "3*2 = 6", "3*3 = 9"]),
];

assert_eq!(table.rows, expected_rows);
assert_eq!(table.widths, [Constraint::Percentage(25); 4]);
}

#[test]
fn widths() {
let table = Table::default().widths([Constraint::Length(100)]);
Expand Down

0 comments on commit 1170eba

Please sign in to comment.