Repository: zhiburt/tabled
Pull request #112 (2022-04-21 13:59:40)

Add Justify width


Patch:
diff --git a/src/lib.rs b/src/lib.rs
index 53dfd64..ffbad1a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -151,7 +151,9 @@
 //! [README.md](https://github.com/zhiburt/tabled/blob/master/README.md)
 //!
 
-#![doc(html_logo_url = "https://raw.githubusercontent.com/zhiburt/tabled/86ac146e532ce9f7626608d7fd05072123603a2e/assets/tabled-gear.svg")]
+#![doc(
+    html_logo_url = "https://raw.githubusercontent.com/zhiburt/tabled/86ac146e532ce9f7626608d7fd05072123603a2e/assets/tabled-gear.svg"
+)]
 
 mod alignment;
 mod concat;
@@ -165,23 +167,34 @@ mod panel;
 mod rotate;
 mod span;
 mod table;
-mod width;
 
 pub mod builder;
 pub mod display;
 pub mod formatting_settings;
 pub mod object;
 pub mod style;
+pub mod width;
 
 use std::fmt;
 
 pub use tabled_derive::Tabled;
 
 pub use crate::{
-    alignment::*, alignment::*, concat::*, concat::*, disable::*, disable::*, extract::*,
-    extract::*, formating::*, formating::*, highlight::*, highlight::*, margin::*, margin::*,
-    object::*, padding::*, padding::*, panel::*, panel::*, rotate::*, rotate::*, span::*, span::*,
-    style::Style, table::*, table::*, width::*, width::*,
+    alignment::*,
+    concat::*,
+    disable::*,
+    extract::*,
+    formating::*,
+    highlight::*,
+    margin::*,
+    object::*,
+    padding::*,
+    panel::*,
+    rotate::*,
+    span::*,
+    style::Style,
+    table::*,
+    width::{Justify, MaxWidth, MinWidth, Truncate, Wrap},
 };
 
 // todo: change return type to impl Iterator<Cow<str>>?
diff --git a/src/padding.rs b/src/padding.rs
index b8791cb..f18166d 100644
--- a/src/padding.rs
+++ b/src/padding.rs
@@ -24,6 +24,20 @@ impl Padding {
         })
     }
 
+    /// Construct's an Padding object with all sides set to 0.
+    ///
+    /// It uses space(' ') as a default fill character.
+    /// To set a custom character you can use [Self::set_fill] function.
+    pub fn zero() -> Self {
+        let indent = Indent::spaced(0);
+        Self(papergrid::Padding {
+            top: indent,
+            bottom: indent,
+            left: indent,
+            right: indent,
+        })
+    }
+
     /// The function, sets a characters for the padding on an each side.
     pub fn set_fill(mut self, left: char, right: char, top: char, bottom: char) -> Self {
         self.0.left.fill = left;
diff --git a/src/width.rs b/src/width.rs
index a0bd46c..d473607 100644
--- a/src/width.rs
+++ b/src/width.rs
@@ -2,6 +2,7 @@
 //!
 //! - [Truncate] cuts a cell content to limit width.
 //! - [Wrap] split the content via new lines in order to fit max width.
+//! - [Justify] sets columns width to the same value.
 //!
 //! To set a a table width a combination of [MaxWidth] and [MinWidth] can be set.
 //!
@@ -405,7 +406,6 @@ fn chunks(s: &str, width: usize) -> Vec<String> {
 ///
 /// let table = Table::new(&["Hello World!"]).with(MinWidth::new(5));
 /// ```
-///
 pub struct MinWidth {
     size: usize,
     fill: char,
@@ -615,7 +615,7 @@ fn decrease_total_width(grid: &Grid, width: usize) -> HashMap<(usize, usize), us
         return points;
     }
 
-    let orig_widths = build_orig_widths(grid);
+    let orig_widths = grid_widths(grid);
 
     let mut min_widths = build_min_widths(grid);
 
@@ -807,7 +807,7 @@ fn correct_width(style: &Style, mut width: usize) -> usize {
     width
 }
 
-fn build_orig_widths(grid: &Grid) -> Vec<Vec<usize>> {
+fn grid_widths(grid: &Grid) -> Vec<Vec<usize>> {
     (0..grid.count_rows())
         .map(|row| {
             (0..grid.count_columns())
@@ -827,3 +827,113 @@ fn is_zero_spanned_grid(grid: &Grid) -> bool {
         })
         .all(|b| b)
 }
+
+/// Justify sets all columns widths to the set value.
+///
+/// Be aware that it doesn't consider padding.
+/// So if you want to set a exact width you might need to use [crate::Padding] to set it to 0.
+///
+/// ## Examples
+///
+/// ```
+/// use tabled::{Justify, Style, Modify, Full, Padding, Table};
+///
+/// let data = ["Hello", "World", "!"];
+///
+/// let table = Table::new(&data)
+///     .with(Style::github_markdown())
+///     .with(Modify::new(Full).with(Padding::zero()))
+///     .with(Justify::new(3));
+/// ```
+///
+/// [Max] usage to justify by a max column width.
+///
+/// ```
+/// use tabled::{Justify, Style, Modify, Full, Padding, Table, width};
+///
+/// let data = ["Hello", "World", "!"];
+///
+/// let table = Table::new(&data)
+///     .with(Style::github_markdown())
+///     .with(Justify::max());
+/// ```
+pub struct Justify<W> {
+    width: W,
+}
+
+impl Justify<usize> {
+    /// Creates a new Justify instance.
+    ///
+    /// Be aware that [crate::Padding] is not considered when comparing the width.
+    pub fn new(width: usize) -> Self {
+        Self { width }
+    }
+}
+
+impl Justify<Max> {
+    /// Creates a new Justify instance with a Max width used as a value.
+    pub fn max() -> Self {
+        Self { width: Max }
+    }
+}
+
+impl Justify<Min> {
+    /// Creates a new Justify instance with a Min width used as a value.
+    pub fn min() -> Self {
+        Self { width: Min }
+    }
+}
+
+impl<W> TableOption for Justify<W>
+where
+    W: Width,
+{
+    fn change(&mut self, grid: &mut Grid) {
+        let width = self.width.width(grid);
+
+        for row in 0..grid.count_rows() {
+            for col in 0..grid.count_columns() {
+                MinWidth::new(width).change_cell(grid, row, col);
+                MaxWidth::truncating(width).change_cell(grid, row, col);
+            }
+        }
+    }
+}
+
+/// A width value which can be obtained on behaif of [Grid].
+trait Width {
+    /// Returns a width value.
+    fn width(&self, grid: &Grid) -> usize;
+}
+
+impl Width for usize {
+    fn width(&self, _: &Grid) -> usize {
+        *self
+    }
+}
+
+/// Max width value.
+pub struct Max;
+
+impl Width for Max {
+    fn width(&self, grid: &Grid) -> usize {
+        grid_widths(grid)
+            .into_iter()
+            .map(|r| r.into_iter().max().unwrap_or(0))
+            .max()
+            .unwrap_or(0)
+    }
+}
+
+/// Min width value.
+pub struct Min;
+
+impl Width for Min {
+    fn width(&self, grid: &Grid) -> usize {
+        grid_widths(grid)
+            .into_iter()
+            .map(|r| r.into_iter().min().unwrap_or(0))
+            .min()
+            .unwrap_or(0)
+    }
+}


Test patch:
diff --git a/tests/padding_test.rs b/tests/padding_test.rs
index 9f9b314..40fc47c 100644
--- a/tests/padding_test.rs
+++ b/tests/padding_test.rs
@@ -64,7 +64,7 @@ fn padding_with_set_characters_and_zero_ident() {
     let data = create_vector::<3, 3>();
     let table = Table::new(&data)
         .with(Style::psql())
-        .with(Modify::new(Full).with(Padding::new(0, 0, 0, 0).set_fill('>', '<', '^', 'V')))
+        .with(Modify::new(Full).with(Padding::zero().set_fill('>', '<', '^', 'V')))
         .to_string();
 
     let expected = concat!(
diff --git a/tests/style_test.rs b/tests/style_test.rs
index 593024a..68130a9 100644
--- a/tests/style_test.rs
+++ b/tests/style_test.rs
@@ -317,7 +317,7 @@ fn empty_style() {
     let data = create_vector::<3, 3>();
     let table = Table::new(&data)
         .with(Style::empty())
-        .with(Modify::new(Full).with(Padding::new(0, 0, 0, 0)))
+        .with(Modify::new(Full).with(Padding::zero()))
         .to_string();
 
     let expected = concat!(
diff --git a/tests/width_test.rs b/tests/width_test.rs
index 46f35cc..2a76877 100644
--- a/tests/width_test.rs
+++ b/tests/width_test.rs
@@ -2,7 +2,7 @@ use crate::util::{create_vector, is_lines_equal};
 use tabled::{
     formatting_settings::TrimStrategy,
     object::{Cell, Columns, Full, Object, Rows},
-    Alignment, MaxWidth, MinWidth, Modify, Panel, Span, Style, Table, Tabled,
+    Alignment, Justify, MaxWidth, MinWidth, Modify, Panel, Span, Style, Table, Tabled,
 };
 
 mod util;
@@ -1536,3 +1536,107 @@ fn min_width_with_span_2() {
     );
     assert!(is_lines_equal(&table, 100));
 }
+
+#[test]
+fn justify_width_constant_test() {
+    let data = create_vector::<3, 3>();
+    let table = Table::new(&data)
+        .with(Style::github_markdown())
+        .with(Justify::new(3))
+        .to_string();
+
+    assert_eq!(
+        table,
+        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",
+        )
+    );
+}
+
+#[test]
+fn justify_width_constant_different_sizes_test() {
+    let mut data = create_vector::<3, 3>();
+    data[0][1] = "Hello World".to_owned();
+    data[2][2] = "multi\nline string\n".to_owned();
+
+    let table = Table::new(&data)
+        .with(Style::github_markdown())
+        .with(Justify::new(3))
+        .to_string();
+
+    assert_eq!(
+        table,
+        concat!(
+            "|  N  | col | col | col |\n",
+            "|-----+-----+-----+-----|\n",
+            "|  0  | Hel | 0-1 | 0-2 |\n",
+            "|  1  | 1-0 | 1-1 | 1-2 |\n",
+            "|  2  | 2-0 | mul | 2-2 |\n",
+        )
+    );
+}
+
+#[test]
+fn justify_width_constant_0_test() {
+    let data = create_vector::<3, 3>();
+    let table = Table::new(&data)
+        .with(Style::github_markdown())
+        .with(Justify::new(0))
+        .to_string();
+
+    assert_eq!(
+        table,
+        concat!(
+            "|  |  |  |  |\n",
+            "|--+--+--+--|\n",
+            "|  |  |  |  |\n",
+            "|  |  |  |  |\n",
+            "|  |  |  |  |\n",
+        )
+    );
+}
+
+#[test]
+fn justify_width_min_test() {
+    let data = create_vector::<3, 3>();
+    let m = Justify::min();
+    let table = Table::new(&data)
+        .with(Style::github_markdown())
+        .with(m)
+        .to_string();
+
+    assert_eq!(
+        table,
+        concat!(
+            "| N | c | c | c |\n",
+            "|---+---+---+---|\n",
+            "| 0 | 0 | 0 | 0 |\n",
+            "| 1 | 1 | 1 | 1 |\n",
+            "| 2 | 2 | 2 | 2 |\n",
+        )
+    );
+}
+
+#[test]
+fn justify_width_max_test() {
+    let data = create_vector::<3, 3>();
+    let table = Table::new(&data)
+        .with(Style::github_markdown())
+        .with(Justify::max())
+        .to_string();
+
+    assert_eq!(
+        table,
+        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",
+        )
+    );
+}