tabled/settings/shadow/
mod.rs

1//! This module contains a [`Shadow`] option for a [`Table`].
2//!
3//! # Example
4//!
5//! ```
6//! use tabled::{Table, settings::{Shadow, Style}};
7//!
8//! let data = vec!["Hello", "World", "!"];
9//!
10//! let table = Table::new(data)
11//!     .with(Style::markdown())
12//!     .with(Shadow::new(1))
13//!     .to_string();
14//!
15//! assert_eq!(
16//!     table,
17//!     concat!(
18//!         "| &str  | \n",
19//!         "|-------|▒\n",
20//!         "| Hello |▒\n",
21//!         "| World |▒\n",
22//!         "| !     |▒\n",
23//!         " ▒▒▒▒▒▒▒▒▒",
24//!     )
25//! );
26//! ```
27//!
28//! [`Table`]: crate::Table
29
30use crate::{
31    grid::ansi::ANSIBuf,
32    grid::config::{ColoredConfig, Indent, Offset, Sides},
33    settings::{color::Color, TableOption},
34};
35
36/// The structure represents a shadow of a table.
37///
38/// NOTICE: It uses [`Margin`] therefore it often can't be combined.
39///
40/// [`Margin`]: crate::settings::Margin
41#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
42pub struct Shadow {
43    c: char,
44    size: usize,
45    size_offset: usize,
46    direction: Sides<bool>,
47    color: Option<Color>,
48}
49
50impl Shadow {
51    /// A default fill character to be used.
52    pub const DEFAULT_FILL: char = '▒';
53
54    /// Construct's an [`Shadow`] object with default fill [`Shadow::DEFAULT_FILL`].
55    ///
56    /// It uses space(' ') as a default fill character.
57    /// To set a custom character you can use [`Self::set_fill`] function.
58    pub fn new(size: usize) -> Self {
59        Self {
60            c: Self::DEFAULT_FILL,
61            size,
62            size_offset: 1,
63            direction: Sides::new(false, true, false, true),
64            color: None,
65        }
66    }
67
68    /// The function, sets a characters for the [`Shadow`] to be used.
69    pub fn set_fill(mut self, c: char) -> Self {
70        self.c = c;
71        self
72    }
73
74    /// Set an offset value (default is '1').
75    pub fn set_offset(mut self, size: usize) -> Self {
76        self.size_offset = size;
77        self
78    }
79
80    /// Switch shadow to top.
81    pub fn set_top(mut self) -> Self {
82        self.direction.top = true;
83        self.direction.bottom = false;
84        self
85    }
86
87    /// Switch shadow to bottom.
88    pub fn set_bottom(mut self) -> Self {
89        self.direction.bottom = true;
90        self.direction.top = false;
91        self
92    }
93
94    /// Switch shadow to left.
95    pub fn set_left(mut self) -> Self {
96        self.direction.left = true;
97        self.direction.right = false;
98        self
99    }
100
101    /// Switch shadow to right.
102    pub fn set_right(mut self) -> Self {
103        self.direction.right = true;
104        self.direction.left = false;
105        self
106    }
107
108    /// Sets a color for a shadow.
109    pub fn set_color(mut self, color: Color) -> Self {
110        self.color = Some(color);
111        self
112    }
113}
114
115impl<R, D> TableOption<R, ColoredConfig, D> for Shadow {
116    fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
117        set_margin(cfg, self.size, self.c, &self.direction);
118        set_margin_offset(cfg, self.size_offset, &self.direction);
119
120        if let Some(color) = &self.color {
121            set_margin_color(cfg, color.clone().into(), &self.direction);
122        }
123    }
124}
125
126fn set_margin(cfg: &mut ColoredConfig, size: usize, c: char, direction: &Sides<bool>) {
127    let mut margin: Sides<Indent> = Sides::default();
128    if direction.top {
129        margin.top.size = size;
130        margin.top.fill = c;
131    }
132
133    if direction.bottom {
134        margin.bottom.size = size;
135        margin.bottom.fill = c;
136    }
137
138    if direction.left {
139        margin.left.size = size;
140        margin.left.fill = c;
141    }
142
143    if direction.right {
144        margin.right.size = size;
145        margin.right.fill = c;
146    }
147
148    cfg.set_margin(margin);
149}
150
151fn set_margin_offset(cfg: &mut ColoredConfig, size: usize, direction: &Sides<bool>) {
152    let mut margin = Sides::filled(Offset::Start(0));
153    if direction.right && direction.bottom {
154        margin.bottom = Offset::Start(size);
155        margin.right = Offset::Start(size);
156    }
157
158    if direction.right && direction.top {
159        margin.top = Offset::Start(size);
160        margin.right = Offset::End(size);
161    }
162
163    if direction.left && direction.bottom {
164        margin.bottom = Offset::End(size);
165        margin.left = Offset::Start(size);
166    }
167
168    if direction.left && direction.top {
169        margin.top = Offset::End(size);
170        margin.left = Offset::End(size);
171    }
172
173    cfg.set_margin_offset(margin);
174}
175
176fn set_margin_color(cfg: &mut ColoredConfig, color: ANSIBuf, direction: &Sides<bool>) {
177    let mut margin: Sides<Option<ANSIBuf>> = Sides::default();
178    if direction.right {
179        margin.right = Some(color.clone());
180    }
181
182    if direction.top {
183        margin.top = Some(color.clone());
184    }
185
186    if direction.left {
187        margin.left = Some(color.clone());
188    }
189
190    if direction.bottom {
191        margin.bottom = Some(color.clone());
192    }
193
194    cfg.set_margin_color(margin);
195}