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::color::AnsiColor,
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, D, ColoredConfig> 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: &Color) = &self.color {
121 set_margin_color(cfg, color: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 = Sides::filled(Offset::Begin(0));
153 if direction.right && direction.bottom {
154 margin.bottom = Offset::Begin(size);
155 margin.right = Offset::Begin(size);
156 }
157
158 if direction.right && direction.top {
159 margin.top = Offset::Begin(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::Begin(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: AnsiColor<'static>, direction: &Sides<bool>) {
177 let mut margin: Sides<Option<AnsiColor<'static>>> = 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}
196