tabled/settings/style/
border_color.rs

1//! This module contains a configuration of a Border to set its color via [`BorderColor`].
2
3use crate::{
4    grid::{
5        config::{Border as GridBorder, ColoredConfig, Entity},
6        records::{ExactRecords, Records},
7    },
8    settings::{CellOption, Color, TableOption},
9};
10
11/// Border represents a border color of a Cell.
12///
13/// ```text
14///                         top border
15///                             |
16///                             V
17/// corner top left ------> +_______+  <---- corner top left
18///                         |       |
19/// left border ----------> |  cell |  <---- right border
20///                         |       |
21/// corner bottom right --> +_______+  <---- corner bottom right
22///                             ^
23///                             |
24///                        bottom border
25/// ```
26///
27/// # Example
28///
29/// ```rust,no_run
30/// # use tabled::{Table, settings::{style::{Style, BorderColor}, object::Rows, Color}};
31/// # let data: Vec<&'static str> = Vec::new();
32/// let mut table = Table::new(&data);
33/// table.with(Style::ascii());
34/// table.modify(Rows::one(0), BorderColor::new().top(Color::FG_RED));
35/// ```
36#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
37pub struct BorderColor {
38    inner: GridBorder<Color>,
39}
40
41impl BorderColor {
42    pub(crate) const fn from_border(inner: GridBorder<Color>) -> Self {
43        BorderColor { inner }
44    }
45
46    /// Creates an empty border.
47    pub const fn new() -> Self {
48        Self::from_border(GridBorder::empty())
49    }
50
51    /// This function constructs a cell borders with all sides set.
52    #[allow(clippy::too_many_arguments)]
53    pub const fn full(
54        top: Color,
55        bottom: Color,
56        left: Color,
57        right: Color,
58        top_left: Color,
59        top_right: Color,
60        bottom_left: Color,
61        bottom_right: Color,
62    ) -> Self {
63        Self::from_border(GridBorder::full(
64            top,
65            bottom,
66            left,
67            right,
68            top_left,
69            top_right,
70            bottom_left,
71            bottom_right,
72        ))
73    }
74
75    /// This function constructs a cell borders with all sides's char set to a given color.
76    /// It behaves like [`BorderColor::full`] with the same color set to each side.
77    pub fn filled(c: Color) -> Self {
78        Self::full(
79            c.clone(),
80            c.clone(),
81            c.clone(),
82            c.clone(),
83            c.clone(),
84            c.clone(),
85            c.clone(),
86            c,
87        )
88    }
89
90    /// Set a top border color.
91    pub fn top(mut self, c: Color) -> Self {
92        self.inner.top = Some(c);
93        BorderColor::from_border(self.inner)
94    }
95
96    /// Set a bottom border color.
97    pub fn bottom(mut self, c: Color) -> Self {
98        self.inner.bottom = Some(c);
99        BorderColor::from_border(self.inner)
100    }
101
102    /// Set a left border color.
103    pub fn left(mut self, c: Color) -> Self {
104        self.inner.left = Some(c);
105        BorderColor::from_border(self.inner)
106    }
107
108    /// Set a right border color.
109    pub fn right(mut self, c: Color) -> Self {
110        self.inner.right = Some(c);
111        BorderColor::from_border(self.inner)
112    }
113
114    /// Converts a border into a general data structure.
115    pub fn into_inner(self) -> GridBorder<Color> {
116        self.inner
117    }
118
119    /// Set a top left intersection color.
120    pub fn corner_top_left(mut self, c: Color) -> Self {
121        self.inner.left_top_corner = Some(c);
122        self
123    }
124
125    /// Set a top right intersection color.
126    pub fn corner_top_right(mut self, c: Color) -> Self {
127        self.inner.right_top_corner = Some(c);
128        self
129    }
130
131    /// Set a bottom left intersection color.
132    pub fn corner_bottom_left(mut self, c: Color) -> Self {
133        self.inner.left_bottom_corner = Some(c);
134        self
135    }
136
137    /// Set a bottom right intersection color.
138    pub fn corner_bottom_right(mut self, c: Color) -> Self {
139        self.inner.right_bottom_corner = Some(c);
140        self
141    }
142}
143
144impl From<BorderColor> for GridBorder<Color> {
145    fn from(value: BorderColor) -> Self {
146        value.inner
147    }
148}
149
150impl<Data> CellOption<Data, ColoredConfig> for BorderColor
151where
152    Data: Records + ExactRecords,
153{
154    fn change(self, records: &mut Data, cfg: &mut ColoredConfig, entity: Entity) {
155        let border = self.inner.clone().convert();
156        if matches!(entity, Entity::Global) && border.is_same() && !border.is_empty() {
157            let color = border.top.expect("ok");
158            cfg.set_border_color_default(color);
159            return;
160        }
161
162        let count_rows = records.count_rows();
163        let count_columns = records.count_columns();
164
165        for pos in entity.iter(count_rows, count_columns) {
166            cfg.set_border_color(pos, border.clone());
167        }
168    }
169}
170
171impl<Data, D> TableOption<Data, ColoredConfig, D> for BorderColor
172where
173    Data: Records + ExactRecords,
174{
175    fn change(self, records: &mut Data, cfg: &mut ColoredConfig, _: &mut D) {
176        let count_rows = records.count_rows();
177        let count_columns = records.count_columns();
178        let shape = (count_rows, count_columns);
179
180        if count_rows == 0 || count_columns == 0 {
181            return;
182        }
183
184        let color = self.inner.clone().convert();
185
186        for col in 0..count_columns {
187            let pos = (0, col).into();
188            let mut b = GridBorder::cloned(&cfg.get_border_color(pos, shape));
189            b.top = color.top.clone();
190            b.right_top_corner = color.top.clone();
191            cfg.set_border_color(pos, b);
192
193            let pos = (count_rows - 1, col).into();
194            let mut b = GridBorder::cloned(&cfg.get_border_color(pos, shape));
195            b.bottom = color.bottom.clone();
196            b.right_bottom_corner = color.bottom.clone();
197            cfg.set_border_color(pos, b);
198        }
199
200        for row in 0..count_rows {
201            let pos = (row, 0).into();
202            let mut b = GridBorder::cloned(&cfg.get_border_color(pos, shape));
203            b.left = color.left.clone();
204            b.left_bottom_corner = color.left.clone();
205            cfg.set_border_color(pos, b);
206
207            let pos = (row, count_columns - 1).into();
208            let mut b = GridBorder::cloned(&cfg.get_border_color(pos, shape));
209            b.right = color.right.clone();
210            b.right_bottom_corner = color.right.clone();
211            cfg.set_border_color(pos, b);
212        }
213
214        let pos = (0, 0).into();
215        let mut b = GridBorder::cloned(&cfg.get_border_color(pos, shape));
216        b.left_top_corner = color.left_top_corner.clone();
217        cfg.remove_border_color(pos, shape);
218        cfg.set_border_color(pos, b);
219
220        let pos = (0, count_columns - 1).into();
221        let mut b = GridBorder::cloned(&cfg.get_border_color(pos, shape));
222        b.right_top_corner = color.right_top_corner.clone();
223        cfg.remove_border_color(pos, shape);
224        cfg.set_border_color(pos, b);
225
226        let pos = (count_rows - 1, 0).into();
227        let mut b = GridBorder::cloned(&cfg.get_border_color(pos, shape));
228        b.left_bottom_corner = color.left_bottom_corner.clone();
229        cfg.remove_border_color(pos, shape);
230        cfg.set_border_color(pos, b);
231
232        let pos = (count_rows - 1, count_columns - 1).into();
233        let mut b = GridBorder::cloned(&cfg.get_border_color(pos, shape));
234        b.right_bottom_corner = color.right_bottom_corner.clone();
235        cfg.remove_border_color(pos, shape);
236        cfg.set_border_color(pos, b);
237    }
238}