Repository: zhiburt/tabled (rust)

Issue:
Add a `Shadow` support
Firstly I've seen it on wiki https://en.wikipedia.org/wiki/Box-drawing_character

```
┌───────────────────┐
│  ╔═══╗ Some Text  │▒
│  ╚═╦═╝ in the box │▒
╞═╤══╩══╤═══════════╡▒
│ ├──┬──┤           │▒
│ └──┴──┘           │▒
└───────────────────┘▒
 ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
```

Looks quite something :)

We already have a `Margin` support `Shadow` would have the same logic but only 1 space shift character.

Pull request:
Add Shadow support
close #129 

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 99bd5de..25eab77 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "tabled"
-version = "0.9.0-fix.docs.rs.1"
+version = "0.9.0-fix.docs.rs.4"
 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."
@@ -162,7 +162,7 @@ color = ["papergrid/color", "ansi-str", "vte"]
 macros = []
 
 [dependencies]
-papergrid = "0.6.0"
+papergrid = { path = "papergrid" }
 unicode-width = "0.1.9"
 tabled_derive = { version = "0.5.0", optional = true }
 ansi-str = { version = "0.4.0", optional = true }
@@ -177,7 +177,7 @@ quickcheck_macros = "1.0.0"
 #
 # RUSTDOCFLAGS="--cfg docsrs" cargo +nightly -Zunstable-options doc --no-deps --all-features --open
 [package.metadata.docs.rs]
+all-features = true
 targets = ["x86_64-unknown-linux-gnu"]
-features = ["unstable-doc"]
 rustdoc-args = ["--cfg", "docsrs"]
-cargo-args = ["-Zunstable-options"]
+rustc-args = ["--cfg", "docsrs"]
diff --git a/examples/shadow.rs b/examples/shadow.rs
new file mode 100644
index 0000000..eb510c8
--- /dev/null
+++ b/examples/shadow.rs
@@ -0,0 +1,41 @@
+//! The example can be run by this command
+//! `cargo run --example basic`
+
+use std::iter::FromIterator;
+
+use tabled::{object::Rows, shadow::Shadow, Alignment, ModifyObject, Style, Table, Tabled};
+
+#[derive(Tabled)]
+struct Distribution {
+    name: String,
+    based_on: String,
+    is_active: bool,
+    is_cool: bool,
+}
+
+impl Distribution {
+    fn new(name: &str, based_on: &str, is_active: bool, is_cool: bool) -> Self {
+        Self {
+            name: name.to_string(),
+            based_on: based_on.to_string(),
+            is_active,
+            is_cool,
+        }
+    }
+}
+
+fn main() {
+    let data = [
+        Distribution::new("Manjaro", "Arch", true, true),
+        Distribution::new("Arch", "", true, true),
+        Distribution::new("Debian", "", true, true),
+    ];
+
+    let mut table = Table::from_iter(&data);
+    table
+        .with(Style::ascii())
+        .with(Rows::first().modify().with(Alignment::center()))
+        .with(Shadow::new(2));
+
+    println!("{}", table);
+}
diff --git a/papergrid/src/config/mod.rs b/papergrid/src/config/mod.rs
index 4e5f223..62d9475 100644
--- a/papergrid/src/config/mod.rs
+++ b/papergrid/src/config/mod.rs
@@ -16,13 +16,13 @@ pub use self::{
     entity::{Entity, EntityIterator, Position},
     formatting::Formatting,
     offset::Offset,
-    sides::Indent,
+    sides::{Indent, Sides},
 };
 
 #[cfg(feature = "color")]
 use crate::AnsiColor;
 
-use self::{borders::BordersConfig, entity_map::EntityMap, sides::Sides};
+use self::{borders::BordersConfig, entity_map::EntityMap};
 
 /// This structure represents a settings of a grid.
 ///
@@ -31,6 +31,7 @@ use self::{borders::BordersConfig, entity_map::EntityMap, sides::Sides};
 pub struct GridConfig {
     tab_width: usize,
     margin: Margin,
+    margin_offset: Sides<Offset>,
     padding: EntityMap<Padding>,
     alignment_h: EntityMap<AlignmentHorizontal>,
     alignment_v: EntityMap<AlignmentVertical>,
@@ -52,9 +53,17 @@ pub struct GridConfig {
 
 impl Default for GridConfig {
     fn default() -> Self {
+        let margin_offset = Sides::new(
+            Offset::Begin(0),
+            Offset::Begin(0),
+            Offset::Begin(0),
+            Offset::Begin(0),
+        );
+
         Self {
             tab_width: 4,
             margin: Margin::default(),
+            margin_offset,
             padding: EntityMap::default(),
             formatting: EntityMap::default(),
             alignment_h: EntityMap::new(AlignmentHorizontal::Left),
@@ -145,6 +154,16 @@ impl GridConfig {
         &self.margin
     }
 
+    /// Set [`Margin`] offset.
+    pub fn set_margin_offset(&mut self, margin: Sides<Offset>) {
+        self.margin_offset = margin;
+    }
+
+    /// Returns a [`Margin`] offset.
+    pub fn get_margin_offset(&self) -> &Sides<Offset> {
+        &self.margin_offset
+    }
+
     /// Clears all theme changes.
     /// And sets it to default.
     pub fn clear_theme(&mut self) {
diff --git a/papergrid/src/config/sides.rs b/papergrid/src/config/sides.rs
index eb7fc28..d95b693 100644
--- a/papergrid/src/config/sides.rs
+++ b/papergrid/src/config/sides.rs
@@ -1,12 +1,18 @@
+/// A structure which represents 4 box sides.
 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
 pub struct Sides<T> {
+    /// Top side.
     pub top: T,
+    /// Bottom side.
     pub bottom: T,
+    /// Left side.
     pub left: T,
+    /// Right side.
     pub right: T,
 }
 
 impl<T> Sides<T> {
+    /// Creates a new object.
     pub fn new(left: T, right: T, top: T, bottom: T) -> Self {
         Self {
             top,
diff --git a/papergrid/src/grid.rs b/papergrid/src/grid.rs
index 64242a5..62dc403 100644
--- a/papergrid/src/grid.rs
+++ b/papergrid/src/grid.rs
@@ -97,11 +97,14 @@ mod print_general {
         let total_width_with_margin =
             total_width + cfg.get_margin().left.size + cfg.get_margin().right.size;
 
+        let total_height = total_height(cfg, records, height);
+
         if cfg.get_margin().top.size > 0 {
             print_margin_top(f, cfg, total_width_with_margin)?;
             f.write_char('\n')?;
         }
 
+        let mut table_line = 0;
         let mut prev_empty_horizontal = false;
         for row in 0..records.count_rows() {
             let count_lines = height.get(row).unwrap();
@@ -111,9 +114,9 @@ mod print_general {
                     f.write_char('\n')?;
                 }
 
-                print_margin_left(f, cfg)?;
+                print_margin_left(f, cfg, table_line, total_height)?;
                 print_split_line(f, cfg, records, width, row, total_width)?;
-                print_margin_right(f, cfg)?;
+                print_margin_right(f, cfg, table_line, total_height)?;
 
                 if count_lines > 0 {
                     f.write_char('\n')?;
@@ -121,13 +124,15 @@ mod print_general {
                 } else {
                     prev_empty_horizontal = true;
                 }
+
+                table_line += 1;
             } else if count_lines > 0 && prev_empty_horizontal {
                 f.write_char('\n')?;
                 prev_empty_horizontal = false;
             }
 
             for i in 0..count_lines {
-                print_margin_left(f, cfg)?;
+                print_margin_left(f, cfg, table_line, total_height)?;
 
                 for col in 0..records.count_columns() {
                     print_vertical_char(f, cfg, records, (row, col), i, count_lines)?;
@@ -142,21 +147,23 @@ mod print_general {
                     }
                 }
 
-                print_margin_right(f, cfg)?;
+                print_margin_right(f, cfg, table_line, total_height)?;
 
                 let is_last_line = i + 1 == count_lines;
                 let is_last_row = row + 1 == records.count_rows();
                 if !(is_last_line && is_last_row) {
                     f.write_char('\n')?;
                 }
+
+                table_line += 1;
             }
         }
 
         if has_horizontal(cfg, records, records.count_rows()) {
             f.write_char('\n')?;
-            print_margin_left(f, cfg)?;
+            print_margin_left(f, cfg, table_line, total_height)?;
             print_split_line(f, cfg, records, width, records.count_rows(), total_width)?;
-            print_margin_right(f, cfg)?;
+            print_margin_right(f, cfg, table_line, total_height)?;
         }
 
         if cfg.get_margin().bottom.size > 0 {
@@ -340,11 +347,14 @@ mod print_spanned {
         let total_width_with_margin =
             total_width + cfg.get_margin().left.size + cfg.get_margin().right.size;
 
+        let total_height = total_height(cfg, records, height);
+
         if cfg.get_margin().top.size > 0 {
             print_margin_top(f, cfg, total_width_with_margin)?;
             f.write_char('\n')?;
         }
 
+        let mut table_line = 0;
         let mut prev_empty_horizontal = false;
         for row in 0..records.count_rows() {
             let count_lines = height.get(row).unwrap();
@@ -354,9 +364,9 @@ mod print_spanned {
                     f.write_char('\n')?;
                 }
 
-                print_margin_left(f, cfg)?;
+                print_margin_left(f, cfg, table_line, total_height)?;
                 print_split_line(f, cfg, records, width, height, row, total_width)?;
-                print_margin_right(f, cfg)?;
+                print_margin_right(f, cfg, table_line, total_height)?;
 
                 if count_lines > 0 {
                     f.write_char('\n')?;
@@ -364,13 +374,15 @@ mod print_spanned {
                 } else {
                     prev_empty_horizontal = true;
                 }
+
+                table_line += 1;
             } else if count_lines > 0 && prev_empty_horizontal {
                 f.write_char('\n')?;
                 prev_empty_horizontal = false;
             }
 
             for i in 0..count_lines {
-                print_margin_left(f, cfg)?;
+                print_margin_left(f, cfg, table_line, total_height)?;
 
                 for col in 0..records.count_columns() {
                     if !cfg.is_cell_covered_by_both_spans((row, col), shape) {
@@ -412,22 +424,24 @@ mod print_spanned {
                     }
                 }
 
-                print_margin_right(f, cfg)?;
+                print_margin_right(f, cfg, table_line, total_height)?;
 
                 let is_last_line = i + 1 == count_lines;
                 let is_last_row = row + 1 == records.count_rows();
                 if !(is_last_line && is_last_row) {
                     f.write_char('\n')?;
                 }
+
+                table_line += 1;
             }
         }
 
         if has_horizontal(cfg, records, records.count_rows()) {
             f.write_char('\n')?;
-            print_margin_left(f, cfg)?;
+            print_margin_left(f, cfg, table_line, total_height)?;
             let row = records.count_rows();
             print_split_line(f, cfg, records, width, height, row, total_width)?;
-            print_margin_right(f, cfg)?;
+            print_margin_right(f, cfg, table_line, total_height)?;
         }
 
         if cfg.get_margin().bottom.size > 0 {
@@ -965,6 +979,17 @@ where
     content_width + count_borders
 }
 
+fn total_height<R, H>(cfg: &GridConfig, records: &R, height: &H) -> usize
+where
+    H: Estimate<R>,
+    R: Records,
+{
+    let content_width = height.total();
+    let count_borders = cfg.count_horizontal(records.count_rows());
+
+    content_width + count_borders
+}
+
 fn print_vertical_char<R>(
     f: &mut fmt::Formatter<'_>,
     cfg: &GridConfig,
@@ -1007,6 +1032,7 @@ fn print_margin_top(f: &mut fmt::Formatter<'_>, cfg: &GridConfig, width: usize)
     print_indent_lines(
         f,
         &cfg.get_margin().top,
+        &cfg.get_margin_offset().top,
         width,
         #[cfg(feature = "color")]
         &cfg.get_margin_color().top,
@@ -1017,46 +1043,137 @@ fn print_margin_bottom(f: &mut fmt::Formatter<'_>, cfg: &GridConfig, width: usiz
     print_indent_lines(
         f,
         &cfg.get_margin().bottom,
+        &cfg.get_margin_offset().bottom,
         width,
         #[cfg(feature = "color")]
         &cfg.get_margin_color().bottom,
     )
 }
 
-fn print_margin_left(f: &mut fmt::Formatter<'_>, cfg: &GridConfig) -> fmt::Result {
-    print_indent(
+fn print_margin_left(
+    f: &mut fmt::Formatter<'_>,
+    cfg: &GridConfig,
+    line: usize,
+    count_lines: usize,
+) -> fmt::Result {
+    print_margin_vertical(
         f,
-        cfg.get_margin().left.fill,
-        cfg.get_margin().left.size,
+        cfg.get_margin().left,
+        cfg.get_margin_offset().left,
+        line,
+        count_lines,
         #[cfg(feature = "color")]
         &cfg.get_margin_color().left,
     )
 }
 
-fn print_margin_right(f: &mut fmt::Formatter<'_>, cfg: &GridConfig) -> fmt::Result {
-    print_indent(
+fn print_margin_right(
+    f: &mut fmt::Formatter<'_>,
+    cfg: &GridConfig,
+    line: usize,
+    count_lines: usize,
+) -> fmt::Result {
+    print_margin_vertical(
         f,
-        cfg.get_margin().right.fill,
-        cfg.get_margin().right.size,
+        cfg.get_margin().right,
+        cfg.get_margin_offset().right,
+        line,
+        count_lines,
         #[cfg(feature = "color")]
         &cfg.get_margin_color().right,
     )
 }
 
-fn print_indent_lines(
+fn print_margin_vertical(
     f: &mut fmt::Formatter<'_>,
-    indent: &Indent,
-    width: usize,
+    indent: Indent,
+    offset: Offset,
+    line: usize,
+    count_lines: usize,
     #[cfg(feature = "color")] color: &AnsiColor,
 ) -> fmt::Result {
-    for i in 0..indent.size {
+    if indent.size == 0 {
+        return Ok(());
+    }
+
+    let (start_offset, end_offset) = match offset {
+        Offset::Begin(start) => (start, 0),
+        Offset::End(end) => (0, end),
+    };
+
+    let start_offset = std::cmp::min(start_offset, count_lines);
+    let end_offset = std::cmp::min(end_offset, count_lines);
+    let end_pos = count_lines - end_offset;
+
+    if line >= start_offset && line < end_pos {
         print_indent(
             f,
             indent.fill,
-            width,
+            indent.size,
             #[cfg(feature = "color")]
             color,
-        )?;
+        )
+    } else {
+        print_indent(
+            f,
+            ' ',
+            indent.size,
+            #[cfg(feature = "color")]
+            &AnsiColor::default(),
+        )
+    }
+}
+
+fn print_indent_lines(
+    f: &mut fmt::Formatter<'_>,
+    indent: &Indent,
+    offset: &Offset,
+    width: usize,
+    #[cfg(feature = "color")] color: &AnsiColor,
+) -> fmt::Result {
+    if indent.size == 0 {
+        return Ok(());
+    }
+
+    let (start_offset, end_offset) = match offset {
+        Offset::Begin(start) => (*start, 0),
+        Offset::End(end) => (0, *end),
+    };
+
+    let start_offset = std::cmp::min(start_offset, width);
+    let end_offset = std::cmp::min(end_offset, width);
+    let indent_size = width - start_offset - end_offset;
+
+    for i in 0..indent.size {
+        if start_offset > 0 {
+            print_indent(
+                f,
+                ' ',
+                start_offset,
+                #[cfg(feature = "color")]
+                &AnsiColor::default(),
+            )?;
+        }
+
+        if indent_size > 0 {
+            print_indent(
+                f,
+                indent.fill,
+                indent_size,
+                #[cfg(feature = "color")]
+                color,
+            )?;
+        }
+
+        if end_offset > 0 {
+            print_indent(
+                f,
+                ' ',
+                end_offset,
+                #[cfg(feature = "color")]
+                &AnsiColor::default(),
+            )?;
+        }
 
         if i + 1 != indent.size {
             f.write_char('\n')?;
diff --git a/papergrid/src/lib.rs b/papergrid/src/lib.rs
index 01c168a..776a885 100644
--- a/papergrid/src/lib.rs
+++ b/papergrid/src/lib.rs
@@ -84,7 +84,7 @@ pub mod util;
 pub use self::{
     config::{
         AlignmentHorizontal, AlignmentVertical, Border, Borders, Entity, EntityIterator,
-        Formatting, GridConfig, HorizontalLine, Indent, Margin, Offset, Padding, Position,
+        Formatting, GridConfig, HorizontalLine, Indent, Margin, Offset, Padding, Position, Sides,
         VerticalLine,
     },
     estimation::{height, width, Estimate},
diff --git a/src/features/mod.rs b/src/features/mod.rs
index 21b589d..1d61118 100644
--- a/src/features/mod.rs
+++ b/src/features/mod.rs
@@ -7,6 +7,7 @@ pub mod highlight;
 pub mod locator;
 pub mod measurment;
 pub mod peaker;
+pub mod shadow;
 pub mod style;
 pub mod width;
 
diff --git a/src/features/shadow.rs b/src/features/shadow.rs
new file mode 100644
index 0000000..0b992d8
--- /dev/null
+++ b/src/features/shadow.rs
@@ -0,0 +1,194 @@
+//! This module contains a [`Shadow`] option for a [`Table`].
+//!
+//! # Example
+//!
+//! ```
+//! use tabled::{Style, TableIteratorExt, shadow::Shadow};
+//!
+//! let data = vec!["Hello", "World", "!"];
+//!
+//! let table = data.table()
+//!     .with(Style::markdown())
+//!     .with(Shadow::new(1))
+//!     .to_string();
+//!
+//! assert_eq!(
+//!     table,
+//!     concat!(
+//!         "| &str  | \n",
+//!         "|-------|▒\n",
+//!         "| Hello |▒\n",
+//!         "| World |▒\n",
+//!         "| !     |▒\n",
+//!         " ▒▒▒▒▒▒▒▒▒",
+//!     )
+//! );
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use crate::{
+    papergrid::{Offset, Sides},
+    Table, TableOption,
+};
+
+#[cfg(feature = "color")]
+use crate::color::Color;
+
+/// The structure represents a shadow of a table.
+///
+/// NOTICE: It uses [`Margin`] therefore it often can't be combined.
+///
+/// [`Margin`]: crate::Margin
+#[derive(Debug, Clone)]
+pub struct Shadow {
+    c: char,
+    size: usize,
+    size_offset: usize,
+    direction: Sides<bool>,
+    #[cfg(feature = "color")]
+    color: Option<Color>,
+}
+
+impl Shadow {
+    /// A default fill character to be used.
+    pub const DEFAULT_FILL: char = '▒';
+
+    /// Construct's an [`Shadow`] object with default fill [`Shadow::DEFAULT_FILL`].
+    ///
+    /// It uses space(' ') as a default fill character.
+    /// To set a custom character you can use [`Self::set_fill`] function.
+    pub fn new(size: usize) -> Self {
+        Self {
+            c: Self::DEFAULT_FILL,
+            size,
+            size_offset: 1,
+            direction: Sides::new(false, true, false, true),
+            #[cfg(feature = "color")]
+            color: None,
+        }
+    }
+
+    /// The function, sets a characters for the [`Shadow`] to be used.
+    pub fn set_fill(&mut self, c: char) -> &mut Self {
+        self.c = c;
+        self
+    }
+
+    /// Set an offset value (default is '1').
+    pub fn set_offset(&mut self, size: usize) -> &mut Self {
+        self.size_offset = size;
+        self
+    }
+
+    /// Switch shadow to top.
+    pub fn set_top(&mut self) -> &mut Self {
+        self.direction.top = true;
+        self.direction.bottom = false;
+        self
+    }
+
+    /// Switch shadow to bottom.
+    pub fn set_bottom(&mut self) -> &mut Self {
+        self.direction.bottom = true;
+        self.direction.top = false;
+        self
+    }
+
+    /// Switch shadow to left.
+    pub fn set_left(&mut self) -> &mut Self {
+        self.direction.left = true;
+        self.direction.right = false;
+        self
+    }
+
+    /// Switch shadow to right.
+    pub fn set_right(&mut self) -> &mut Self {
+        self.direction.right = true;
+        self.direction.left = false;
+        self
+    }
+
+    /// Sets a color for a shadow.
+    #[cfg(feature = "color")]
+    pub fn set_color(&mut self, color: Color) -> &mut Self {
+        self.color = Some(color);
+        self
+    }
+}
+
+impl<R> TableOption<R> for Shadow {
+    fn change(&mut self, table: &mut Table<R>) {
+        let mut margin = *table.get_config().get_margin();
+
+        if self.direction.top {
+            margin.top.size = self.size;
+            margin.top.fill = self.c;
+        }
+
+        if self.direction.bottom {
+            margin.bottom.size = self.size;
+            margin.bottom.fill = self.c;
+        }
+
+        if self.direction.left {
+            margin.left.size = self.size;
+            margin.left.fill = self.c;
+        }
+
+        if self.direction.right {
+            margin.right.size = self.size;
+            margin.right.fill = self.c;
+        }
+
+        let mut offset = Sides::new(
+            Offset::Begin(0),
+            Offset::Begin(0),
+            Offset::Begin(0),
+            Offset::Begin(0),
+        );
+
+        if self.direction.right && self.direction.bottom {
+            offset.bottom = Offset::Begin(self.size_offset);
+            offset.right = Offset::Begin(self.size_offset);
+        }
+
+        if self.direction.right && self.direction.top {
+            offset.top = Offset::Begin(self.size_offset);
+            offset.right = Offset::End(self.size_offset);
+        }
+
+        if self.direction.left && self.direction.bottom {
+            offset.bottom = Offset::End(self.size_offset);
+            offset.left = Offset::Begin(self.size_offset);
+        }
+
+        if self.direction.left && self.direction.top {
+            offset.top = Offset::End(self.size_offset);
+            offset.left = Offset::End(self.size_offset);
+        }
+
+        let cfg = table.get_config_mut();
+        cfg.set_margin(margin);
+        cfg.set_margin_offset(offset);
+
+        #[cfg(feature = "color")]
+        if let Some(color) = self.color.as_ref() {
+            let mut colors = Sides::default();
+            if self.direction.top {
+                colors.top = color.clone().into();
+            }
+            if self.direction.bottom {
+                colors.bottom = color.clone().into();
+            }
+            if self.direction.left {
+                colors.left = color.clone().into();
+            }
+            if self.direction.right {
+                colors.right = color.clone().into();
+            }
+
+            cfg.set_margin_color(colors);
+        }
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 4332111..7487320 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,11 +1,3 @@
-#![warn(
-    missing_docs,
-    rust_2018_idioms,
-    missing_debug_implementations,
-    unreachable_pub
-)]
-#![deny(unused_must_use)]
-
 //! An easy to use library for pretty print tables of Rust `struct`s and `enum`s.
 //!
 //! The library is based on a [`Tabled`] trait which is used to actually build tables.
@@ -233,9 +225,17 @@
 //! You can find more examples of settings and attributes in
 //! [README.md](https://github.com/zhiburt/tabled/blob/master/README.md)
 
+#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
 #![doc(
     html_logo_url = "https://raw.githubusercontent.com/zhiburt/tabled/86ac146e532ce9f7626608d7fd05072123603a2e/assets/tabled-gear.svg"
 )]
+#![warn(
+    missing_docs,
+    rust_2018_idioms,
+    missing_debug_implementations,
+    unreachable_pub
+)]
+#![deny(unused_must_use)]
 
 mod features;
 mod modify;
@@ -269,6 +269,7 @@ pub use crate::{
         panel::{Footer, Header, Panel},
         peaker,
         rotate::Rotate,
+        shadow,
         span::Span,
         style::{self, Border, BorderText, Style},
         width::{self, Width},


Test patch:
diff --git a/tests/shadow_test.rs b/tests/shadow_test.rs
new file mode 100644
index 0000000..5657fb9
--- /dev/null
+++ b/tests/shadow_test.rs
@@ -0,0 +1,108 @@
+use tabled::{shadow::Shadow, Style};
+
+use crate::util::{new_table, test_table};
+
+#[cfg(feature = "color")]
+use owo_colors::OwoColorize;
+#[cfg(feature = "color")]
+use std::convert::TryFrom;
+#[cfg(feature = "color")]
+use tabled::color::Color;
+
+mod util;
+
+test_table!(
+    test_shadow_bottom_right_0,
+    new_table([(123, 456, 789), (234, 567, 891)]).with(Style::psql()).with(Shadow::new(1)),
+    " i32 | i32 | i32  "
+    "-----+-----+-----▒"
+    " 123 | 456 | 789 ▒"
+    " 234 | 567 | 891 ▒"
+    " ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒"
+);
+
+test_table!(
+    test_shadow_bottom_left_0,
+    new_table([(123, 456, 789), (234, 567, 891)]).with(Style::psql()).with(Shadow::new(1).set_left()),
+    "  i32 | i32 | i32 "
+    "▒-----+-----+-----"
+    "▒ 123 | 456 | 789 "
+    "▒ 234 | 567 | 891 "
+    "▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ "
+);
+
+test_table!(
+    test_shadow_top_right_0,
+    new_table([(123, 456, 789), (234, 567, 891)]).with(Style::psql()).with(Shadow::new(1).set_top()),
+   " ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒"
+   " i32 | i32 | i32 ▒"
+   "-----+-----+-----▒"
+   " 123 | 456 | 789 ▒"
+   " 234 | 567 | 891  "
+);
+
+test_table!(
+    test_shadow_top_left_0,
+    new_table([(123, 456, 789), (234, 567, 891)]).with(Style::psql()).with(Shadow::new(1).set_top().set_left()),
+    "▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ "
+    "▒ i32 | i32 | i32 "
+    "▒-----+-----+-----"
+    "▒ 123 | 456 | 789 "
+    "  234 | 567 | 891 "
+);
+
+test_table!(
+    test_shadow_set_fill,
+    new_table([(123, 456, 789), (234, 567, 891)]).with(Shadow::new(1).set_fill('▓')),
+    "+-----+-----+-----+ "
+    "| i32 | i32 | i32 |▓"
+    "+-----+-----+-----+▓"
+    "| 123 | 456 | 789 |▓"
+    "+-----+-----+-----+▓"
+    "| 234 | 567 | 891 |▓"
+    "+-----+-----+-----+▓"
+    " ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓"
+);
+
+test_table!(
+    test_shadow_size_1,
+    new_table([(123, 456, 789), (234, 567, 891)]).with(Shadow::new(2).set_fill('▓')),
+    "+-----+-----+-----+  "
+    "| i32 | i32 | i32 |▓▓"
+    "+-----+-----+-----+▓▓"
+    "| 123 | 456 | 789 |▓▓"
+    "+-----+-----+-----+▓▓"
+    "| 234 | 567 | 891 |▓▓"
+    "+-----+-----+-----+▓▓"
+    " ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓"
+    " ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓"
+);
+
+test_table!(
+    test_shadow_set_offset_0,
+    new_table([(123, 456, 789), (234, 567, 891)]).with(Shadow::new(2).set_offset(3)),
+    "+-----+-----+-----+  "
+    "| i32 | i32 | i32 |  "
+    "+-----+-----+-----+  "
+    "| 123 | 456 | 789 |▒▒"
+    "+-----+-----+-----+▒▒"
+    "| 234 | 567 | 891 |▒▒"
+    "+-----+-----+-----+▒▒"
+    "   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒"
+    "   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+    test_shadow_set_color_0,
+    new_table([(123, 456, 789), (234, 567, 891)]).with(Shadow::new(2).set_offset(3).set_color(Color::try_from(' '.red().to_string()).unwrap())),
+    "+-----+-----+-----+  "
+    "| i32 | i32 | i32 |  "
+    "+-----+-----+-----+  "
+    "| 123 | 456 | 789 |\u{1b}[31m▒▒\u{1b}[39m"
+    "+-----+-----+-----+\u{1b}[31m▒▒\u{1b}[39m"
+    "| 234 | 567 | 891 |\u{1b}[31m▒▒\u{1b}[39m"
+    "+-----+-----+-----+\u{1b}[31m▒▒\u{1b}[39m"
+    "   \u{1b}[31m▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒\u{1b}[39m"
+    "   \u{1b}[31m▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒\u{1b}[39m"
+);