tabled/features/
shadow.rs

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