Repository: zhiburt/tabled
Pull request #76 (2022-03-07 16:00:36)

tabled/ Add support for MinWidth


Patch:
diff --git a/src/width.rs b/src/width.rs
index 10e4413..9b8a933 100644
--- a/src/width.rs
+++ b/src/width.rs
@@ -4,7 +4,7 @@
 //! - [Wrap] split the content via new lines in order to fit max width.
 
 use crate::CellOption;
-use papergrid::{Entity, Grid, Settings};
+use papergrid::{string_width, Entity, Grid, Settings};
 
 /// MaxWidth allows you to set a max width of an object on a [Grid],
 /// using different strategies.
@@ -319,3 +319,89 @@ fn chunks(s: &str, width: usize) -> Vec<String> {
 
     v
 }
+
+/// MinWidth changes a content in case if it's length is lower then the boundry.
+///
+/// It does anything in case if the content's length is bigger then the boundry.
+/// It doesn't include a [crate::Indent] settings.
+///
+/// ## Example
+///
+/// ```
+/// use tabled::{Full, MinWidth, Modify, Style, Table};
+///
+/// let data = ["Hello", "World", "!"];
+///
+/// let table = Table::new(&data)
+///     .with(Style::github_markdown())
+///     .with(Modify::new(Full).with(MinWidth(10)));
+/// ```
+pub struct MinWidth {
+    size: usize,
+    fill: char,
+}
+
+impl MinWidth {
+    /// Creates a new instance of MinWidth.
+    pub fn new(size: usize) -> Self {
+        Self { size, fill: ' ' }
+    }
+
+    /// Set's a fill character which will be used to fill the space
+    /// when increasing the length of the string to the set boundry.
+    pub fn fill_with(mut self, c: char) -> Self {
+        self.fill = c;
+        self
+    }
+}
+
+impl CellOption for MinWidth {
+    fn change_cell(&mut self, grid: &mut Grid, row: usize, column: usize) {
+        let content = grid.get_cell_content(row, column);
+        let new_content = increase_width(content, self.size, self.fill);
+        grid.set(
+            &Entity::Cell(row, column),
+            Settings::new().text(new_content),
+        )
+    }
+}
+
+fn increase_width(s: &str, width: usize, fill_with: char) -> String {
+    let has_big_lines = s.lines().any(|line| string_width(line) < width);
+    if !has_big_lines {
+        return s.to_owned();
+    }
+
+    #[cfg(not(feature = "color"))]
+    {
+        s.lines()
+            .map(|line| {
+                let length = string_width(line);
+                if length < width {
+                    let remain = width - length;
+                    let mut new_line = String::with_capacity(width);
+                    new_line.push_str(line);
+                    new_line.extend(std::iter::repeat(fill_with).take(remain));
+                    std::borrow::Cow::Owned(new_line)
+                } else {
+                    std::borrow::Cow::Borrowed(line)
+                }
+            })
+            .collect::<String>()
+    }
+    #[cfg(feature = "color")]
+    {
+        ansi_str::AnsiStr::ansi_split(s, "\n")
+            .map(|mut line| {
+                let length = string_width(&line);
+                if length < width {
+                    let remain = width - length;
+                    line.extend(std::iter::repeat(fill_with).take(remain));
+                    line
+                } else {
+                    line
+                }
+            })
+            .collect::<String>()
+    }
+}


Test patch:
diff --git a/tests/width_test.rs b/tests/width_test.rs
index 53edc30..38f00af 100644
--- a/tests/width_test.rs
+++ b/tests/width_test.rs
@@ -1,5 +1,7 @@
 use crate::util::create_vector;
-use tabled::{Alignment, Column, Full, MaxWidth, Modify, Object, Row, Style, Table};
+use tabled::{
+    Alignment, Cell, Column, Full, MaxWidth, MinWidth, Modify, Object, Row, Style, Table,
+};
 
 mod util;
 
@@ -186,7 +188,7 @@ fn max_width_wrapped_keep_words_color() {
 
     let data = vec!["this is a long sentence".on_black().green().to_string()];
     let table = Table::new(&data)
-        .with(Style::GITHUB_MARKDOWN)
+        .with(Style::github_markdown())
         .with(Modify::new(Full).with(Alignment::left()))
         .with(Modify::new(Full).with(MaxWidth::wrapping(17).keep_words()))
         .to_string();
@@ -202,7 +204,7 @@ fn max_width_wrapped_keep_words_color() {
 
     let data = vec!["this is a long  sentence".on_black().green().to_string()];
     let table = Table::new(&data)
-        .with(Style::GITHUB_MARKDOWN)
+        .with(Style::github_markdown())
         .with(Modify::new(Full).with(Alignment::left()))
         .with(Modify::new(Full).with(MaxWidth::wrapping(17).keep_words()))
         .to_string();
@@ -220,7 +222,7 @@ fn max_width_wrapped_keep_words_color() {
 
     let data = vec!["this is a long   sentence".on_black().green().to_string()];
     let table = Table::new(&data)
-        .with(Style::GITHUB_MARKDOWN)
+        .with(Style::github_markdown())
         .with(Modify::new(Full).with(Alignment::left()))
         .with(Modify::new(Full).with(MaxWidth::wrapping(17).keep_words()))
         .to_string();
@@ -236,7 +238,7 @@ fn max_width_wrapped_keep_words_color() {
 
     let data = vec!["this is a long    sentence".on_black().green().to_string()];
     let table = Table::new(&data)
-        .with(Style::GITHUB_MARKDOWN)
+        .with(Style::github_markdown())
         .with(Modify::new(Full).with(Alignment::left()))
         .with(Modify::new(Full).with(MaxWidth::wrapping(17).keep_words()))
         .to_string();
@@ -252,7 +254,7 @@ fn max_width_wrapped_keep_words_color() {
 
     let data = vec!["this".on_black().green().to_string()];
     let table = Table::new(&data)
-        .with(Style::GITHUB_MARKDOWN)
+        .with(Style::github_markdown())
         .with(Modify::new(Full).with(MaxWidth::wrapping(10).keep_words()))
         .to_string();
 
@@ -295,7 +297,7 @@ fn max_width_wrapped_keep_words_long_word_color() {
         .green()
         .to_string()];
     let table = Table::new(&data)
-        .with(Style::GITHUB_MARKDOWN)
+        .with(Style::github_markdown())
         .with(Modify::new(Full).with(Alignment::left()))
         .with(Modify::new(Full).with(MaxWidth::wrapping(17).keep_words()))
         .to_string();
@@ -405,7 +407,7 @@ fn color_chars_are_stripped() {
     );
 
     let table = Table::new(data)
-        .with(Style::GITHUB_MARKDOWN)
+        .with(Style::github_markdown())
         .with(Modify::new(Full).with(MaxWidth::truncating(3).suffix("...")))
         .to_string();
 
@@ -413,3 +415,161 @@ fn color_chars_are_stripped() {
 
     assert_eq!(expected, table);
 }
+
+#[test]
+fn min_width() {
+    let data = create_vector::<3, 3>();
+    let table = Table::new(&data)
+        .with(Style::github_markdown())
+        .with(Modify::new(Row(..1)).with(MinWidth::new(12)))
+        .to_string();
+
+    let expected = concat!(
+        "|      N       |   column 0   |   column 1   |   column 2   |\n",
+        "|--------------+--------------+--------------+--------------|\n",
+        "|      0       |     0-0      |     0-1      |     0-2      |\n",
+        "|      1       |     1-0      |     1-1      |     1-2      |\n",
+        "|      2       |     2-0      |     2-1      |     2-2      |\n",
+    );
+
+    assert_eq!(table, expected);
+}
+
+#[test]
+fn min_width_with_filler() {
+    let data = create_vector::<3, 3>();
+    let table = Table::new(&data)
+        .with(Style::github_markdown())
+        .with(Modify::new(Row(..1)).with(MinWidth::new(12).fill_with('.')))
+        .to_string();
+
+    let expected = concat!(
+        "| N........... | column 0.... | column 1.... | column 2.... |\n",
+        "|--------------+--------------+--------------+--------------|\n",
+        "|      0       |     0-0      |     0-1      |     0-2      |\n",
+        "|      1       |     1-0      |     1-1      |     1-2      |\n",
+        "|      2       |     2-0      |     2-1      |     2-2      |\n",
+    );
+
+    assert_eq!(table, expected);
+}
+
+#[test]
+fn min_width_one_column() {
+    let data = create_vector::<3, 3>();
+    let table = Table::new(&data)
+        .with(Style::github_markdown())
+        .with(Modify::new(Cell(0, 0)).with(MinWidth::new(5)))
+        .to_string();
+
+    let expected = concat!(
+        "|   N   | column 0 | column 1 | column 2 |\n",
+        "|-------+----------+----------+----------|\n",
+        "|   0   |   0-0    |   0-1    |   0-2    |\n",
+        "|   1   |   1-0    |   1-1    |   1-2    |\n",
+        "|   2   |   2-0    |   2-1    |   2-2    |\n",
+    );
+
+    assert_eq!(table, expected);
+}
+
+#[test]
+fn min_width_on_smaller_content() {
+    let data = create_vector::<3, 3>();
+
+    assert_eq!(
+        Table::new(&data)
+            .with(Style::github_markdown())
+            .with(Modify::new(Row(..1)).with(MinWidth::new(1)))
+            .to_string(),
+        Table::new(&data).with(Style::github_markdown()).to_string()
+    );
+}
+
+#[test]
+fn min_with_max_width() {
+    let data = create_vector::<3, 3>();
+    let table = Table::new(&data)
+        .with(Style::github_markdown())
+        .with(Modify::new(Row(..1)).with(MinWidth::new(3)))
+        .with(Modify::new(Row(..1)).with(MaxWidth::truncating(3)))
+        .to_string();
+
+    let expected = concat!(
+        "|  N  | col | col | col |\n",
+        "|-----+-----+-----+-----|\n",
+        "|  0  | 0-0 | 0-1 | 0-2 |\n",
+        "|  1  | 1-0 | 1-1 | 1-2 |\n",
+        "|  2  | 2-0 | 2-1 | 2-2 |\n",
+    );
+
+    assert_eq!(table, expected);
+}
+
+#[test]
+fn min_with_max_width_truncate_suffix() {
+    let data = create_vector::<3, 3>();
+    let table = Table::new(&data)
+        .with(Style::github_markdown())
+        .with(Modify::new(Row(..1)).with(MinWidth::new(3)))
+        .with(Modify::new(Row(..1)).with(MaxWidth::truncating(3).suffix("...")))
+        .to_string();
+
+    let expected = concat!(
+        "|  N  | col... | col... | col... |\n",
+        "|-----+--------+--------+--------|\n",
+        "|  0  |  0-0   |  0-1   |  0-2   |\n",
+        "|  1  |  1-0   |  1-1   |  1-2   |\n",
+        "|  2  |  2-0   |  2-1   |  2-2   |\n",
+    );
+
+    assert_eq!(table, expected);
+}
+
+#[cfg(feature = "color")]
+#[test]
+fn min_width_color() {
+    use owo_colors::OwoColorize;
+
+    let data = &[
+        "asd".red().to_string(),
+        "zxc".blue().to_string(),
+        "asdasd".on_black().green().to_string(),
+    ];
+
+    let expected = concat!(
+        "|   String   |\n",
+        "|------------|\n",
+        "|    \u{1b}[31masd\u{1b}[0m     |\n",
+        "|    \u{1b}[34mzxc\u{1b}[0m     |\n",
+        "|   \u{1b}[32m\u{1b}[40masdasd\u{1b}[0m\u{1b}[0m   |\n",
+    );
+
+    let table = Table::new(data)
+        .with(Style::github_markdown())
+        .with(Modify::new(Full).with(MinWidth::new(10)))
+        .to_string();
+
+    println!("{}", table);
+
+    assert_eq!(expected, table);
+}
+
+#[cfg(feature = "color")]
+#[test]
+fn min_width_color_with_smaller_then_width() {
+    use owo_colors::OwoColorize;
+
+    let data = &[
+        "asd".red().to_string(),
+        "zxc".blue().to_string(),
+        "asdasd".on_black().green().to_string(),
+    ];
+
+    assert_eq!(
+        Table::new(data)
+            .with(Modify::new(Full).with(MinWidth::new(1)))
+            .to_string(),
+        Table::new(data).to_string()
+    );
+}