tabled/features/style/
raw_style.rs

1//! This module contains [`RawStyle`] structure, which is analogues to [`Style`] but not generic,
2//! so sometimes it can be used more conviently.
3
4use std::collections::HashMap;
5
6use papergrid::{records::Records, Borders};
7
8use crate::{
9    style::{HorizontalLine, Line, VerticalLine},
10    Border, Style, Table, TableOption,
11};
12
13/// A raw style data, which can be produced safely from [`Style`].
14///
15/// It can be useful in order to not have a generics and be able to use it as a variable more conveniently.
16#[derive(Default, Debug, Clone)]
17pub struct RawStyle {
18    borders: Borders<char>,
19    horizontals: HashMap<usize, Line>,
20    verticals: HashMap<usize, Line>,
21}
22
23impl RawStyle {
24    /// Set a top border character.
25    pub fn set_top(&mut self, s: Option<char>) -> &mut Self {
26        self.borders.top = s;
27        self
28    }
29
30    /// Set a bottom border character.
31    pub fn set_bottom(&mut self, s: Option<char>) -> &mut Self {
32        self.borders.bottom = s;
33        self
34    }
35
36    /// Set a left border character.
37    pub fn set_left(&mut self, s: Option<char>) -> &mut Self {
38        self.borders.vertical_left = s;
39        self
40    }
41
42    /// Set a right border character.
43    pub fn set_right(&mut self, s: Option<char>) -> &mut Self {
44        self.borders.vertical_right = s;
45        self
46    }
47
48    /// Set a top split border character.
49    pub fn set_top_split(&mut self, s: Option<char>) -> &mut Self {
50        self.borders.top_intersection = s;
51        self
52    }
53
54    /// Set a bottom split character.
55    pub fn set_bottom_split(&mut self, s: Option<char>) -> &mut Self {
56        self.borders.bottom_intersection = s;
57        self
58    }
59
60    /// Set a left split character.
61    pub fn set_left_split(&mut self, s: Option<char>) -> &mut Self {
62        self.borders.horizontal_left = s;
63        self
64    }
65
66    /// Set a right split character.
67    pub fn set_right_split(&mut self, s: Option<char>) -> &mut Self {
68        self.borders.horizontal_right = s;
69        self
70    }
71
72    /// Set an internal character.
73    pub fn set_internal_split(&mut self, s: Option<char>) -> &mut Self {
74        self.borders.intersection = s;
75        self
76    }
77
78    /// Set a vertical character.
79    pub fn set_vertical(&mut self, s: Option<char>) -> &mut Self {
80        self.borders.vertical = s;
81        self
82    }
83
84    /// Set a horizontal character.
85    pub fn set_horizontal(&mut self, s: Option<char>) -> &mut Self {
86        self.borders.horizontal = s;
87        self
88    }
89
90    /// Set a character for a top left corner.
91    pub fn set_top_left(&mut self, s: Option<char>) -> &mut Self {
92        self.borders.top_left = s;
93        self
94    }
95
96    /// Set a character for a top right corner.
97    pub fn set_top_right(&mut self, s: Option<char>) -> &mut Self {
98        self.borders.top_right = s;
99        self
100    }
101
102    /// Set a character for a bottom left corner.
103    pub fn set_bottom_left(&mut self, s: Option<char>) -> &mut Self {
104        self.borders.bottom_left = s;
105        self
106    }
107
108    /// Set a character for a bottom right corner.
109    pub fn set_bottom_right(&mut self, s: Option<char>) -> &mut Self {
110        self.borders.bottom_right = s;
111        self
112    }
113
114    /// Set horizontal border lines.
115    ///
116    /// # Example
117    ///
118    /// ```
119    /// use std::collections::HashMap;
120    /// use tabled::{style::{Style, Line, RawStyle}, TableIteratorExt};
121    ///
122    /// let mut style = RawStyle::from(Style::re_structured_text());
123    ///
124    /// let mut lines = HashMap::new();
125    /// lines.insert(1, Style::extended().get_horizontal());
126    /// style.set_horizontals(lines);
127    ///
128    /// let table = (0..3)
129    ///    .map(|i| ("Hello", i))
130    ///    .table()
131    ///    .with(style)
132    ///    .to_string();
133    ///
134    /// assert_eq!(
135    ///     table,
136    ///     concat!(
137    ///         " ======= ===== \n",
138    ///         "  &str    i32  \n",
139    ///         "╠═══════╬═════╣\n",
140    ///         "  Hello   0    \n",
141    ///         "  Hello   1    \n",
142    ///         "  Hello   2    \n",
143    ///         " ======= ===== ",
144    ///     ),
145    /// )
146    /// ```
147    pub fn set_horizontals(&mut self, lines: HashMap<usize, Line>) -> &mut Self {
148        self.horizontals = lines;
149        self
150    }
151
152    /// Set vertical border lines.
153    ///
154    /// # Example
155    ///
156    /// ```
157    /// use std::collections::HashMap;
158    /// use tabled::{style::{Style, Line, RawStyle}, TableIteratorExt};
159    ///
160    /// let mut style = RawStyle::from(Style::re_structured_text());
161    ///
162    /// let mut lines = HashMap::new();
163    /// lines.insert(1, Style::extended().get_horizontal());
164    /// style.set_verticals(lines);
165    ///
166    /// let table = (0..3)
167    ///    .map(|i| ("Hello", i))
168    ///    .table()
169    ///    .with(style)
170    ///    .to_string();
171    ///
172    /// assert_eq!(
173    ///     table,
174    ///     concat!(
175    ///         "=======╠=====\n",
176    ///         " &str  ═ i32 \n",
177    ///         "======= =====\n",
178    ///         " Hello ═ 0   \n",
179    ///         " Hello ═ 1   \n",
180    ///         " Hello ═ 2   \n",
181    ///         "=======╣=====",
182    ///     ),
183    /// )
184    /// ```
185    pub fn set_verticals(&mut self, lines: HashMap<usize, Line>) -> &mut Self {
186        self.verticals = lines;
187        self
188    }
189
190    /// Get a left char.
191    pub fn get_left(&self) -> Option<char> {
192        self.borders.vertical_left
193    }
194
195    /// Get a left intersection char.
196    pub fn get_left_intersection(&self) -> Option<char> {
197        self.borders.horizontal_left
198    }
199
200    /// Get a right char.
201    pub fn get_right(&self) -> Option<char> {
202        self.borders.vertical_right
203    }
204
205    /// Get a right intersection char.
206    pub fn get_right_intersection(&self) -> Option<char> {
207        self.borders.horizontal_right
208    }
209
210    /// Get a top char.
211    pub fn get_top(&self) -> Option<char> {
212        self.borders.top
213    }
214
215    /// Get a top left char.
216    pub fn get_top_left(&self) -> Option<char> {
217        self.borders.top_left
218    }
219
220    /// Get a top right char.
221    pub fn get_top_right(&self) -> Option<char> {
222        self.borders.top_right
223    }
224
225    /// Get a top intersection char.
226    pub fn get_top_intersection(&self) -> Option<char> {
227        self.borders.top_intersection
228    }
229
230    /// Get a bottom intersection char.
231    pub fn get_bottom(&self) -> Option<char> {
232        self.borders.bottom
233    }
234
235    /// Get a bottom intersection char.
236    pub fn get_bottom_left(&self) -> Option<char> {
237        self.borders.bottom_left
238    }
239
240    /// Get a bottom intersection char.
241    pub fn get_bottom_right(&self) -> Option<char> {
242        self.borders.bottom_right
243    }
244
245    /// Get a bottom intersection char.
246    pub fn get_bottom_intersection(&self) -> Option<char> {
247        self.borders.bottom_intersection
248    }
249
250    /// Returns an outer border of the style.
251    pub fn get_frame(&self) -> Border {
252        Border::new_raw(Some(papergrid::Border {
253            top: self.borders.top,
254            bottom: self.borders.bottom,
255            left: self.borders.vertical_left,
256            right: self.borders.vertical_right,
257            left_top_corner: self.borders.top_left,
258            right_top_corner: self.borders.top_right,
259            left_bottom_corner: self.borders.bottom_left,
260            right_bottom_corner: self.borders.bottom_right,
261        }))
262    }
263
264    /// Returns a [`RawStyle`] version which can set colors.
265    #[cfg_attr(docsrs, doc(cfg(feature = "color")))]
266    #[cfg(feature = "color")]
267    pub fn colored(self) -> crate::style::RawStyleColored {
268        crate::style::RawStyleColored::from(self)
269    }
270}
271
272impl From<Borders<char>> for RawStyle {
273    fn from(borders: Borders<char>) -> Self {
274        Self {
275            borders,
276            horizontals: HashMap::new(),
277            verticals: HashMap::new(),
278        }
279    }
280}
281
282impl<R> TableOption<R> for RawStyle
283where
284    R: Records,
285{
286    fn change(&mut self, table: &mut Table<R>) {
287        (&*self).change(table)
288    }
289}
290
291impl<R> TableOption<R> for &RawStyle
292where
293    R: Records,
294{
295    fn change(&mut self, table: &mut Table<R>) {
296        if table.is_empty() {
297            return;
298        }
299
300        let (count_rows, count_cols) = table.shape();
301
302        let cfg = table.get_config_mut();
303        cfg.clear_theme();
304        cfg.set_borders(self.borders.clone());
305
306        if count_rows > 1 {
307            for (&row, line) in &self.horizontals {
308                if line.is_empty() {
309                    cfg.remove_horizontal_line(row);
310                } else {
311                    cfg.set_horizontal_line(row, papergrid::HorizontalLine::from(*line));
312                }
313            }
314        }
315
316        if count_cols > 1 {
317            for (&col, line) in &self.verticals {
318                if line.is_empty() {
319                    cfg.remove_vertical_line(col);
320                } else {
321                    cfg.set_vertical_line(col, papergrid::VerticalLine::from(*line));
322                }
323            }
324        }
325
326        table.destroy_width_cache();
327        table.destroy_height_cache();
328    }
329}
330
331impl<T, B, L, R, H, V, HLines, VLines> From<Style<T, B, L, R, H, V, HLines, VLines>> for RawStyle
332where
333    HLines: IntoIterator<Item = HorizontalLine>,
334    VLines: IntoIterator<Item = VerticalLine>,
335{
336    fn from(style: Style<T, B, L, R, H, V, HLines, VLines>) -> Self {
337        let horizontals = style
338            .horizontals
339            .into_iter()
340            .flat_map(|hr| {
341                let index = hr.index;
342                hr.line.map(|line| (index, line))
343            })
344            .collect();
345
346        let verticals = style
347            .verticals
348            .into_iter()
349            .flat_map(|hr| {
350                let index = hr.index;
351                hr.line.map(|line| (index, line))
352            })
353            .collect();
354
355        Self {
356            borders: style.borders,
357            horizontals,
358            verticals,
359        }
360    }
361}