Repository: zhiburt/tabled
Pull request #289 (2023-02-17 19:45:46)

bump 'clap' dependency


Patch:
diff --git a/.gitignore b/.gitignore
index c80e472..e4be040 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,6 @@
 # Generated by Cargo
 # will have compiled files and executables
 /target/
-/papergrid/target/
 **/target/
 
 # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
diff --git a/papergrid/examples/common_grid_no_std.rs b/papergrid/examples/common_grid_no_std.rs
index 7d5bdb8..7356992 100644
--- a/papergrid/examples/common_grid_no_std.rs
+++ b/papergrid/examples/common_grid_no_std.rs
@@ -6,8 +6,6 @@ use papergrid::{
 };
 
 fn main() {
-    let cfg = generate_table_config();
-
     let data = [
         ["Papergrid", "is a library", "for print tables", "!"],
         [
@@ -17,9 +15,10 @@ fn main() {
             "H\ne\nl\nl\no",
         ],
     ];
-    let records = IterRecords::new(data, 4, None);
 
-    let dim = ConstDims(&[20, 15, 40, 3], 1);
+    let records = IterRecords::new(data, 4, None);
+    let dim = ConstDims(&[20, 15, 40, 3], 4);
+    let cfg = generate_table_config();
 
     let grid = CompactGrid::new(records, &dim, &cfg);
 
@@ -51,7 +50,7 @@ fn generate_table_config() -> CompactConfig {
         .set_padding(Sides::new(
             Indent::spaced(1),
             Indent::spaced(1),
-            Indent::spaced(0),
+            Indent::spaced(3),
             Indent::spaced(0),
         ))
 }
diff --git a/papergrid/src/color/ansi_color.rs b/papergrid/src/color/ansi_color.rs
index 2872270..2d0049b 100644
--- a/papergrid/src/color/ansi_color.rs
+++ b/papergrid/src/color/ansi_color.rs
@@ -3,7 +3,7 @@ use std::{
     fmt::{self, Write},
 };
 
-use super::Color;
+use super::{Color, StaticColor};
 
 /// The structure represents a ANSI color by suffix and prefix.
 #[derive(Debug, Clone, Eq, PartialEq, Default)]
@@ -74,3 +74,12 @@ fn parse_ansi_color(s: &str) -> Option<AnsiColor<'static>> {
 
     Some(AnsiColor::new(start.into(), end.into()))
 }
+
+impl From<StaticColor> for AnsiColor<'static> {
+    fn from(value: StaticColor) -> Self {
+        Self::new(
+            Cow::Borrowed(value.get_prefix()),
+            Cow::Borrowed(value.get_suffix()),
+        )
+    }
+}
diff --git a/papergrid/src/color/static_color.rs b/papergrid/src/color/static_color.rs
index 5ed2d06..b1e03a4 100644
--- a/papergrid/src/color/static_color.rs
+++ b/papergrid/src/color/static_color.rs
@@ -23,12 +23,12 @@ impl StaticColor {
 
 impl StaticColor {
     /// Gets a reference to a prefix.
-    pub fn get_prefix(&self) -> &str {
+    pub fn get_prefix(&self) -> &'static str {
         self.prefix
     }
 
     /// Gets a reference to a suffix.
-    pub fn get_suffix(&self) -> &str {
+    pub fn get_suffix(&self) -> &'static str {
         self.suffix
     }
 }
diff --git a/papergrid/src/grid/compact/config.rs b/papergrid/src/grid/compact/config.rs
index a324650..7ce9109 100644
--- a/papergrid/src/grid/compact/config.rs
+++ b/papergrid/src/grid/compact/config.rs
@@ -4,7 +4,7 @@
 
 use crate::color::StaticColor;
 
-use crate::config::{AlignmentHorizontal, AlignmentVertical, Borders, Indent, Line, Sides};
+use crate::config::{AlignmentHorizontal, Borders, Indent, Line, Sides};
 
 /// This structure represents a settings of a grid.
 ///
@@ -19,7 +19,6 @@ pub struct CompactConfig {
     padding: Sides<Indent>,
     padding_color: Sides<StaticColor>,
     halignment: AlignmentHorizontal,
-    valignment: AlignmentVertical,
     tab_width: usize,
 }
 
@@ -35,7 +34,6 @@ impl CompactConfig {
         Self {
             tab_width: 4,
             halignment: AlignmentHorizontal::Left,
-            valignment: AlignmentVertical::Top,
             horizontal_line1: None,
             borders: Borders::empty(),
             border_colors: Borders::empty(),
@@ -121,17 +119,6 @@ impl CompactConfig {
         self.halignment
     }
 
-    /// Set a horizontal alignment to a given cells.
-    pub const fn set_alignment_vertical(mut self, alignment: AlignmentVertical) -> Self {
-        self.valignment = alignment;
-        self
-    }
-
-    /// Get a alignment vertical.
-    pub const fn get_alignment_vertical(&self) -> AlignmentVertical {
-        self.valignment
-    }
-
     /// Sets colors of border carcass on the grid.
     pub const fn set_borders_color(mut self, borders: Borders<StaticColor>) -> Self {
         self.border_colors = borders;
@@ -145,8 +132,8 @@ impl CompactConfig {
     }
 
     /// Returns a margin color.
-    pub const fn get_margin_color(&self) -> &Sides<StaticColor> {
-        &self.margin_color
+    pub const fn get_margin_color(&self) -> Sides<StaticColor> {
+        self.margin_color
     }
 
     /// Set a padding to a given cells.
diff --git a/papergrid/src/grid/compact/dimension.rs b/papergrid/src/grid/compact/dimension.rs
index 3b34378..d17cf20 100644
--- a/papergrid/src/grid/compact/dimension.rs
+++ b/papergrid/src/grid/compact/dimension.rs
@@ -17,7 +17,7 @@ use super::config::CompactConfig;
 /// [`Grid`]: crate::grid::spanned::Grid
 #[derive(Debug, Default, Clone, PartialEq, Eq)]
 pub struct ExactDimension {
-    height: Vec<usize>,
+    height: usize,
     width: Vec<usize>,
 }
 
@@ -31,11 +31,6 @@ impl ExactDimension {
     pub fn width<R: Records>(records: R, cfg: &CompactConfig) -> Vec<usize> {
         build_width(records, cfg)
     }
-
-    /// Return width and height lists.
-    pub fn get_values(self) -> (Vec<usize>, Vec<usize>) {
-        (self.width, self.height)
-    }
 }
 
 impl Dimension for ExactDimension {
@@ -43,41 +38,20 @@ impl Dimension for ExactDimension {
         self.width[column]
     }
 
-    fn get_height(&self, row: usize) -> usize {
-        self.height[row]
+    fn get_height(&self, _: usize) -> usize {
+        self.height
     }
 }
 
 impl Estimate<CompactConfig> for ExactDimension {
     fn estimate<R: Records>(&mut self, records: R, cfg: &CompactConfig) {
-        let (width, height) = build_dimensions(records, cfg);
-        self.width = width;
-        self.height = height;
+        self.width = Self::width(records, cfg);
+        self.height = calc_height(cfg);
     }
 }
 
-fn build_dimensions<R: Records>(records: R, cfg: &CompactConfig) -> (Vec<usize>, Vec<usize>) {
-    let count_columns = records.count_columns();
-
-    let mut widths = vec![0; count_columns];
-    let mut heights = vec![];
-
-    for columns in records.iter_rows() {
-        let mut row_height = 0;
-        for (col, cell) in columns.into_iter().enumerate() {
-            let cell = cell.as_ref();
-
-            let width = get_cell_width(cell, cfg);
-            widths[col] = max(widths[col], width);
-
-            let height = get_cell_height(cell, cfg);
-            row_height = max(row_height, height);
-        }
-
-        heights.push(row_height);
-    }
-
-    (widths, heights)
+fn calc_height(cfg: &CompactConfig) -> usize {
+    1 + cfg.get_padding().top.size + cfg.get_padding().bottom.size
 }
 
 fn build_height<R: Records>(records: R, cfg: &CompactConfig) -> Vec<usize> {
diff --git a/papergrid/src/grid/compact/grid.rs b/papergrid/src/grid/compact/grid.rs
index 3896a53..430144b 100644
--- a/papergrid/src/grid/compact/grid.rs
+++ b/papergrid/src/grid/compact/grid.rs
@@ -9,7 +9,7 @@ use core::{
 use crate::{
     color::{Color, StaticColor},
     colors::{Colors, NoColors},
-    config::{AlignmentHorizontal, AlignmentVertical, Borders, Indent, Line, Sides},
+    config::{AlignmentHorizontal, Borders, Indent, Line, Sides},
     dimension::Dimension,
     records::Records,
     util::string::string_width_tab,
@@ -143,7 +143,6 @@ fn print_grid<F: Write, R: Records, D: Dimension, C: Colors>(
     let margin_color = cfg.get_margin_color();
     let pad = create_padding(cfg);
     let align = cfg.get_alignment_horizontal();
-    let valign = cfg.get_alignment_vertical();
     let tab_size = cfg.get_tab_width();
     let mar = (
         (margin.left, margin_color.left),
@@ -217,25 +216,18 @@ fn print_grid<F: Write, R: Records, D: Dimension, C: Colors>(
             print_indent2(f, margin.right, margin_color.right)?;
         }
 
-        let height = dims.get_height(row);
-
-        if height > 0 {
-            if new_line {
-                f.write_char('\n')?;
-            }
-
-            let columns = columns
-                .enumerate()
-                .map(|(col, text)| (text, colors.get_color((row, col))));
+        if new_line {
+            f.write_char('\n')?;
+        }
 
-            let widths = widths.clone();
-            print_grid_row(
-                f, columns, widths, height, mar, pad, vert, align, valign, tab_size,
-            )?;
+        let columns = columns
+            .enumerate()
+            .map(|(col, text)| (text, colors.get_color((row, col))));
 
-            new_line = true;
-        }
+        let widths = widths.clone();
+        print_grid_row(f, columns, widths, mar, pad, vert, align, tab_size)?;
 
+        new_line = true;
         row += 1;
     }
 
@@ -270,14 +262,12 @@ type ColoredIndent = (Indent, StaticColor);
 #[allow(clippy::too_many_arguments)]
 fn print_grid_row<F, I, T, C, D>(
     f: &mut F,
-    mut columns: I,
+    columns: I,
     widths: D,
-    height: usize,
     mar: (ColoredIndent, ColoredIndent),
     pad: Sides<ColoredIndent>,
     vert: (BorderChar, BorderChar, BorderChar),
     align: AlignmentHorizontal,
-    valign: AlignmentVertical,
     tab_size: usize,
 ) -> fmt::Result
 where
@@ -287,33 +277,19 @@ where
     C: Color,
     D: Iterator<Item = usize> + Clone,
 {
-    {
-        let top_indent = top_indent(pad.top.0.size, valign, 1, height);
-        if top_indent > 0 {
-            for _ in 0..top_indent {
-                print_indent2(f, mar.0 .0, mar.0 .1)?;
-                print_columns_empty(f, widths.clone(), vert)?;
-                print_indent2(f, mar.1 .0, mar.1 .1)?;
-
-                f.write_char('\n')?;
-            }
-        }
-    }
-    {
-        if pad.top.0.size > 0 {
-            for _ in 0..pad.top.0.size {
-                print_indent2(f, mar.0 .0, mar.0 .1)?;
-                print_columns_empty_colored(f, widths.clone(), vert, pad.top.1)?;
-                print_indent2(f, mar.1 .0, mar.1 .1)?;
+    if pad.top.0.size > 0 {
+        for _ in 0..pad.top.0.size {
+            print_indent2(f, mar.0 .0, mar.0 .1)?;
+            print_columns_empty_colored(f, widths.clone(), vert, pad.top.1)?;
+            print_indent2(f, mar.1 .0, mar.1 .1)?;
 
-                f.write_char('\n')?;
-            }
+            f.write_char('\n')?;
         }
     }
 
-    let widths1 = widths.clone();
-    let columns = widths1.map(move |width| {
-        let (text, color) = columns.next().expect("must be here");
+    let mut widths1 = widths.clone();
+    let columns = columns.map(move |(text, color)| {
+        let width = widths1.next().expect("must be here");
         (text, color, width)
     });
 
@@ -321,16 +297,12 @@ where
     print_row_columns(f, columns, vert, pad, align, tab_size)?;
     print_indent2(f, mar.1 .0, mar.1 .1)?;
 
-    {
-        if pad.bottom.0.size > 0 {
-            for _ in 0..pad.bottom.0.size {
-                f.write_char('\n')?;
+    for _ in 0..pad.bottom.0.size {
+        f.write_char('\n')?;
 
-                print_indent2(f, mar.0 .0, mar.0 .1)?;
-                print_columns_empty_colored(f, widths.clone(), vert, pad.bottom.1)?;
-                print_indent2(f, mar.1 .0, mar.1 .1)?;
-            }
-        }
+        print_indent2(f, mar.0 .0, mar.0 .1)?;
+        print_columns_empty_colored(f, widths.clone(), vert, pad.bottom.1)?;
+        print_indent2(f, mar.1 .0, mar.1 .1)?;
     }
 
     Ok(())
@@ -414,9 +386,9 @@ fn total_columns_width<D: Dimension>(count_columns: usize, dims: &D) -> usize {
 }
 
 fn count_verticals(cfg: &CompactConfig, count_columns: usize) -> usize {
-    assert!(count_columns > 1);
+    assert!(count_columns > 0);
 
-    let count_verticals = count_columns - 2;
+    let count_verticals = count_columns - 1;
     let borders = cfg.get_borders();
     borders.has_vertical() as usize * count_verticals
         + borders.has_left() as usize
@@ -499,34 +471,6 @@ fn print_columns_empty_colored<F: Write, I: Iterator<Item = usize>>(
     Ok(())
 }
 
-fn print_columns_empty<F: Write, I: Iterator<Item = usize>>(
-    f: &mut F,
-    mut columns: I,
-    borders: (BorderChar, BorderChar, BorderChar),
-) -> Result<(), fmt::Error> {
-    if let Some((c, color)) = borders.0 {
-        print_char(f, c, color)?;
-    }
-
-    if let Some(width) = columns.next() {
-        repeat_char(f, ' ', width)?;
-    }
-
-    for width in columns {
-        if let Some((c, color)) = borders.1 {
-            print_char(f, c, color)?;
-        }
-
-        repeat_char(f, ' ', width)?;
-    }
-
-    if let Some((c, color)) = borders.2 {
-        print_char(f, c, color)?;
-    }
-
-    Ok(())
-}
-
 fn print_cell<F: Write, C: Color>(
     f: &mut F,
     text: &str,
@@ -672,26 +616,6 @@ fn prepare_coloring<F: Write>(f: &mut F, clr: &StaticColor, used: &mut StaticCol
     Ok(())
 }
 
-fn top_indent(
-    pad_top: usize,
-    valign: AlignmentVertical,
-    cell_height: usize,
-    available: usize,
-) -> usize {
-    let height = available - pad_top;
-    let indent = indent_from_top(valign, height, cell_height);
-
-    indent + pad_top
-}
-
-fn indent_from_top(alignment: AlignmentVertical, available: usize, real: usize) -> usize {
-    match alignment {
-        AlignmentVertical::Top => 0,
-        AlignmentVertical::Bottom => available - real,
-        AlignmentVertical::Center => (available - real) / 2,
-    }
-}
-
 fn calculate_indent(
     alignment: AlignmentHorizontal,
     text_width: usize,
diff --git a/papergrid/src/grid/spanned/config/mod.rs b/papergrid/src/grid/spanned/config/mod.rs
index 368e0a2..1ce3a8f 100644
--- a/papergrid/src/grid/spanned/config/mod.rs
+++ b/papergrid/src/grid/spanned/config/mod.rs
@@ -9,8 +9,9 @@ mod offset;
 
 use std::collections::HashMap;
 
-use crate::color::AnsiColor;
+use crate::color::{AnsiColor, StaticColor};
 use crate::config::{AlignmentHorizontal, AlignmentVertical, Entity, Indent, Position, Sides};
+use crate::grid::compact::CompactConfig;
 use borders_config::BordersConfig;
 
 pub use self::{entity_map::EntityMap, formatting::Formatting, offset::Offset};
@@ -675,6 +676,65 @@ impl From<&GridConfig> for GridConfig {
     }
 }
 
+impl From<CompactConfig> for GridConfig {
+    fn from(compact: CompactConfig) -> Self {
+        use Entity::Global;
+
+        let mut cfg = Self::default();
+        cfg.set_padding(Global, *compact.get_padding());
+        cfg.set_padding_color(
+            Global,
+            sides_static_color_to_ansi_color(compact.get_padding_color()),
+        );
+        cfg.set_margin(*compact.get_margin());
+        cfg.set_margin_color(sides_static_color_to_ansi_color(compact.get_margin_color()));
+        cfg.set_alignment_horizontal(Global, compact.get_alignment_horizontal());
+        cfg.set_borders(*compact.get_borders());
+        cfg.set_borders_color(borders_static_color_to_ansi_color(
+            *compact.get_borders_color(),
+        ));
+        cfg.set_tab_width(compact.get_tab_width());
+
+        if let Some(line) = compact.get_first_horizontal_line() {
+            cfg.set_horizontal_line(
+                1,
+                HorizontalLine {
+                    intersection: line.intersection,
+                    left: line.connect1,
+                    right: line.connect2,
+                    main: Some(line.main),
+                },
+            );
+        }
+
+        cfg
+    }
+}
+
+fn sides_static_color_to_ansi_color(b: Sides<StaticColor>) -> Sides<AnsiColor<'static>> {
+    Sides::new(b.left.into(), b.right.into(), b.top.into(), b.bottom.into())
+}
+
+fn borders_static_color_to_ansi_color(b: crate::config::Borders<StaticColor>) -> BordersColor {
+    BordersColor {
+        left: b.left.map(|c| c.into()),
+        right: b.right.map(|c| c.into()),
+        top: b.top.map(|c| c.into()),
+        bottom: b.bottom.map(|c| c.into()),
+        bottom_intersection: b.bottom_intersection.map(|c| c.into()),
+        bottom_left: b.bottom_left.map(|c| c.into()),
+        bottom_right: b.bottom_right.map(|c| c.into()),
+        horizontal: b.horizontal.map(|c| c.into()),
+        intersection: b.intersection.map(|c| c.into()),
+        left_intersection: b.left_intersection.map(|c| c.into()),
+        right_intersection: b.right_intersection.map(|c| c.into()),
+        top_intersection: b.top_intersection.map(|c| c.into()),
+        top_left: b.top_left.map(|c| c.into()),
+        top_right: b.top_right.map(|c| c.into()),
+        vertical: b.vertical.map(|c| c.into()),
+    }
+}
+
 fn set_cell_row_span(cfg: &mut GridConfig, pos: Position, span: usize) {
     // such spans aren't supported
     if span == 0 {
diff --git a/papergrid/src/records/iter_records.rs b/papergrid/src/records/iter_records.rs
index d358598..3d4f507 100644
--- a/papergrid/src/records/iter_records.rs
+++ b/papergrid/src/records/iter_records.rs
@@ -10,7 +10,7 @@ pub struct IterRecords<I> {
 
 impl<I> IterRecords<I> {
     /// Returns a new [IterRecords] object.
-    pub fn new(iter: I, count_columns: usize, count_rows: Option<usize>) -> Self {
+    pub const fn new(iter: I, count_columns: usize, count_rows: Option<usize>) -> Self {
         Self {
             iter,
             count_columns,
diff --git a/papergrid/src/util/string.rs b/papergrid/src/util/string.rs
index bbb1112..e366776 100644
--- a/papergrid/src/util/string.rs
+++ b/papergrid/src/util/string.rs
@@ -6,18 +6,60 @@
 
 /// Returns a string width with correction to tab width.
 pub fn string_width_tab(text: &str, tab_width: usize) -> usize {
-    let width = string_width(text);
-    let count_tabs = count_tabs(text);
+    #[cfg(not(feature = "color"))]
+    {
+        __string_width_tab(text, tab_width)
+    }
+
+    #[cfg(feature = "color")]
+    {
+        // we need to strip ansi because of terminal links
+        // and they're can't be stripped by ansi_str.
+
+        ansitok::parse_ansi(text)
+            .filter(|e| e.kind() == ansitok::ElementKind::Text)
+            .map(|e| &text[e.start()..e.end()])
+            .map(|e| __string_width_tab(e, tab_width))
+            .sum()
+    }
+}
 
-    width + count_tabs * tab_width
+fn __string_width_tab(text: &str, tab_width: usize) -> usize {
+    text.chars().fold(0, |acc, c| {
+        if c == '\t' {
+            acc + tab_width
+        } else {
+            let w = unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
+            acc + w
+        }
+    })
 }
 
 /// Returns a max per line string width with correction to tab width.
 pub fn string_width_multiline_tab(text: &str, tab_width: usize) -> usize {
-    text.lines()
-        .map(|line| string_width_tab(line, tab_width))
-        .max()
-        .unwrap_or(0)
+    #[cfg(not(feature = "color"))]
+    {
+        let (acc, max) = text.chars().fold((0, 0), |(acc, max), c| {
+            if c == '\t' {
+                (acc + tab_width, max)
+            } else if c == '\n' {
+                (0, std::cmp::max(acc, max))
+            } else {
+                let w = unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
+                (acc + w, max)
+            }
+        });
+
+        std::cmp::max(acc, max)
+    }
+
+    #[cfg(feature = "color")]
+    {
+        text.lines()
+            .map(|line| string_width_tab(line, tab_width))
+            .max()
+            .unwrap_or(0)
+    }
 }
 
 /// Returns a string width.
diff --git a/tabled/Cargo.toml b/tabled/Cargo.toml
index b9194c1..8aeb28b 100644
--- a/tabled/Cargo.toml
+++ b/tabled/Cargo.toml
@@ -65,8 +65,8 @@ path = "examples/derive/display_with.rs"
 required-features = ["derive"]
 
 [[example]]
-name = "basic"
-path = "examples/basic.rs"
+name = "table"
+path = "examples/table.rs"
 required-features = ["derive"]
 
 [[example]]
@@ -234,6 +234,11 @@ name = "compact_table2"
 path = "examples/compact_table2.rs"
 required-features = []
 
+[[example]]
+name = "alphabet"
+path = "examples/alphabet.rs"
+required-features = ["std"]
+
 [features]
 default = ["derive", "macros"]
 std = ["papergrid/std"]
diff --git a/tabled/benches/Cargo.toml b/tabled/benches/functions/Cargo.toml
similarity index 71%
rename from tabled/benches/Cargo.toml
rename to tabled/benches/functions/Cargo.toml
index 96c644c..006da7c 100644
--- a/tabled/benches/Cargo.toml
+++ b/tabled/benches/functions/Cargo.toml
@@ -5,11 +5,13 @@ edition = "2021"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
+[workspace]
+
 [dependencies]
 
 [dev-dependencies]
-tabled = { path = "..", features = ["color"] }
-tabled_master = { version = "0.9.0", features = ["color"], package = "tabled"}
+tabled = { path = "../../..", features = ["color"] }
+tabled_master = { version = "0.10.0", features = ["color"], package = "tabled"}
 
 owo-colors = "3.5.0"
 criterion = { version = "0.4.0", features = ["html_reports"] }
diff --git a/tabled/benches/benches/strings.rs b/tabled/benches/functions/benches/strings.rs
similarity index 100%
rename from tabled/benches/benches/strings.rs
rename to tabled/benches/functions/benches/strings.rs
diff --git a/tabled/benches/benches/to_string.rs b/tabled/benches/functions/benches/to_string.rs
similarity index 100%
rename from tabled/benches/benches/to_string.rs
rename to tabled/benches/functions/benches/to_string.rs
diff --git a/tabled/benches/lib_comp/Cargo.toml b/tabled/benches/lib_comp/Cargo.toml
new file mode 100644
index 0000000..47adc0b
--- /dev/null
+++ b/tabled/benches/lib_comp/Cargo.toml
@@ -0,0 +1,29 @@
+[package]
+name = "lib_comp"
+version = "0.0.0"
+authors = ["Maxim Zhiburt <zhiburt@gmail.com>"]
+edition = "2018"
+description = "An easy to use library for pretty print tables of Rust `struct`s and `enum`s."
+repository = "https://github.com/zhiburt/tabled"
+homepage = "https://github.com/zhiburt/tabled"
+license = "MIT"
+
+[workspace]
+members = ["crates/*", "readme"]
+
+[dependencies]
+tabled = { path = "./crates/tabled" }
+tabled_color = { path = "./crates/tabled_color" }
+tabled_current = { path = "./crates/tabled_current" }
+tabled_color_current = { path = "./crates/tabled_color_current" }
+cli-table = "0.4.7"
+comfy-table = "6.1.4"
+term-table = "1.3.2"
+prettytable-rs = "0.10.0"
+
+[dev-dependencies]
+criterion = "0.3.5"
+
+[[bench]]
+name = "benchmark"
+harness = false
diff --git a/tabled/benches/lib_comp/README.md b/tabled/benches/lib_comp/README.md
new file mode 100644
index 0000000..89f671c
--- /dev/null
+++ b/tabled/benches/lib_comp/README.md
@@ -0,0 +1,39 @@
+# Benchmark's result
+
+|                          | cli_table      | comfy_table  | tabled             | tabled_color       | tabled_par        | term_table    |
+|--------------------------|----------------|--------------|--------------------|--------------------|-------------------|---------------|
+| test_const_table/1       | 2.8±0.02µs     | 5.3±0.07µs   | 1974.4±100.72ns    | **1727.3±24.80ns** | 20.2±0.49µs       | 4.7±0.04µs    |
+| test_const_table/8       | 53.8±0.76µs    | 59.0±2.65µs  | **26.3±0.27µs**    | 27.8±0.70µs        | 79.6±4.62µs       | 101.9±0.86µs  |
+| test_const_table/32      | 662.5±8.78µs   | 865.4±4.49µs | **395.8±2.06µs**   | 398.6±2.96µs       | 450.6±21.39µs     | 1281.5±8.86µs |
+| test_const_table/128     | 9.6±0.11ms     | 11.9±0.09ms  | **5.4±0.04ms**     | 6.1±0.04ms         | 5.5±0.28ms        | 18.0±0.14ms   |
+| test_const_table/512     | 172.0±1.44ms   | 195.5±1.27ms | 91.3±2.43ms        | 90.7±2.05ms        | **80.7±2.70ms**   | 290.6±1.49ms  |
+| test_dynamic_table/1     | 2.6±0.03µs     | 4.8±0.06µs   | 1470.9±22.17ns     | **1453.8±28.48ns** | 19.3±0.53µs       | 3.0±0.02µs    |
+| test_dynamic_table/8     | 46.6±0.94µs    | 50.0±0.49µs  | 22.3±0.24µs        | **21.7±0.29µs**    | 71.9±3.84µs       | 68.0±0.75µs   |
+| test_dynamic_table/32    | 547.3±4.25µs   | 784.6±7.05µs | 338.5±3.10µs       | **336.8±5.03µs**   | 401.4±21.20µs     | 984.3±9.86µs  |
+| test_dynamic_table/128   | 7.5±0.07ms     | 11.7±0.09ms  | **4.6±0.05ms**     | **4.6±0.05ms**     | 4.8±0.35ms        | 14.8±0.12ms   |
+| test_dynamic_table/512   | 138.3±1.32ms   | 172.9±1.40ms | 79.8±2.25ms        | 85.6±2.14ms        | **70.3±3.30ms**   | 246.3±1.33ms  |
+| test_empty_table/1       | 1607.7±16.72ns | 5.2±0.06µs   | **1378.8±20.19ns** | 1408.2±11.60ns     | 19.7±1.09µs       | 2.3±0.02µs    |
+| test_empty_table/8       | 24.1±0.66µs    | 50.7±0.34µs  | 21.2±0.15µs        | **18.7±0.17µs**    | 66.6±4.17µs       | 46.2±0.51µs   |
+| test_empty_table/32      | 291.0±3.47µs   | 684.9±6.03µs | **243.9±1.44µs**   | 244.0±1.50µs       | 323.0±31.64µs     | 617.8±6.25µs  |
+| test_empty_table/128     | 3.8±0.04ms     | 10.1±0.17ms  | **3.7±0.03ms**     | **3.7±0.03ms**     | 4.0±0.27ms        | 9.1±0.06ms    |
+| test_empty_table/512     | 70.1±1.01ms    | 184.0±1.35ms | 71.4±1.87ms        | 70.5±1.85ms        | **57.2±2.55ms**   | 157.5±1.12ms  |
+| test_multiline_table/1   | 8.0±0.28µs     | 11.7±0.11µs  | **5.4±0.22µs**     | **5.4±0.03µs**     | 29.5±0.70µs       | 10.8±0.24µs   |
+| test_multiline_table/8   | 181.8±3.24µs   | 234.6±4.86µs | 133.7±12.06µs      | **127.8±1.51µs**   | 194.4±14.10µs     | 356.9±3.38µs  |
+| test_multiline_table/32  | 2.1±0.02ms     | 3.1±0.03ms   | **1777.7±12.99µs** | 1849.3±17.30µs     | 1865.5±74.60µs    | 4.9±0.07ms    |
+| test_multiline_table/128 | 36.7±0.39ms    | 52.6±0.53ms  | **28.3±0.18ms**    | **28.3±0.18ms**    | 29.1±1.86ms       | 65.2±0.49ms   |
+| test_multiline_table/512 | 550.1±2.67ms   | 774.2±4.13ms | 501.1±4.18ms       | 496.1±2.68ms       | **450.9±21.15ms** | 1061.6±2.10ms |
+
+## Table Generation
+
+You can generate the table by this command (it relays on `critcmp`).
+
+```bash
+cargo run --manifest-path=readme/Cargo.toml
+```
+
+#### System
+
+```
+Kernel: 5.18.9-arch1-1 
+CPU: 11th Gen Intel i7-11850H (16) @ 4.800GHz
+```
diff --git a/tabled/benches/lib_comp/benches/benchmark.rs b/tabled/benches/lib_comp/benches/benchmark.rs
new file mode 100644
index 0000000..590a7c9
--- /dev/null
+++ b/tabled/benches/lib_comp/benches/benchmark.rs
@@ -0,0 +1,108 @@
+use criterion::{black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};
+
+macro_rules! bench_lib {
+    ($name:ident, $data_fn:expr, $({ $lib_name:expr, $lib_table:expr }),* $(,)?) => {
+        pub fn $name(c: &mut Criterion) {
+            let mut group = c.benchmark_group(stringify!($name));
+
+            for size in [1, 8, 32, 128, 512] {
+                let (columns, data) = $data_fn(size);
+
+                $({
+                    group.bench_with_input(BenchmarkId::new($lib_name, size), &size, |b, _size| {
+                        b.iter_batched(
+                            || (columns.clone(), data.clone()),
+                            |(columns, data)| { let _ = black_box($lib_table(columns, data)); },
+                            BatchSize::SmallInput
+                        );
+                    });
+                })*
+            }
+
+            group.finish();
+        }
+    };
+}
+
+macro_rules! create_bench {
+    ($name:ident, $table:expr) => {
+        bench_lib!(
+            $name,
+            $table,
+            // { "tabled", lib_comp::tabled::build },
+            // { "tabled_color", lib_comp::tabled_color::build },
+            { "tabled", lib_comp::tabled_current::build },
+            { "tabled_color", lib_comp::tabled_color_current::build },
+            { "tabled_iter", lib_comp::tabled_current_iter::build },
+            { "tabled_compact", lib_comp::tabled_current_compact::build },
+            { "cli_table", lib_comp::cli_table::build  },
+            { "comfy_table", lib_comp::comfy_table::build },
+            { "term_table", lib_comp::term_table::build  },
+            { "prettytable_rs", lib_comp::prettytable_rs::build },
+        );
+    };
+}
+
+create_bench!(test_empty_table, |size| build_cost_table(size, "", ""));
+
+create_bench!(test_const_table, |size| build_cost_table(
+    size,
+    "Hello World",
+    "Hi!"
+));
+
+create_bench!(test_dynamic_table, build_dynamic_table);
+
+create_bench!(test_multiline_table, |size| build_cost_table(
+    size,
+    "H\ne\nl\nlo\nWo\nr\nld",
+    "Hello\n111\n111\ni\n!"
+));
+
+criterion_group!(
+    benches,
+    test_empty_table,
+    test_const_table,
+    test_dynamic_table,
+    test_multiline_table,
+);
+criterion_main!(benches);
+
+fn build_cost_table<H, R>(size: usize, header: H, record: R) -> (Vec<String>, Vec<Vec<String>>)
+where
+    H: Into<String>,
+    R: Into<String>,
+{
+    (
+        vec![header.into(); size],
+        vec![vec![record.into(); size]; size],
+    )
+}
+
+fn build_dynamic_table(size: usize) -> (Vec<String>, Vec<Vec<String>>) {
+    let mut data = Vec::with_capacity(size);
+    for i in 0..size {
+        let mut row = build_row(size, |i| format!("{i}"));
+
+        // just make things more complex
+        if i % 2 == 0 {
+            row.sort_by(|a, b| b.cmp(a));
+        }
+
+        data.push(row);
+    }
+
+    let columns = build_row(size, |i| format!("{i}"));
+
+    (columns, data)
+}
+
+fn build_row(size: usize, f: impl Fn(usize) -> String) -> Vec<String> {
+    let mut row = Vec::with_capacity(size);
+    for i in 0..size {
+        let s = f(i);
+        row.push(s);
+    }
+
+    row
+}
diff --git a/tabled/benches/lib_comp/crates/tabled/Cargo.toml b/tabled/benches/lib_comp/crates/tabled/Cargo.toml
new file mode 100644
index 0000000..1da1f8f
--- /dev/null
+++ b/tabled/benches/lib_comp/crates/tabled/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "tabled"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+tabled = { version = "0.10.0", default-features = false }
diff --git a/tabled/benches/lib_comp/crates/tabled/src/lib.rs b/tabled/benches/lib_comp/crates/tabled/src/lib.rs
new file mode 100644
index 0000000..4d1ae0e
--- /dev/null
+++ b/tabled/benches/lib_comp/crates/tabled/src/lib.rs
@@ -0,0 +1,1 @@
+pub use tabled::{self, *};
diff --git a/tabled/benches/lib_comp/crates/tabled_color/Cargo.toml b/tabled/benches/lib_comp/crates/tabled_color/Cargo.toml
new file mode 100644
index 0000000..2e96d64
--- /dev/null
+++ b/tabled/benches/lib_comp/crates/tabled_color/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "tabled_color"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+tabled = { version = "0.10.0", default-features = false, features = ["color"] }
diff --git a/tabled/benches/lib_comp/crates/tabled_color/src/lib.rs b/tabled/benches/lib_comp/crates/tabled_color/src/lib.rs
new file mode 100644
index 0000000..4d1ae0e
--- /dev/null
+++ b/tabled/benches/lib_comp/crates/tabled_color/src/lib.rs
@@ -0,0 +1,1 @@
+pub use tabled::{self, *};
diff --git a/tabled/benches/lib_comp/crates/tabled_color_current/Cargo.toml b/tabled/benches/lib_comp/crates/tabled_color_current/Cargo.toml
new file mode 100644
index 0000000..d127465
--- /dev/null
+++ b/tabled/benches/lib_comp/crates/tabled_color_current/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "tabled_color_current"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+tabled = { path = "../../../..", default-features = false, features = ["color"] }
diff --git a/tabled/benches/lib_comp/crates/tabled_color_current/src/lib.rs b/tabled/benches/lib_comp/crates/tabled_color_current/src/lib.rs
new file mode 100644
index 0000000..4d1ae0e
--- /dev/null
+++ b/tabled/benches/lib_comp/crates/tabled_color_current/src/lib.rs
@@ -0,0 +1,1 @@
+pub use tabled::{self, *};
diff --git a/tabled/benches/lib_comp/crates/tabled_current/Cargo.toml b/tabled/benches/lib_comp/crates/tabled_current/Cargo.toml
new file mode 100644
index 0000000..5533430
--- /dev/null
+++ b/tabled/benches/lib_comp/crates/tabled_current/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "tabled_current"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+tabled = { path = "../../../..", default-features = false }
diff --git a/tabled/benches/lib_comp/crates/tabled_current/src/lib.rs b/tabled/benches/lib_comp/crates/tabled_current/src/lib.rs
new file mode 100644
index 0000000..4d1ae0e
--- /dev/null
+++ b/tabled/benches/lib_comp/crates/tabled_current/src/lib.rs
@@ -0,0 +1,1 @@
+pub use tabled::{self, *};
diff --git a/tabled/benches/lib_comp/readme/Cargo.toml b/tabled/benches/lib_comp/readme/Cargo.toml
new file mode 100644
index 0000000..959f6ef
--- /dev/null
+++ b/tabled/benches/lib_comp/readme/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "readme"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+tabled = { version = "0.10.0", default-features = false, package = "tabled" }
diff --git a/tabled/benches/lib_comp/readme/benchcmp.sh b/tabled/benches/lib_comp/readme/benchcmp.sh
new file mode 100755
index 0000000..8b23366
--- /dev/null
+++ b/tabled/benches/lib_comp/readme/benchcmp.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+critcmp | tail +3 | awk '{print $1, $3 }' | awk -F "/" '{print $0, $1, $2, $3}'  | awk '{print $1, $3, $4, $5, $6}'
diff --git a/tabled/benches/lib_comp/readme/src/main.rs b/tabled/benches/lib_comp/readme/src/main.rs
new file mode 100644
index 0000000..d7c2e60
--- /dev/null
+++ b/tabled/benches/lib_comp/readme/src/main.rs
@@ -0,0 +1,224 @@
+use std::{
+    collections::{HashMap, HashSet},
+    io::{BufRead, BufReader, Error, ErrorKind, Read, Result},
+    process::Command,
+};
+
+use tabled::{
+    builder::Builder,
+    papergrid::{
+        records::{Records, RecordsMut},
+        width::CfgWidthFunction,
+    },
+    Style, Table, TableOption,
+};
+
+fn main() {
+    let data = collect_data().unwrap();
+    let mut benches = parse_benches(std::io::Cursor::new(data)).unwrap();
+
+    sort_benches(&mut benches);
+
+    let mut table = build_markdown_table(&benches);
+    table.with(Style::markdown());
+    table.with(HighlightMin);
+
+    println!("{}", table);
+}
+
+fn sort_benches(benches: &mut [Bench]) {
+    benches.sort_by(|a, b| {
+        a.function
+            .cmp(&b.function)
+            .then_with(|| a.group.cmp(&b.group))
+            .then_with(|| {
+                let ap = a.param.parse::<usize>().unwrap();
+                let bp = b.param.parse::<usize>().unwrap();
+
+                ap.cmp(&bp)
+            })
+    });
+}
+
+fn collect_data() -> Result<String> {
+    let output = Command::new("./readme/benchcmp.sh").output()?;
+    if !output.status.success() {
+        println!(
+            "failed to run benchcmp: status={} stdout={:?} stderr={:?}",
+            output.status,
+            String::from_utf8_lossy(&output.stdout),
+            String::from_utf8_lossy(&output.stderr),
+        );
+        return Ok(String::new());
+    }
+
+    return Ok(String::from_utf8_lossy(&output.stdout).into_owned());
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+struct Bench {
+    name: String,
+    group: String,
+    function: String,
+    param: String,
+    value: String,
+}
+
+impl Bench {
+    fn new(name: String, function: String, group: String, param: String, value: String) -> Self {
+        Self {
+            name,
+            group,
+            function,
+            param,
+            value,
+        }
+    }
+}
+
+fn parse_benches<R>(input: R) -> Result<Vec<Bench>>
+where
+    R: Read,
+{
+    let mut out = vec![];
+    let reader = BufReader::new(input);
+    for line in reader.lines() {
+        let line = line?;
+
+        let mut parts = line.split_whitespace().take(5);
+        let name = get_data_particle(&mut parts).unwrap();
+        let function = get_data_particle(&mut parts).unwrap();
+        let group = get_data_particle(&mut parts).unwrap();
+        let param = get_data_particle(&mut parts).unwrap();
+        let value = get_data_particle(&mut parts).unwrap();
+
+        out.push(Bench::new(name, function, group, param, value));
+    }
+
+    Ok(out)
+}
+
+fn get_data_particle<'a>(parts: &mut impl Iterator<Item = &'a str>) -> Result<String> {
+    parts.next().map(|s| s.to_string()).ok_or_else(|| {
+        Error::new(
+            ErrorKind::Other,
+            "unexpected format, was expected to get [group, name, value] being space separated",
+        )
+    })
+}
+
+fn build_markdown_table(benches: &[Bench]) -> Table {
+    let (_, group_index) =
+        benches
+            .iter()
+            .map(|b| &b.group)
+            .fold((0, HashMap::new()), |(mut i, mut acc), group| {
+                if !acc.contains_key(group) {
+                    acc.insert(group.to_string(), i);
+                    i += 1;
+                }
+
+                (i, acc)
+            });
+
+    let mut groups = vec![String::new(); group_index.len() + 1];
+    for (group, i) in &group_index {
+        groups[i + 1] = group.clone();
+    }
+
+    let mut funcs: Vec<(&str, &str, Vec<&Bench>)> = vec![];
+    let mut seen = HashSet::new();
+    for bench in benches {
+        let key = (&bench.function, &bench.param);
+        if seen.contains(&key) {
+            continue;
+        }
+
+        seen.insert(key);
+
+        let benches = benches
+            .iter()
+            .filter(|b| bench.function == b.function && bench.param == b.param)
+            .collect::<Vec<_>>();
+
+        funcs.push((bench.function.as_str(), bench.param.as_str(), benches));
+    }
+
+    let mut b = Builder::new();
+    b.set_columns(groups);
+
+    for (func, param, func_benches) in funcs {
+        let mut record = vec![String::new(); group_index.len() + 1];
+        record[0] = format!("{}/{}", func, param);
+
+        for bench in func_benches {
+            let i = group_index[bench.group.as_str()];
+            record[i + 1] = bench.value.to_owned();
+        }
+
+        b.add_record(record);
+    }
+
+    b.build()
+}
+
+// returns ns
+fn parse_value(val: &str) -> Option<usize> {
+    let (value, diff) = val.split_once('±')?;
+    let value = value.parse::<f32>().ok()?;
+
+    let suffix = if diff.ends_with("ns") {
+        "ns"
+    } else if diff.ends_with("µs") {
+        "µs"
+    } else if diff.ends_with("ms") {
+        "ms"
+    } else if diff.ends_with('s') {
+        "s"
+    } else {
+        return None;
+    };
+
+    let mult = match suffix {
+        "ns" => 1.0,
+        "µs" => 1_000.0,
+        "ms" => 1_000_000.0,
+        "s" => 1_000_000_000.0,
+        _ => unreachable!(),
+    };
+
+    Some((mult * value) as usize)
+}
+
+struct HighlightMin;
+
+impl<R> TableOption<R> for HighlightMin
+where
+    R: Records + RecordsMut<String>,
+{
+    fn change(&mut self, table: &mut Table<R>) {
+        let (count_rows, count_cols) = table.shape();
+        for row in 1..count_rows {
+            let values = (1..count_cols)
+                .map(|col| table.get_records().get_text((row, col)))
+                .map(parse_value)
+                .collect::<Option<Vec<_>>>();
+
+            if let Some(values) = values {
+                if let Some(min) = values.iter().min() {
+                    for (col, value) in values.iter().enumerate() {
+                        if value == min {
+                            let text = table.get_records().get_text((row, col + 1));
+                            let text = format!("**{}**", text);
+
+                            let pos = (row, col + 1);
+                            let w = CfgWidthFunction::default();
+
+                            table.get_records_mut().set(pos, text, w);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/tabled/benches/lib_comp/src/lib.rs b/tabled/benches/lib_comp/src/lib.rs
new file mode 100644
index 0000000..6617f03
--- /dev/null
+++ b/tabled/benches/lib_comp/src/lib.rs
@@ -0,0 +1,143 @@
+pub mod tabled {
+    use tabled::builder::Builder;
+
+    #[inline]
+    pub fn build(columns: Vec<String>, data: Vec<Vec<String>>) -> String {
+        let mut b = Builder::from(data);
+        b.set_columns(columns);
+        let table = b.build();
+
+        table.to_string()
+    }
+}
+
+pub mod tabled_color {
+    use tabled_color::builder::Builder;
+
+    #[inline]
+    pub fn build(columns: Vec<String>, data: Vec<Vec<String>>) -> String {
+        let mut b = Builder::from(data);
+        b.set_columns(columns);
+        let table = b.build();
+
+        table.to_string()
+    }
+}
+
+pub mod tabled_current {
+    use std::iter::FromIterator;
+    use tabled_current::builder::Builder;
+
+    #[inline]
+    pub fn build(columns: Vec<String>, data: Vec<Vec<String>>) -> String {
+        Builder::from_iter(data)
+            .set_header(columns)
+            .build()
+            .to_string()
+    }
+}
+
+pub mod tabled_color_current {
+    use std::iter::FromIterator;
+    use tabled_color_current::builder::Builder;
+
+    #[inline]
+    pub fn build(columns: Vec<String>, data: Vec<Vec<String>>) -> String {
+        Builder::from_iter(data)
+            .set_header(columns)
+            .build()
+            .to_string()
+    }
+}
+
+pub mod tabled_current_iter {
+    use tabled_current::tables::iter::IterTable;
+
+    #[inline]
+    pub fn build(columns: Vec<String>, data: Vec<Vec<String>>) -> String {
+        let count_columns = columns.len();
+
+        let mut data = data;
+        data.insert(0, columns);
+
+        IterTable::new(data).columns(count_columns).to_string()
+    }
+}
+
+pub mod tabled_current_compact {
+    use tabled_current::tables::compact::CompactTable;
+    use tabled_current::grid::compact::ExactDimension;
+
+    #[inline]
+    pub fn build(columns: Vec<String>, data: Vec<Vec<String>>) -> String {
+        let count_columns = columns.len();
+
+        let mut data = data;
+        data.insert(0, columns);
+
+        CompactTable::with_dimension(data, ExactDimension::default()).columns(count_columns).to_string()
+    }
+}
+
+pub mod cli_table {
+    use cli_table::Table;
+
+    #[inline]
+    pub fn build(columns: Vec<String>, mut data: Vec<Vec<String>>) -> String {
+        data.insert(0, columns);
+        let table = <Vec<Vec<String>> as Table>::table(data);
+
+        // here's a conversion and Vec<u8> cache which is something need to be aware of.
+        table.display().unwrap().to_string()
+    }
+}
+
+pub mod comfy_table {
+    use comfy_table::{Row, Table};
+
+    #[inline]
+    pub fn build(columns: Vec<String>, mut data: Vec<Vec<String>>) -> String {
+        data.insert(0, columns);
+
+        let mut t = Table::new();
+
+        for row in data {
+            t.add_row(Row::from(row));
+        }
+
+        t.to_string()
+    }
+}
+
+pub mod term_table {
+    use term_table::{row::Row, Table};
+
+    #[inline]
+    pub fn build(columns: Vec<String>, mut data: Vec<Vec<String>>) -> String {
+        data.insert(0, columns);
+
+        let mut t = Table::new();
+        for row in data {
+            t.add_row(Row::new(row));
+        }
+
+        t.render()
+    }
+}
+
+pub mod prettytable_rs {
+    use prettytable::{Row, Table};
+
+    #[inline]
+    pub fn build(columns: Vec<String>, mut data: Vec<Vec<String>>) -> String {
+        data.insert(0, columns);
+
+        let mut t = Table::new();
+
+        for row in data {
+            t.add_row(Row::from(row));
+        }
+
+        t.to_string()
+    }
+}
diff --git a/tabled/examples/alphabet.rs b/tabled/examples/alphabet.rs
new file mode 100644
index 0000000..b69579c
--- /dev/null
+++ b/tabled/examples/alphabet.rs
@@ -0,0 +1,11 @@
+//! The example can be run by this command
+//! `cargo run --example basic`
+
+use std::iter::FromIterator;
+
+use tabled::Table;
+
+fn main() {
+    let table = Table::from_iter(['a'..='z']);
+    println!("{table}");
+}
diff --git a/tabled/examples/colored_borders.rs b/tabled/examples/colored_borders.rs
index f5f4e80..15e0186 100644
--- a/tabled/examples/colored_borders.rs
+++ b/tabled/examples/colored_borders.rs
@@ -54,7 +54,7 @@ fn main() {
         CodeEditor::new("Neovim", "2015", "Vim community"),
     ];
 
-    let table = data.iter().collect::<Table>().with(style).to_string();
+    let table = Table::new(data).with(style).to_string();
 
     println!("{table}");
 }
diff --git a/tabled/examples/hyperlink.rs b/tabled/examples/hyperlink.rs
index d95314b..ce1241b 100644
--- a/tabled/examples/hyperlink.rs
+++ b/tabled/examples/hyperlink.rs
@@ -44,7 +44,7 @@ fn main() {
         Distribution::new(debian_repeat, false),
     ];
 
-    let mut table = Table::from_iter(&data);
+    let mut table = Table::new(&data);
     table
         .with(Style::ascii_rounded())
         .with(Alignment::left())
@@ -52,7 +52,7 @@ fn main() {
 
     println!("{table}");
 
-    let mut table = Table::from_iter(&data);
+    let mut table = Table::new(&data);
     table
         .with(Style::ascii_rounded())
         .with(Alignment::left())
diff --git a/tabled/examples/matrix.rs b/tabled/examples/matrix.rs
index 17f8ccf..a2fdd81 100644
--- a/tabled/examples/matrix.rs
+++ b/tabled/examples/matrix.rs
@@ -1,15 +1,19 @@
 //! The example can be run by this command
 //! `cargo run --example print_matrix`
 
+use std::iter::FromIterator;
+
 use tabled::{settings::style::Style, Table};
 
-fn matrix<const N: usize>() -> [[usize; N]; N] {
-    let mut matrix = [[0; N]; N];
+fn matrix<const N: usize>() -> Vec<Vec<String>> {
+    let mut matrix = vec![vec![String::new(); N]; N];
 
     #[allow(clippy::needless_range_loop)]
     for i in 0..N {
         for j in 0..N {
-            matrix[i][j] = (i + 1) * (j + 1);
+            let n = (i + 1) * (j + 1);
+            let s = str::repeat("abc\nden", n);
+            matrix[i][j] = s;
         }
     }
 
@@ -17,8 +21,8 @@ fn matrix<const N: usize>() -> [[usize; N]; N] {
 }
 
 fn main() {
-    let data = matrix::<10>();
-    let table = Table::new(data).with(Style::modern()).to_string();
+    let data = matrix::<100>();
+    let _table = Table::from_iter(data).with(Style::modern()).to_string();
 
-    println!("{table}");
+    // println!("{table}");
 }
diff --git a/tabled/examples/show/Cargo.toml b/tabled/examples/show/Cargo.toml
index 046659c..f6841dc 100644
--- a/tabled/examples/show/Cargo.toml
+++ b/tabled/examples/show/Cargo.toml
@@ -10,4 +10,4 @@ edition = "2021"
 [dependencies]
 tabled = { path = "../..", features = ["color", "derive"] }
 crossterm = "0.25.0"
-clap = { version = "3.2.19", features = ["cargo"] }
\ No newline at end of file
+clap = { version = "4.1.6", features = ["cargo", "derive"] }
diff --git a/tabled/examples/show/src/config.rs b/tabled/examples/show/src/config.rs
deleted file mode 100644
index 80bba1a..0000000
--- a/tabled/examples/show/src/config.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-use clap::{arg, command};
-
-pub struct Config {
-    pub debug: bool,
-}
-
-pub fn parse() -> Config {
-    let matches = command!()
-        .arg(arg!(
-            -d --debug ... "Turn debugging information on"
-        ))
-        .get_matches();
-
-    let debug = matches.is_present("debug");
-
-    Config { debug }
-}
diff --git a/tabled/examples/show/src/main.rs b/tabled/examples/show/src/main.rs
index 6901552..4ad8310 100644
--- a/tabled/examples/show/src/main.rs
+++ b/tabled/examples/show/src/main.rs
@@ -11,6 +11,7 @@
 //!
 //! Credit for data: https://www.boxofficemojo.com/
 
+use clap::Parser;
 use std::{
     io::{stdout, Stdout, StdoutLock, Write},
     time::Duration,
@@ -39,8 +40,6 @@ use tabled::{
     Table, Tabled,
 };
 
-mod config;
-
 #[derive(Tabled, Debug, Clone)]
 struct Movie {
     #[tabled(rename = "Release Date")]
@@ -110,8 +109,15 @@ const MOVIES: &[Movie] = &[
     },
 ];
 
+#[derive(Parser)]
+struct Config {
+    /// Turn debugging information on
+    #[clap(long, short)]
+    pub debug: bool,
+}
+
 fn main() {
-    let cfg = config::parse();
+    let cfg = Config::parse();
 
     run(MOVIES, cfg.debug);
 }
diff --git a/tabled/examples/basic.rs b/tabled/examples/table.rs
similarity index 91%
rename from tabled/examples/basic.rs
rename to tabled/examples/table.rs
index 7bb6b09..a4a159f 100644
--- a/tabled/examples/basic.rs
+++ b/tabled/examples/table.rs
@@ -1,8 +1,6 @@
 //! The example can be run by this command
 //! `cargo run --example basic`
 
-use std::iter::FromIterator;
-
 use tabled::{
     settings::{alignment::Alignment, object::Rows, style::Style, Modify},
     Table, Tabled,
@@ -32,7 +30,7 @@ fn main() {
         Distribution::new("Manjaro", "Arch", true),
     ];
 
-    let mut table = Table::from_iter(&data);
+    let mut table = Table::new(data);
     table
         .with(Style::markdown())
         .with(Modify::new(Rows::first()).with(Alignment::center()));
diff --git a/tabled/src/records/into_records/truncate_records.rs b/tabled/src/records/into_records/truncate_records.rs
index 8b417f4..fa5dac1 100644
--- a/tabled/src/records/into_records/truncate_records.rs
+++ b/tabled/src/records/into_records/truncate_records.rs
@@ -12,7 +12,7 @@ use super::either_string::EitherString;
 #[derive(Debug)]
 pub struct TruncateContent<'a, I> {
     records: I,
-    width: Width<'a>,
+    width: ExactValue<'a>,
     tab_size: usize,
 }
 
@@ -20,7 +20,7 @@ impl TruncateContent<'_, ()> {
     /// Creates new [`TruncateContent`] object.
     pub fn new<I: IntoRecords>(
         records: I,
-        width: Width<'_>,
+        width: ExactValue<'_>,
         tab_size: usize,
     ) -> TruncateContent<'_, I> {
         TruncateContent {
@@ -52,7 +52,7 @@ where
 #[derive(Debug)]
 pub struct TruncateContentIter<'a, I> {
     iter: I,
-    width: Width<'a>,
+    width: ExactValue<'a>,
     tab_size: usize,
 }
 
@@ -79,7 +79,7 @@ where
 #[derive(Debug)]
 pub struct TruncateContentColumnsIter<'a, I> {
     iter: I,
-    width: Width<'a>,
+    width: ExactValue<'a>,
     tab_size: usize,
     current: usize,
 }
@@ -112,19 +112,37 @@ where
 
 /// A width value.
 #[derive(Debug, Clone)]
-pub enum Width<'a> {
+pub enum ExactValue<'a> {
     /// Const width value.
     Exact(usize),
     /// A list of width values for columns.
     List(Cow<'a, [usize]>),
 }
 
-impl Width<'_> {
+impl<'a> From<&'a [usize]> for ExactValue<'a> {
+    fn from(value: &'a [usize]) -> Self {
+        Self::List(value.into())
+    }
+}
+
+impl From<Vec<usize>> for ExactValue<'_> {
+    fn from(value: Vec<usize>) -> Self {
+        Self::List(value.into())
+    }
+}
+
+impl From<usize> for ExactValue<'_> {
+    fn from(value: usize) -> Self {
+        Self::Exact(value)
+    }
+}
+
+impl ExactValue<'_> {
     /// Get a width by column.
     pub fn get(&self, col: usize) -> usize {
         match self {
-            Width::Exact(val) => *val,
-            Width::List(cols) => cols[col],
+            ExactValue::Exact(val) => *val,
+            ExactValue::List(cols) => cols[col],
         }
     }
 }
diff --git a/tabled/src/settings/alignment/mod.rs b/tabled/src/settings/alignment/mod.rs
index 0150fdb..e932a25 100644
--- a/tabled/src/settings/alignment/mod.rs
+++ b/tabled/src/settings/alignment/mod.rs
@@ -174,9 +174,8 @@ impl<R, D> TableOption<R, D, GridConfig> for Alignment {
 
 impl<R, D> TableOption<R, D, CompactConfig> for Alignment {
     fn change(&mut self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
-        match self.inner {
-            Horizontal(a) => *cfg = cfg.set_alignment_horizontal(a),
-            Vertical(a) => *cfg = cfg.set_alignment_vertical(a),
+        if let Horizontal(a) = self.inner {
+            *cfg = cfg.set_alignment_horizontal(a)
         }
     }
 }
diff --git a/tabled/src/tables/compact/dimension.rs b/tabled/src/tables/compact/dimension.rs
index 2622b53..3310015 100644
--- a/tabled/src/tables/compact/dimension.rs
+++ b/tabled/src/tables/compact/dimension.rs
@@ -3,32 +3,25 @@
 //! [`CompactTable`]: crate::tables::compact::CompactTable
 
 use crate::{
-    grid::{
-        compact::CompactConfig,
-        dimension::{Dimension, Estimate},
-    },
+    grid::dimension::{Dimension, Estimate},
     records::Records,
 };
 
-#[cfg(feature = "std")]
-#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
-use crate::grid::spanned::GridConfig;
-
 /// A constant size dimension or a value dimension.
 #[derive(Debug, Clone, Copy)]
-pub struct ConstantDimension<const ROWS: usize, const COLUMNS: usize> {
-    width: ConstSize<COLUMNS>,
+pub struct ConstantDimension<const COLUMNS: usize, const ROWS: usize> {
     height: ConstSize<ROWS>,
+    width: ConstSize<COLUMNS>,
 }
 
-impl<const ROWS: usize, const COLUMNS: usize> ConstantDimension<ROWS, COLUMNS> {
+impl<const COLUMNS: usize, const ROWS: usize> ConstantDimension<COLUMNS, ROWS> {
     /// Returns a new dimension object with a given estimates.
-    pub fn new(width: ConstSize<COLUMNS>, height: ConstSize<ROWS>) -> Self {
+    pub const fn new(width: ConstSize<COLUMNS>, height: ConstSize<ROWS>) -> Self {
         Self { width, height }
     }
 }
 
-impl<const ROWS: usize, const COLUMNS: usize> Dimension for ConstantDimension<ROWS, COLUMNS> {
+impl<const COLUMNS: usize, const ROWS: usize> Dimension for ConstantDimension<COLUMNS, ROWS> {
     fn get_width(&self, column: usize) -> usize {
         match self.width {
             ConstSize::List(list) => list[column],
@@ -44,18 +37,16 @@ impl<const ROWS: usize, const COLUMNS: usize> Dimension for ConstantDimension<RO
     }
 }
 
-impl<const ROWS: usize, const COLUMNS: usize> Estimate<CompactConfig>
-    for ConstantDimension<ROWS, COLUMNS>
+impl<const COLUMNS: usize, const ROWS: usize> From<ConstantDimension<COLUMNS, ROWS>>
+    for (ConstSize<COLUMNS>, ConstSize<ROWS>)
 {
-    fn estimate<R: Records>(&mut self, _: R, _: &CompactConfig) {}
+    fn from(value: ConstantDimension<COLUMNS, ROWS>) -> Self {
+        (value.width, value.height)
+    }
 }
 
-#[cfg(feature = "std")]
-#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
-impl<const ROWS: usize, const COLUMNS: usize> Estimate<GridConfig>
-    for ConstantDimension<ROWS, COLUMNS>
-{
-    fn estimate<R: Records>(&mut self, _: R, _: &GridConfig) {}
+impl<D, const COLUMNS: usize, const ROWS: usize> Estimate<D> for ConstantDimension<COLUMNS, ROWS> {
+    fn estimate<R: Records>(&mut self, _: R, _: &D) {}
 }
 
 /// Const size represents either a const array values or a single value which responsible for the whole list.
diff --git a/tabled/src/tables/compact/mod.rs b/tabled/src/tables/compact/mod.rs
index 0b4a20e..396e9e7 100644
--- a/tabled/src/tables/compact/mod.rs
+++ b/tabled/src/tables/compact/mod.rs
@@ -45,21 +45,22 @@
 //!
 //! // See what will happen if the given width is too narrow
 //!
-//! let table = CompactTable::new(&data)
+//! let mut buf = String::new();
+//! CompactTable::new(&data)
 //!     .columns(4)
 //!     .width(5)
 //!     .with(Style::ascii())
-//!     .to_string();
+//!     .build(&mut buf);
 //!
 //! assert_eq!(
-//!     table,
-//!     "+-------+-------+-------+-------+\n\
-//!      | FreeBSD | 1993  | William and Lynne Jolitz | ?     |\n\
-//!      |-------+-------+-------+-------|\n\
-//!      | OpenBSD | 1995  | Theo de Raadt |       |\n\
-//!      |-------+-------+-------+-------|\n\
-//!      | HardenedBSD | 2014  | Oliver Pinter and Shawn Webb |       |\n\
-//!      +-------+-------+-------+-------+"
+//!     buf,
+//!     "+-----+-----+-----+-----+\n\
+//!      | FreeBSD | 1993 | William and Lynne Jolitz | ?   |\n\
+//!      |-----+-----+-----+-----|\n\
+//!      | OpenBSD | 1995 | Theo de Raadt |     |\n\
+//!      |-----+-----+-----+-----|\n\
+//!      | HardenedBSD | 2014 | Oliver Pinter and Shawn Webb |     |\n\
+//!      +-----+-----+-----+-----+"
 //! );
 //! ```
 //!
@@ -67,16 +68,13 @@
 
 pub mod dimension;
 
-#[cfg(feature = "std")]
-#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
-mod utf8_writer;
-
 use core::cmp::max;
 use core::fmt;
 
 use papergrid::{
+    dimension::{Dimension, Estimate},
     grid::compact::{CompactConfig, CompactGrid},
-    util::string::{count_lines, string_width_tab},
+    util::string::string_width_tab,
 };
 
 use crate::{
@@ -92,24 +90,17 @@ use crate::{
 use self::dimension::{ConstSize, ConstantDimension};
 
 /// A table which consumes an [`IntoRecords`] iterator.
-///
-/// To be able to build table we need a descrit dimensions.
+/// It assumes that the content has only single line.
 #[derive(Debug, Clone)]
-pub struct CompactTable<I, const ROWS: usize, const COLS: usize> {
+pub struct CompactTable<I, D> {
     records: I,
     cfg: CompactConfig,
-    table: TableConfig<ROWS, COLS>,
-}
-
-#[derive(Debug, Clone)]
-struct TableConfig<const ROWS: usize, const COLS: usize> {
-    width: ConstSize<COLS>,
-    height: ConstSize<ROWS>,
+    dims: D,
     count_columns: usize,
     count_rows: Option<usize>,
 }
 
-impl<I> CompactTable<I, 0, 0> {
+impl<I> CompactTable<I, ConstantDimension<0, 0>> {
     /// Creates a new [`CompactTable`] structure with a width dimension for all columns.
     pub const fn new(iter: I) -> Self
     where
@@ -118,81 +109,26 @@ impl<I> CompactTable<I, 0, 0> {
         Self {
             records: iter,
             cfg: create_config(),
-            table: TableConfig {
-                count_columns: 0,
-                count_rows: None,
-                width: ConstSize::Value(0),
-                height: ConstSize::Value(1),
-            },
+            count_columns: 0,
+            count_rows: None,
+            dims: ConstantDimension::new(ConstSize::Value(2), ConstSize::Value(1)),
         }
     }
 }
 
-impl<I, const ROWS: usize, const COLS: usize> CompactTable<I, ROWS, COLS> {
-    /// Creates a new [`CompactTable`] structure with a width dimension for all columns.
-    pub const fn with_dimension(iter: I, width: ConstSize<COLS>, height: ConstSize<ROWS>) -> Self
-    where
-        I: IntoRecords,
-    {
-        let count_rows = if ROWS == 0 { None } else { Some(ROWS) };
-
-        Self {
-            records: iter,
-            cfg: create_config(),
-            table: TableConfig {
-                width,
-                height,
-                count_columns: COLS,
-                count_rows,
-            },
-        }
-    }
-
-    /// With is a generic function which applies options to the [`CompactTable`].
-    pub fn with<O>(mut self, option: O) -> Self
-    where
-        for<'a> O: TableOption<IterRecords<&'a I>, ConstantDimension<ROWS, COLS>, CompactConfig>,
-    {
-        let mut records = IterRecords::new(
-            &self.records,
-            self.table.count_columns,
-            self.table.count_rows,
-        );
-
-        let mut dims = ConstantDimension::new(self.table.width, self.table.height);
-
-        let mut option = option;
-        option.change(&mut records, &mut self.cfg, &mut dims);
-
-        self
-    }
-
-    /// Limit a number of rows.
-    pub fn rows(mut self, count_rows: usize) -> Self {
-        self.table.count_rows = Some(count_rows);
-        self
-    }
-
-    /// Limit a number of columns.
-    pub const fn columns(mut self, count: usize) -> Self {
-        self.table.count_columns = count;
-        self
-    }
-
+impl<I, const ROWS: usize, const COLS: usize> CompactTable<I, ConstantDimension<COLS, ROWS>> {
     /// Set a height for each row.
     pub fn height<S: Into<ConstSize<COUNT_ROWS>>, const COUNT_ROWS: usize>(
         self,
         size: S,
-    ) -> CompactTable<I, COUNT_ROWS, COLS> {
+    ) -> CompactTable<I, ConstantDimension<COLS, COUNT_ROWS>> {
+        let (width, _) = self.dims.into();
         CompactTable {
+            dims: ConstantDimension::new(width, size.into()),
             records: self.records,
             cfg: self.cfg,
-            table: TableConfig {
-                width: self.table.width,
-                height: size.into(),
-                count_columns: self.table.count_columns,
-                count_rows: self.table.count_rows,
-            },
+            count_columns: self.count_columns,
+            count_rows: self.count_rows,
         }
     }
 
@@ -200,132 +136,143 @@ impl<I, const ROWS: usize, const COLS: usize> CompactTable<I, ROWS, COLS> {
     pub fn width<S: Into<ConstSize<COUNT_COLUMNS>>, const COUNT_COLUMNS: usize>(
         self,
         size: S,
-    ) -> CompactTable<I, ROWS, COUNT_COLUMNS> {
+    ) -> CompactTable<I, ConstantDimension<COUNT_COLUMNS, ROWS>> {
+        let (_, height) = self.dims.into();
         CompactTable {
+            dims: ConstantDimension::new(size.into(), height),
             records: self.records,
             cfg: self.cfg,
-            table: TableConfig {
-                width: size.into(),
-                height: self.table.height,
-                count_columns: self.table.count_columns,
-                count_rows: self.table.count_rows,
-            },
+            count_columns: self.count_columns,
+            count_rows: self.count_rows,
         }
     }
+}
 
-    /// Format table into [fmt::Write]er.
-    pub fn fmt<W: fmt::Write>(self, writer: W) -> fmt::Result
+impl<I, D> CompactTable<I, D> {
+    /// Creates a new [`CompactTable`] structure with a width dimension for all columns.
+    pub const fn with_dimension(iter: I, dimension: D) -> Self
     where
         I: IntoRecords,
+        D: Dimension,
     {
-        build_grid(writer, self.records, self.cfg, &self.table)
+        Self {
+            records: iter,
+            dims: dimension,
+            cfg: create_config(),
+            count_columns: 0,
+            count_rows: None,
+        }
     }
 
-    /// Build a string.
-    ///
-    /// We can't implement [`std::string::ToString`] cause it does takes `&self` reference.
-    #[allow(clippy::inherent_to_string)]
-    #[cfg(feature = "std")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
-    pub fn to_string(self) -> String
+    /// With is a generic function which applies options to the [`CompactTable`].
+    pub fn with<O>(mut self, option: O) -> Self
     where
-        I: IntoRecords,
+        for<'a> O: TableOption<IterRecords<&'a I>, D, CompactConfig>,
     {
-        let mut buf = String::new();
-        self.fmt(&mut buf).expect("safe");
+        let mut records = IterRecords::new(&self.records, self.count_columns, self.count_rows);
+
+        let mut option = option;
+        option.change(&mut records, &mut self.cfg, &mut self.dims);
+
+        self
+    }
+
+    /// Limit a number of rows.
+    pub const fn rows(mut self, count_rows: usize) -> Self {
+        self.count_rows = Some(count_rows);
+        self
+    }
 
-        buf
+    /// Limit a number of columns.
+    pub const fn columns(mut self, count: usize) -> Self {
+        self.count_columns = count;
+        self
     }
 
-    /// Format table into [`std::io::Write`]r.
-    #[cfg(feature = "std")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
-    pub fn build<W: std::io::Write>(self, writer: W) -> std::io::Result<()>
+    /// Format table into [fmt::Write]er.
+    pub fn build<W: fmt::Write>(self, writer: W) -> fmt::Result
     where
         I: IntoRecords,
+        D: Dimension,
     {
-        let writer = utf8_writer::UTF8Writer::new(writer);
-        self.fmt(writer)
-            .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))
+        build_grid(
+            writer,
+            self.records,
+            self.dims,
+            self.cfg,
+            self.count_columns,
+            self.count_rows,
+        )
     }
 }
 
-impl<I, const ROWS: usize, const COLS: usize> core::fmt::Display for CompactTable<I, ROWS, COLS>
+impl<I, D> core::fmt::Display for CompactTable<I, D>
 where
     for<'a> &'a I: IntoRecords,
+    D: Dimension + Estimate<CompactConfig> + Clone,
 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result {
-        build_grid(f, &self.records, self.cfg, &self.table)
+        let mut dims = self.dims.clone();
+        dims.estimate(
+            IterRecords::new(&self.records, self.count_columns, self.count_rows),
+            &self.cfg,
+        );
+
+        build_grid(
+            f,
+            &self.records,
+            dims,
+            self.cfg,
+            self.count_columns,
+            self.count_rows,
+        )
     }
 }
 
 impl<T, const ROWS: usize, const COLS: usize> From<[[T; COLS]; ROWS]>
-    for CompactTable<[[T; COLS]; ROWS], ROWS, COLS>
+    for CompactTable<[[T; COLS]; ROWS], ConstantDimension<COLS, ROWS>>
 where
     T: AsRef<str>,
 {
     fn from(mat: [[T; COLS]; ROWS]) -> Self {
         let mut width = [0; COLS];
-        let mut height = [0; ROWS];
-        for (i, row) in mat.iter().enumerate() {
+        for row in mat.iter() {
             for (col, text) in row.iter().enumerate() {
                 let text = text.as_ref();
                 let text_width = string_width_tab(text, 4);
-                let text_lines = count_lines(text);
                 width[col] = max(width[col], text_width);
-                height[i] = max(height[i], text_lines)
             }
         }
 
-        Self::with_dimension(mat, ConstSize::List(width), ConstSize::List(height))
+        // add padding
+        for w in &mut width {
+            *w += 2;
+        }
+
+        let dims = ConstantDimension::new(ConstSize::List(width), ConstSize::Value(1));
+        Self::with_dimension(mat, dims).columns(COLS).rows(ROWS)
     }
 }
 
-fn build_grid<W: fmt::Write, I: IntoRecords, const ROWS: usize, const COLS: usize>(
+fn build_grid<W: fmt::Write, I: IntoRecords, D: Dimension>(
     writer: W,
     records: I,
+    dims: D,
     config: CompactConfig,
-    iter_cfg: &TableConfig<ROWS, COLS>,
+    cols: usize,
+    rows: Option<usize>,
 ) -> Result<(), fmt::Error> {
-    let pad = config.get_padding();
-    let hpad = pad.left.size + pad.right.size;
-    let vpad = pad.top.size + pad.bottom.size;
-
-    let mut width = iter_cfg.width;
-    match &mut width {
-        ConstSize::List(list) => {
-            for w in list.iter_mut() {
-                *w += hpad;
-            }
-        }
-        ConstSize::Value(w) => *w += hpad,
-    }
-
-    let mut height = iter_cfg.height;
-    match &mut height {
-        ConstSize::List(list) => {
-            for w in list.iter_mut() {
-                *w += vpad;
-            }
-        }
-        ConstSize::Value(w) => *w += vpad,
-    }
-
-    let dimension = dimension::ConstantDimension::new(width, iter_cfg.height);
-
-    let count_rows = iter_cfg.count_rows;
-    let count_columns = iter_cfg.count_columns;
-    match count_rows {
+    match rows {
         Some(limit) => {
             let records = LimitRows::new(records, limit);
-            let records = LimitColumns::new(records, count_columns);
-            let records = IterRecords::new(records, count_columns, count_rows);
-            CompactGrid::new(records, &dimension, config).build(writer)
+            let records = LimitColumns::new(records, cols);
+            let records = IterRecords::new(records, cols, rows);
+            CompactGrid::new(records, dims, config).build(writer)
         }
         None => {
-            let records = LimitColumns::new(records, count_columns);
-            let records = IterRecords::new(records, count_columns, count_rows);
-            CompactGrid::new(records, &dimension, config).build(writer)
+            let records = LimitColumns::new(records, cols);
+            let records = IterRecords::new(records, cols, rows);
+            CompactGrid::new(records, dims, config).build(writer)
         }
     }
 }
diff --git a/tabled/src/tables/compact/utf8_writer.rs b/tabled/src/tables/compact/utf8_writer.rs
deleted file mode 100644
index e19a309..0000000
--- a/tabled/src/tables/compact/utf8_writer.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-use std::fmt::Result;
-use std::fmt::{self, Error};
-use std::io;
-
-pub(super) struct UTF8Writer<W>(W);
-
-impl<W> UTF8Writer<W> {
-    pub(crate) fn new(writer: W) -> Self {
-        Self(writer)
-    }
-}
-
-impl<W> fmt::Write for UTF8Writer<W>
-where
-    W: io::Write,
-{
-    fn write_str(&mut self, s: &str) -> Result {
-        let mut buf = s.as_bytes();
-        loop {
-            let n = self.0.write(buf).map_err(|_| Error::default())?;
-            if n == buf.len() {
-                break;
-            }
-
-            buf = &buf[n..];
-        }
-
-        Ok(())
-    }
-}
diff --git a/tabled/src/tables/iter/dimension.rs b/tabled/src/tables/iter/dimension.rs
index 585a4a7..5ea1f6e 100644
--- a/tabled/src/tables/iter/dimension.rs
+++ b/tabled/src/tables/iter/dimension.rs
@@ -4,36 +4,60 @@ use crate::{
         dimension::{Dimension, Estimate},
         spanned::GridConfig,
     },
-    records::into_records::truncate_records::Width,
     records::Records,
 };
 
-#[derive(Debug)]
-pub struct IterTableDimension<'a> {
-    width: Width<'a>,
-    height: usize,
+#[derive(Debug, Clone)]
+pub struct IterTableDimension {
+    width: ExactList,
+    height: ExactList,
 }
 
-impl<'a> IterTableDimension<'a> {
-    pub fn new(width: Width<'a>, height: usize) -> Self {
+impl IterTableDimension {
+    pub fn new(width: ExactList, height: ExactList) -> Self {
         Self { width, height }
     }
 }
 
-impl Dimension for IterTableDimension<'_> {
+impl From<IterTableDimension> for (ExactList, ExactList) {
+    fn from(value: IterTableDimension) -> Self {
+        (value.width, value.height)
+    }
+}
+
+impl Dimension for IterTableDimension {
     fn get_width(&self, column: usize) -> usize {
         self.width.get(column)
     }
 
-    fn get_height(&self, _: usize) -> usize {
-        self.height
+    fn get_height(&self, row: usize) -> usize {
+        self.height.get(row)
     }
 }
 
-impl Estimate<CompactConfig> for IterTableDimension<'_> {
+impl Estimate<CompactConfig> for IterTableDimension {
     fn estimate<R: Records>(&mut self, _: R, _: &CompactConfig) {}
 }
 
-impl Estimate<GridConfig> for IterTableDimension<'_> {
+impl Estimate<GridConfig> for IterTableDimension {
     fn estimate<R: Records>(&mut self, _: R, _: &GridConfig) {}
 }
+
+/// A dimension value.
+#[derive(Debug, Clone)]
+pub enum ExactList {
+    /// Const width value.
+    Exact(usize),
+    /// A list of width values for columns.
+    List(Vec<usize>),
+}
+
+impl ExactList {
+    /// Get a width by column.
+    pub fn get(&self, col: usize) -> usize {
+        match self {
+            ExactList::Exact(val) => *val,
+            ExactList::List(cols) => cols[col],
+        }
+    }
+}
diff --git a/tabled/src/tables/iter/mod.rs b/tabled/src/tables/iter/mod.rs
index 2486623..e09c7d9 100644
--- a/tabled/src/tables/iter/mod.rs
+++ b/tabled/src/tables/iter/mod.rs
@@ -29,26 +29,27 @@
 mod dimension;
 mod utf8_writer;
 
-use std::{borrow::Cow, cmp, fmt, io};
+use std::{cmp, fmt, io};
+
+use papergrid::dimension::{Dimension, Estimate};
+
+use crate::grid::compact::{CompactConfig, ExactDimension};
+use crate::grid::spanned::{Grid, GridConfig};
 
 use crate::{
     grid::config::AlignmentHorizontal,
-    grid::{
-        config::{Entity, Indent},
-        spanned::{
-            config::{Formatting, GridConfig, Padding},
-            ExactDimension, Grid,
-        },
-    },
+    grid::{config::Indent, spanned::config::Padding},
     records::{
         into_records::{
-            truncate_records::Width, BufColumns, BufRows, LimitColumns, LimitRows, TruncateContent,
+            truncate_records::ExactValue, BufColumns, BufRows, LimitColumns, LimitRows,
+            TruncateContent,
         },
         IntoRecords, IterRecords,
     },
     settings::{style::Style, TableOption},
 };
 
+use self::dimension::ExactList;
 use self::{dimension::IterTableDimension, utf8_writer::UTF8Writer};
 
 /// A table which consumes an [`IntoRecords`] iterator.
@@ -59,15 +60,14 @@ use self::{dimension::IterTableDimension, utf8_writer::UTF8Writer};
 #[derive(Debug, Clone)]
 pub struct IterTable<I> {
     records: I,
-    cfg: GridConfig,
-    table: TableConfig,
+    cfg: CompactConfig,
+    dim: IterTableDimension,
+    table: Settings,
 }
 
 #[derive(Debug, Clone)]
-struct TableConfig {
+struct Settings {
     sniff: usize,
-    height: usize,
-    width: Option<usize>,
     count_columns: Option<usize>,
     count_rows: Option<usize>,
 }
@@ -81,10 +81,9 @@ impl<I> IterTable<I> {
         Self {
             records: iter,
             cfg: create_config(),
-            table: TableConfig {
+            dim: IterTableDimension::new(ExactList::Exact(0), ExactList::Exact(1)),
+            table: Settings {
                 sniff: 1000,
-                height: 1,
-                width: None,
                 count_columns: None,
                 count_rows: None,
             },
@@ -92,29 +91,19 @@ impl<I> IterTable<I> {
     }
 
     /// With is a generic function which applies options to the [`IterTable`].
-    pub fn with<O>(mut self, option: O) -> Self
+    pub fn with<O>(mut self, mut option: O) -> Self
     where
-        for<'a> O: TableOption<IterRecords<&'a I>, IterTableDimension<'static>, GridConfig>,
+        for<'a> O: TableOption<IterRecords<&'a I>, IterTableDimension, CompactConfig>,
     {
-        let mut dimension = IterTableDimension::new(
-            Width::Exact(self.table.width.unwrap_or(0)),
-            self.table.height,
-        );
-
-        let mut records = IterRecords::new(
-            &self.records,
-            self.table.count_columns.unwrap_or(0),
-            self.table.count_rows,
-        );
-
-        let mut option = option;
-        option.change(&mut records, &mut self.cfg, &mut dimension);
+        let count_columns = self.table.count_columns.unwrap_or(0);
+        let mut records = IterRecords::new(&self.records, count_columns, self.table.count_rows);
+        option.change(&mut records, &mut self.cfg, &mut self.dim);
 
         self
     }
 
     /// Limit a number of columns.
-    pub fn cols(mut self, count_columns: usize) -> Self {
+    pub fn columns(mut self, count_columns: usize) -> Self {
         self.table.count_columns = Some(count_columns);
         self
     }
@@ -133,13 +122,19 @@ impl<I> IterTable<I> {
 
     /// Set a height for each row.
     pub fn height(mut self, size: usize) -> Self {
-        self.table.height = size;
+        let pad = self.cfg.get_padding();
+        let pad = pad.top.size + pad.bottom.size;
+        let (w, _) = self.dim.into();
+        self.dim = IterTableDimension::new(w, ExactList::Exact(size + pad));
         self
     }
 
     /// Set a width for each column.
     pub fn width(mut self, size: usize) -> Self {
-        self.table.width = Some(size);
+        let pad = self.cfg.get_padding();
+        let pad = pad.left.size + pad.right.size;
+        let (_, h) = self.dim.into();
+        self.dim = IterTableDimension::new(ExactList::Exact(size + pad), h);
         self
     }
 
@@ -148,10 +143,12 @@ impl<I> IterTable<I> {
     where
         I: IntoRecords,
     {
-        let mut config = self.cfg;
-        clean_config(&mut config);
+        let (width, height) = self.dim.into();
+        let width = exact_list_to_exact_value(width);
+        let height = exact_list_to_exact_value(height);
+        let dims = Dims::new(width, height);
 
-        build_grid(writer, self.records, config, &self.table)
+        build_grid(writer, self.records, self.cfg, &self.table, dims)
     }
 
     /// Build a string.
@@ -184,47 +181,60 @@ where
     for<'a> &'a I: IntoRecords,
 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let mut config = self.cfg.clone();
-        clean_config(&mut config);
+        if let Some(cols) = self.table.count_columns {
+            let records = IterRecords::new(&self.records, cols, self.table.count_rows);
+            let mut dims = ExactDimension::default();
+            dims.estimate(records, &self.cfg);
+
+            let records = IterRecords::new(&self.records, cols, self.table.count_rows);
 
-        build_grid(f, &self.records, config, &self.table)
+            let cfg = GridConfig::from(self.cfg);
+            return Grid::new(records, dims, cfg).build(f);
+        }
+
+        let dims = dims_from_exact_dims(self.dim.clone());
+        build_grid(f, &self.records, self.cfg, &self.table, dims)
     }
 }
 
 fn build_grid<W: fmt::Write, I: IntoRecords>(
     writer: W,
     records: I,
-    config: GridConfig,
-    iter_cfg: &TableConfig,
+    config: CompactConfig,
+    iter_cfg: &Settings,
+    dims: Dims<'_>,
 ) -> Result<(), fmt::Error> {
     let tab_size = config.get_tab_width();
     let count_rows = iter_cfg.count_rows;
 
-    let padding = config.get_padding(Entity::Global);
+    let padding = config.get_padding();
     let padding = padding.left.size + padding.right.size;
 
-    let dont_sniff = iter_cfg.width.is_some() && iter_cfg.count_columns.is_some();
+    let dont_sniff =
+        !matches!(dims.width, ExactValue::Exact(0)) && iter_cfg.count_columns.is_some();
     if dont_sniff {
-        let width = iter_cfg.width.unwrap();
         let count_columns = iter_cfg.count_columns.unwrap();
 
-        let dims_width = Width::Exact(cmp::max(width, padding));
-        let dimension = IterTableDimension::new(dims_width, iter_cfg.height);
-
-        let width = width.saturating_sub(padding);
-        let content_width = Width::Exact(width);
+        let content_width = match &dims.width {
+            ExactValue::Exact(w) => ExactValue::Exact(w.saturating_sub(padding)),
+            ExactValue::List(list) => {
+                ExactValue::List(list.iter().map(|w| w.saturating_sub(padding)).collect())
+            }
+        };
 
         match count_rows {
             Some(limit) => {
                 let records = LimitRows::new(records, limit);
                 let records =
                     build_records(records, content_width, count_columns, count_rows, tab_size);
-                return Grid::new(records, &dimension, config).build(writer);
+                let cfg = GridConfig::from(config);
+                return Grid::new(records, dims, cfg).build(writer);
             }
             None => {
                 let records =
                     build_records(records, content_width, count_columns, count_rows, tab_size);
-                return Grid::new(records, &dimension, config).build(writer);
+                let cfg = GridConfig::from(config);
+                return Grid::new(records, dims, cfg).build(writer);
             }
         }
     }
@@ -242,77 +252,64 @@ fn build_grid<W: fmt::Write, I: IntoRecords>(
             .unwrap_or(0),
     };
 
-    #[allow(unused_assignments)]
-    let mut content_width = Vec::new();
-    #[allow(unused_assignments)]
-    let mut dims_width = Vec::new();
-
-    let (contentw, dimsw) = match iter_cfg.width {
-        Some(width) => {
-            let contentwidth = width.saturating_sub(padding);
-            let dims_width = cmp::max(width, padding);
-
-            (Width::Exact(contentwidth), Width::Exact(dims_width))
-        }
-        None => {
+    let (contentw, dimsw) = match dims.width {
+        ExactValue::Exact(0) => {
             let records = LimitColumns::new(records.as_slice(), count_columns);
             let records = IterRecords::new(records, count_columns, None);
             let width = ExactDimension::width(records, &config);
 
-            dims_width = width.iter().map(|i| cmp::max(*i, padding)).collect();
-            content_width = width.iter().map(|i| i.saturating_sub(padding)).collect();
+            let dims_width = width.iter().map(|i| cmp::max(*i, padding)).collect();
+            let content_width = width.iter().map(|i| i.saturating_sub(padding)).collect();
 
             (
-                Width::List(Cow::Borrowed(&content_width)),
-                Width::List(Cow::Borrowed(&dims_width)),
+                ExactValue::List(content_width),
+                ExactValue::List(dims_width),
             )
         }
+        width => {
+            let content_width = match &width {
+                ExactValue::Exact(w) => ExactValue::Exact(w.saturating_sub(padding)),
+                ExactValue::List(list) => {
+                    ExactValue::List(list.iter().map(|w| w.saturating_sub(padding)).collect())
+                }
+            };
+
+            (content_width, width)
+        }
     };
 
-    let dimension = IterTableDimension::new(dimsw, iter_cfg.height);
-
+    let dimension = Dims::new(dimsw, dims.height);
     match count_rows {
         Some(limit) => {
             let records = LimitRows::new(records, limit);
             let records = build_records(records, contentw, count_columns, count_rows, tab_size);
-            Grid::new(records, &dimension, config).build(writer)
+            let cfg = GridConfig::from(config);
+            Grid::new(records, dimension, cfg).build(writer)
         }
         None => {
             let records = build_records(records, contentw, count_columns, count_rows, tab_size);
-            Grid::new(records, &dimension, config).build(writer)
+            let cfg = GridConfig::from(config);
+            Grid::new(records, dimension, cfg).build(writer)
         }
     }
 }
 
-fn create_config() -> GridConfig {
-    let mut cfg = GridConfig::default();
-    cfg.set_tab_width(4);
-    cfg.set_padding(
-        Entity::Global,
-        Padding::new(
+fn create_config() -> CompactConfig {
+    CompactConfig::default()
+        .set_tab_width(4)
+        .set_padding(Padding::new(
             Indent::spaced(1),
             Indent::spaced(1),
             Indent::default(),
             Indent::default(),
-        ),
-    );
-    cfg.set_alignment_horizontal(Entity::Global, AlignmentHorizontal::Left);
-    cfg.set_formatting(Entity::Global, Formatting::new(false, false, false));
-    cfg.set_borders(*Style::ascii().get_borders());
-
-    cfg
-}
-
-fn clean_config(cfg: &mut GridConfig) {
-    cfg.clear_span_column();
-    cfg.clear_span_row();
-
-    // todo: leave only global options...
+        ))
+        .set_alignment_horizontal(AlignmentHorizontal::Left)
+        .set_borders(*Style::ascii().get_borders())
 }
 
 fn build_records<I: IntoRecords>(
     records: I,
-    width: Width<'_>,
+    width: ExactValue<'_>,
     count_columns: usize,
     count_rows: Option<usize>,
     tab_size: usize,
@@ -321,3 +318,40 @@ fn build_records<I: IntoRecords>(
     let records = LimitColumns::new(records, count_columns);
     IterRecords::new(records, count_columns, count_rows)
 }
+
+#[derive(Debug, Clone)]
+struct Dims<'a> {
+    width: ExactValue<'a>,
+    height: ExactValue<'a>,
+}
+
+impl<'a> Dims<'a> {
+    fn new(width: ExactValue<'a>, height: ExactValue<'a>) -> Self {
+        Self { width, height }
+    }
+}
+
+impl Dimension for Dims<'_> {
+    fn get_width(&self, column: usize) -> usize {
+        self.width.get(column)
+    }
+
+    fn get_height(&self, row: usize) -> usize {
+        self.height.get(row)
+    }
+}
+
+fn dims_from_exact_dims(d: IterTableDimension) -> Dims<'static> {
+    let (width, height) = d.into();
+    let width = exact_list_to_exact_value(width);
+    let height = exact_list_to_exact_value(height);
+    let dims = Dims::new(width, height);
+    dims
+}
+
+fn exact_list_to_exact_value(width: ExactList) -> ExactValue<'static> {
+    match width {
+        ExactList::Exact(w) => ExactValue::Exact(w),
+        ExactList::List(list) => ExactValue::List(list.into()),
+    }
+}
diff --git a/tabled/src/tables/table/mod.rs b/tabled/src/tables/table/mod.rs
index a0f1d44..052ecad 100644
--- a/tabled/src/tables/table/mod.rs
+++ b/tabled/src/tables/table/mod.rs
@@ -257,12 +257,13 @@ impl fmt::Display for Table {
     }
 }
 
-impl<T> FromIterator<T> for Table
+impl<T, V> FromIterator<T> for Table
 where
-    T: Tabled,
+    T: IntoIterator<Item = V>,
+    V: Into<String>,
 {
     fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
-        Self::new(iter)
+        Builder::from_iter(iter.into_iter().map(|i| i.into_iter().map(|s| s.into()))).build()
     }
 }
 


Test patch:
diff --git a/csv_to_table/tests/iter.rs b/csv_to_table/tests/iter.rs
index cb43aa5..e2dbf5e 100644
--- a/csv_to_table/tests/iter.rs
+++ b/csv_to_table/tests/iter.rs
@@ -48,7 +48,7 @@ fn test_iter() {
 #[cfg(test)]
 #[test]
 fn test_iter_width() {
-    let table = csv_to_table::iter::from_reader(CSV_DATA_1.trim().as_bytes()).width(4);
+    let table = csv_to_table::iter::from_reader(CSV_DATA_1.trim().as_bytes()).width(2);
 
     let table = table.to_string();
 
@@ -103,8 +103,8 @@ fn test_iter_width_zero() {
 #[test]
 fn test_iter_width_and_cols() {
     let table = csv_to_table::iter::from_reader(CSV_DATA_1.trim().as_bytes())
-        .width(4)
-        .cols(5);
+        .width(2)
+        .columns(5);
 
     let table = table.to_string();
 
@@ -131,7 +131,7 @@ fn test_iter_width_and_cols() {
 #[cfg(test)]
 #[test]
 fn test_iter_cols() {
-    let table = csv_to_table::iter::from_reader(CSV_DATA_1.trim().as_bytes()).cols(5);
+    let table = csv_to_table::iter::from_reader(CSV_DATA_1.trim().as_bytes()).columns(5);
 
     let table = table.to_string();
 
@@ -212,7 +212,7 @@ fn test_iter_rows() {
 fn test_iter_rows_cols() {
     let table = csv_to_table::iter::from_reader(CSV_DATA_1.trim().as_bytes())
         .rows(2)
-        .cols(2);
+        .columns(2);
 
     let table = table.to_string();
 
@@ -233,7 +233,7 @@ fn test_iter_rows_cols() {
 fn test_iter_rows_cols_zero() {
     let table = csv_to_table::iter::from_reader(CSV_DATA_1.trim().as_bytes())
         .rows(0)
-        .cols(0);
+        .columns(0);
 
     let table = table.to_string();
 
@@ -319,7 +319,7 @@ fn test_iter_sniff_zero() {
 fn test_iter_sniff_zero_cols() {
     let table = csv_to_table::iter::from_reader(CSV_DATA_2.trim().as_bytes())
         .sniff(0)
-        .cols(3);
+        .columns(3);
 
     let table = table.to_string();
 
diff --git a/tabled/tests/compact_table.rs b/tabled/tests/compact_table.rs
new file mode 100644
index 0000000..0b8f034
--- /dev/null
+++ b/tabled/tests/compact_table.rs
@@ -0,0 +1,109 @@
+#![cfg(feature = "std")]
+
+mod util;
+
+use tabled::grid::compact::ExactDimension;
+use tabled::tables::compact::CompactTable;
+use util::{create_matrix, test_table};
+
+test_table!(
+    compact_new,
+    CompactTable::new(create_matrix::<3, 3>()).to_string(),
+    ""
+);
+
+test_table!(
+    compact_with_dimension,
+    CompactTable::with_dimension(create_matrix::<3, 3>(), ExactDimension::default()).columns(3).to_string(),
+    "+-----+-----+-----+"
+    "| 0-0 | 0-1 | 0-2 |"
+    "|-----+-----+-----|"
+    "| 1-0 | 1-1 | 1-2 |"
+    "|-----+-----+-----|"
+    "| 2-0 | 2-1 | 2-2 |"
+    "+-----+-----+-----+"
+);
+
+test_table!(
+    compact_width,
+    CompactTable::new(create_matrix::<3, 3>()).columns(3).width(5).to_string(),
+    "+-----+-----+-----+"
+    "| 0-0 | 0-1 | 0-2 |"
+    "|-----+-----+-----|"
+    "| 1-0 | 1-1 | 1-2 |"
+    "|-----+-----+-----|"
+    "| 2-0 | 2-1 | 2-2 |"
+    "+-----+-----+-----+"
+);
+
+test_table!(
+    compact_width_pad_not_included,
+    CompactTable::new(create_matrix::<3, 3>()).columns(3).width(3).to_string(),
+    "+---+---+---+"
+    "| 0-0 | 0-1 | 0-2 |"
+    "|---+---+---|"
+    "| 1-0 | 1-1 | 1-2 |"
+    "|---+---+---|"
+    "| 2-0 | 2-1 | 2-2 |"
+    "+---+---+---+"
+);
+
+test_table!(
+    compact_width_bigger,
+    CompactTable::new(create_matrix::<3, 3>()).columns(3).width(10).to_string(),
+    "+----------+----------+----------+"
+    "| 0-0      | 0-1      | 0-2      |"
+    "|----------+----------+----------|"
+    "| 1-0      | 1-1      | 1-2      |"
+    "|----------+----------+----------|"
+    "| 2-0      | 2-1      | 2-2      |"
+    "+----------+----------+----------+"
+);
+
+test_table!(
+    compact_columns,
+    CompactTable::new(create_matrix::<3, 3>()).columns(3).to_string(),
+    "+--+--+--+"
+    "| 0-0 | 0-1 | 0-2 |"
+    "|--+--+--|"
+    "| 1-0 | 1-1 | 1-2 |"
+    "|--+--+--|"
+    "| 2-0 | 2-1 | 2-2 |"
+    "+--+--+--+"
+);
+
+test_table!(
+    compact_cols_zero,
+    CompactTable::new(create_matrix::<3, 3>())
+        .columns(0)
+        .to_string(),
+    ""
+);
+
+test_table!(
+    compact_cols_less,
+    CompactTable::new(create_matrix::<3, 3>())
+        .columns(1)
+        .to_string(),
+    "+--+"
+    "| 0-0 |"
+    "|--|"
+    "| 1-0 |"
+    "|--|"
+    "| 2-0 |"
+    "+--+"
+);
+
+test_table!(
+    compact_cols_more,
+    CompactTable::new(create_matrix::<3, 3>())
+        .columns(5)
+        .to_string(),
+    "+--+--+--+--+--+"
+    "| 0-0 | 0-1 | 0-2 |"
+    "|--+--+--+--+--|"
+    "| 1-0 | 1-1 | 1-2 |"
+    "|--+--+--+--+--|"
+    "| 2-0 | 2-1 | 2-2 |"
+    "+--+--+--+--+--+"
+);
diff --git a/tabled/tests/iter_table.rs b/tabled/tests/iter_table.rs
new file mode 100644
index 0000000..58134d8
--- /dev/null
+++ b/tabled/tests/iter_table.rs
@@ -0,0 +1,159 @@
+#![cfg(feature = "std")]
+
+mod util;
+
+use tabled::tables::iter::IterTable;
+use util::{create_matrix, test_table};
+
+test_table!(
+    iter_table,
+    IterTable::new(create_matrix::<3, 3>()).to_string(),
+    "+-----+-----+-----+"
+    "| 0-0 | 0-1 | 0-2 |"
+    "+-----+-----+-----+"
+    "| 1-0 | 1-1 | 1-2 |"
+    "+-----+-----+-----+"
+    "| 2-0 | 2-1 | 2-2 |"
+    "+-----+-----+-----+"
+);
+
+test_table!(
+    iter_table_cols,
+    IterTable::new(create_matrix::<3, 3>()).columns(3).to_string(),
+    "+-----+-----+-----+"
+    "| 0-0 | 0-1 | 0-2 |"
+    "+-----+-----+-----+"
+    "| 1-0 | 1-1 | 1-2 |"
+    "+-----+-----+-----+"
+    "| 2-0 | 2-1 | 2-2 |"
+    "+-----+-----+-----+"
+);
+
+test_table!(
+    iter_table_cols_less,
+    IterTable::new(create_matrix::<3, 3>()).columns(2).to_string(),
+    "+-----+-----+"
+    "| 0-0 | 0-1 |"
+    "+-----+-----+"
+    "| 1-0 | 1-1 |"
+    "+-----+-----+"
+    "| 2-0 | 2-1 |"
+    "+-----+-----+"
+);
+
+test_table!(
+    iter_table_cols_zero,
+    IterTable::new(create_matrix::<3, 3>())
+        .columns(0)
+        .to_string(),
+    ""
+);
+
+test_table!(
+    iter_table_iterator,
+    IterTable::new((0..3).map(|i: usize| (0..5).map(move |j: usize| format!("{i},{j}")))).to_string(),
+    "+-----+-----+-----+-----+-----+"
+    "| 0,0 | 0,1 | 0,2 | 0,3 | 0,4 |"
+    "+-----+-----+-----+-----+-----+"
+    "| 1,0 | 1,1 | 1,2 | 1,3 | 1,4 |"
+    "+-----+-----+-----+-----+-----+"
+    "| 2,0 | 2,1 | 2,2 | 2,3 | 2,4 |"
+    "+-----+-----+-----+-----+-----+"
+);
+
+test_table!(
+    iter_table_width,
+    IterTable::new(create_matrix::<3, 3>()).width(2).to_string(),
+    "+----+----+----+"
+    "| 0- | 0- | 0- |"
+    "+----+----+----+"
+    "| 1- | 1- | 1- |"
+    "+----+----+----+"
+    "| 2- | 2- | 2- |"
+    "+----+----+----+"
+);
+
+test_table!(
+    iter_table_height_does_not_work,
+    IterTable::new(create_matrix::<3, 3>()).height(5).to_string(),
+    "+-----+-----+-----+"
+    "| 0-0 | 0-1 | 0-2 |"
+    "|     |     |     |"
+    "|     |     |     |"
+    "|     |     |     |"
+    "|     |     |     |"
+    "+-----+-----+-----+"
+    "| 1-0 | 1-1 | 1-2 |"
+    "|     |     |     |"
+    "|     |     |     |"
+    "|     |     |     |"
+    "|     |     |     |"
+    "+-----+-----+-----+"
+    "| 2-0 | 2-1 | 2-2 |"
+    "|     |     |     |"
+    "|     |     |     |"
+    "|     |     |     |"
+    "|     |     |     |"
+    "+-----+-----+-----+"
+);
+
+test_table!(
+    iter_table_multiline_height_work,
+    IterTable::new(
+        vec![
+            vec!["0", "1", "2", "3"],
+            vec!["0\n1\n2\n3\n4", "0\n1\n2\n\n\n3\n4", "0\n1\n2\n3\n4\n\n\n", "0\n1\n2\n\n\n3\n4\n"]
+        ]
+    )
+    .height(3)
+    .to_string(),
+    "+---+---+---+---+"
+    "| 0 | 1 | 2 | 3 |"
+    "|   |   |   |   |"
+    "|   |   |   |   |"
+    "+---+---+---+---+"
+    "| 0 | 0 | 0 | 0 |"
+    "| 1 | 1 | 1 | 1 |"
+    "| 2 | 2 | 2 | 2 |"
+    "+---+---+---+---+"
+);
+
+test_table!(
+    iter_table_sniff_cut,
+    IterTable::new(
+        vec![
+            vec!["12", "12", "22", "32"],
+            vec!["0", "0", "0", "0"],
+            vec!["023", "123", "223", "323"],
+        ]
+    )
+    .sniff(2)
+    .to_string(),
+    "+----+----+----+----+"
+    "| 12 | 12 | 22 | 32 |"
+    "+----+----+----+----+"
+    "| 0  | 0  | 0  | 0  |"
+    "+----+----+----+----+"
+    "| 02 | 12 | 22 | 32 |"
+    "+----+----+----+----+"
+);
+
+test_table!(
+    iter_table_sniff,
+    IterTable::new(
+        vec![
+            vec!["023", "123", "223", "323"],
+            vec!["12", "12", "22", "32"],
+            vec!["0", "0", "0", "0"],
+        ]
+    )
+    .sniff(2)
+    .to_string(),
+    "+-----+-----+-----+-----+"
+    "| 023 | 123 | 223 | 323 |"
+    "+-----+-----+-----+-----+"
+    "| 12  | 12  | 22  | 32  |"
+    "+-----+-----+-----+-----+"
+    "| 0   | 0   | 0   | 0   |"
+    "+-----+-----+-----+-----+"
+);
diff --git a/tabled/tests/util.rs b/tabled/tests/util.rs
index 7054fe1..fe8621a 100644
--- a/tabled/tests/util.rs
+++ b/tabled/tests/util.rs
@@ -57,6 +57,21 @@ pub fn create_vector<const ROWS: usize, const COLUMNS: usize>() -> Vec<Obj<COLUM
     arr
 }
 
+pub fn create_matrix<const ROWS: usize, const COLUMNS: usize>() -> Vec<Vec<String>> {
+    let mut arr = Vec::with_capacity(ROWS);
+    for row in 0..ROWS {
+        let mut data = Vec::with_capacity(COLUMNS);
+        for column in 0..COLUMNS {
+            let text = format!("{row}-{column}");
+            data.push(text);
+        }
+
+        arr.push(data);
+    }
+
+    arr
+}
+
 #[derive(Debug)]
 pub struct Obj<const N: usize> {
     data: Vec<String>,
@@ -126,3 +141,13 @@ macro_rules! test_table {
 }
 
 pub(crate) use test_table;
+
+macro_rules! assert_table {
+    ($table:expr, $($line:expr)*) => {
+        let table = $table.to_string();
+        println!("{}", table);
+        assert_eq!(table, crate::util::static_table!($($line)*));
+    };
+}
+
+pub(crate) use assert_table;
diff --git a/tabled/tests/width_test.rs b/tabled/tests/width_test.rs
index 91ff506..b4d848e 100644
--- a/tabled/tests/width_test.rs
+++ b/tabled/tests/width_test.rs
@@ -2585,7 +2585,7 @@ mod derived {
                 name: text.to_owned(),
                 is_hyperlink: true,
             }];
-            tabled::Table::from_iter(&data)
+            tabled::Table::new(&data)
                 .with(
                     Modify::new(Segment::all())
                         .with(Width::wrap(5).keep_words())