tabled/settings/themes/
colorization.rs

1use crate::{
2    grid::{
3        ansi::ANSIBuf,
4        config::{ColoredConfig, Entity, Sides},
5        records::{ExactRecords, Records},
6    },
7    settings::{object::Object, Color, TableOption},
8};
9
10/// [`Colorization`] sets a color for the whole table data (so it's not include the borders).
11///
12/// You can colorize borders in a different round using [`BorderColor`] or [`Theme`]
13///
14/// # Examples
15///
16/// ```
17/// use std::iter::FromIterator;
18///
19/// use tabled::builder::Builder;
20/// use tabled::settings::{style::BorderColor, themes::Colorization, Color, Style};
21///
22/// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]];
23///
24/// let color1 = Color::FG_BLACK | Color::BG_WHITE;
25/// let color2 = Color::BG_BLACK | Color::FG_WHITE;
26/// let color3 = Color::FG_RED | Color::BG_RED;
27///
28/// let mut table = Builder::from_iter(data).build();
29/// table
30///     .with(Colorization::chess(color1, color2))
31///     .with(Style::modern())
32///     .with(BorderColor::filled(color3));
33///
34/// println!("{table}");
35/// ```
36///
37/// [`Theme`]: crate::settings::themes::Theme
38/// [`BorderColor`]: crate::settings::style::BorderColor
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct Colorization {
41    pattern: ColorizationPattern,
42    colors: Vec<Color>,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
46enum ColorizationPattern {
47    Column,
48    Row,
49    ByRow,
50    ByColumn,
51    Chess,
52}
53
54impl Colorization {
55    /// Creates a [`Colorization`] with a chess pattern.
56    ///
57    /// ```
58    /// use std::iter::FromIterator;
59    ///
60    /// use tabled::builder::Builder;
61    /// use tabled::settings::{themes::Colorization, Color, Style};
62    ///
63    /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]];
64    ///
65    /// let color1 = Color::FG_BLACK | Color::BG_WHITE;
66    /// let color2 = Color::BG_BLACK | Color::FG_WHITE;
67    ///
68    /// let mut table = Builder::from_iter(data).build();
69    /// table
70    ///     .with(Colorization::chess(color1, color2))
71    ///     .with(Style::empty());
72    ///
73    /// println!("{table}");
74    /// ```
75    pub fn chess(white: Color, black: Color) -> Self {
76        Self::new(vec![white, black], ColorizationPattern::Chess)
77    }
78
79    /// Creates a [`Colorization`] with a target [`Object`].
80    ///
81    /// ```
82    /// use std::iter::FromIterator;
83    ///
84    /// use tabled::builder::Builder;
85    /// use tabled::settings::object::Rows;
86    /// use tabled::settings::{themes::Colorization, Color, Style};
87    ///
88    /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]];
89    ///
90    /// let color1 = Color::FG_BLACK | Color::BG_WHITE;
91    /// let color2 = Color::BG_BLACK | Color::FG_WHITE;
92    ///
93    /// let mut table = Builder::from_iter(data).build();
94    /// table
95    ///     .with(Colorization::exact([color1, color2], Rows::first()))
96    ///     .with(Style::empty());
97    ///
98    /// println!("{table}");
99    /// ```
100    pub fn exact<I, O>(colors: I, target: O) -> ExactColorization<O>
101    where
102        I: IntoIterator,
103        I::Item: Into<Color>,
104    {
105        let colors = colors.into_iter().map(Into::into).collect();
106        ExactColorization::new(colors, target)
107    }
108
109    /// Creates a [`Colorization`] with a pattern which changes row by row.
110    ///
111    /// ```
112    /// use std::iter::FromIterator;
113    ///
114    /// use tabled::builder::Builder;
115    /// use tabled::settings::object::Rows;
116    /// use tabled::settings::{themes::Colorization, Color, Style};
117    ///
118    /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]];
119    ///
120    /// let color1 = Color::FG_BLACK | Color::BG_WHITE;
121    /// let color2 = Color::BG_BLACK | Color::FG_WHITE;
122    ///
123    /// let mut table = Builder::from_iter(data).build();
124    /// table
125    ///     .with(Colorization::rows([color1, color2]))
126    ///     .with(Style::empty());
127    ///
128    /// println!("{table}");
129    /// ```
130    pub fn rows<I>(colors: I) -> Self
131    where
132        I: IntoIterator,
133        I::Item: Into<Color>,
134    {
135        Self::new(colors, ColorizationPattern::Row)
136    }
137
138    /// Creates a [`Colorization`] with a pattern which changes column by column.
139    ///
140    /// ```
141    /// use std::iter::FromIterator;
142    ///
143    /// use tabled::builder::Builder;
144    /// use tabled::settings::object::Rows;
145    /// use tabled::settings::{themes::Colorization, Color, Style};
146    ///
147    /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]];
148    ///
149    /// let color1 = Color::FG_BLACK | Color::BG_WHITE;
150    /// let color2 = Color::BG_BLACK | Color::FG_WHITE;
151    ///
152    /// let mut table = Builder::from_iter(data).build();
153    /// table
154    ///     .with(Colorization::columns([color1, color2]))
155    ///     .with(Style::empty());
156    ///
157    /// println!("{table}");
158    /// ```
159    pub fn columns<I>(colors: I) -> Self
160    where
161        I: IntoIterator,
162        I::Item: Into<Color>,
163    {
164        Self::new(colors, ColorizationPattern::Column)
165    }
166
167    /// Creates a [`Colorization`] with a pattern which peaks cells one by one iterating over rows.
168    ///
169    /// ```
170    /// use std::iter::FromIterator;
171    ///
172    /// use tabled::builder::Builder;
173    /// use tabled::settings::object::Rows;
174    /// use tabled::settings::{themes::Colorization, Color, Style};
175    ///
176    /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]];
177    ///
178    /// let color1 = Color::FG_BLACK | Color::BG_WHITE;
179    /// let color2 = Color::BG_BLACK | Color::FG_WHITE;
180    ///
181    /// let mut table = Builder::from_iter(data).build();
182    /// table
183    ///     .with(Colorization::by_row([color1, color2]))
184    ///     .with(Style::empty());
185    ///
186    /// println!("{table}");
187    /// ```
188    pub fn by_row<I>(colors: I) -> Self
189    where
190        I: IntoIterator,
191        I::Item: Into<Color>,
192    {
193        Self::new(colors, ColorizationPattern::ByRow)
194    }
195
196    /// Creates a [`Colorization`] with a pattern which peaks cells one by one iterating over columns.
197    ///
198    /// ```
199    /// use std::iter::FromIterator;
200    ///
201    /// use tabled::builder::Builder;
202    /// use tabled::settings::object::Rows;
203    /// use tabled::settings::{themes::Colorization, Color, Style};
204    ///
205    /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]];
206    ///
207    /// let color1 = Color::FG_BLACK | Color::BG_WHITE;
208    /// let color2 = Color::BG_BLACK | Color::FG_WHITE;
209    ///
210    /// let mut table = Builder::from_iter(data).build();
211    /// table
212    ///     .with(Colorization::by_column([color1, color2]))
213    ///     .with(Style::empty());
214    ///
215    /// println!("{table}");
216    /// ```
217    pub fn by_column<I>(colors: I) -> Self
218    where
219        I: IntoIterator,
220        I::Item: Into<Color>,
221    {
222        Self::new(colors, ColorizationPattern::ByColumn)
223    }
224
225    fn new<I>(colors: I, pattern: ColorizationPattern) -> Self
226    where
227        I: IntoIterator,
228        I::Item: Into<Color>,
229    {
230        let colors = colors.into_iter().map(Into::into).collect();
231        Self { colors, pattern }
232    }
233}
234
235impl<R, D> TableOption<R, ColoredConfig, D> for Colorization
236where
237    R: Records + ExactRecords,
238{
239    fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
240        if self.colors.is_empty() {
241            return;
242        }
243
244        let count_columns = records.count_columns();
245        let count_rows = records.count_rows();
246
247        match self.pattern {
248            ColorizationPattern::Column => colorize_columns(&self.colors, count_columns, cfg),
249            ColorizationPattern::Row => colorize_rows(&self.colors, count_rows, cfg),
250            ColorizationPattern::ByRow => {
251                colorize_by_row(&self.colors, count_rows, count_columns, cfg)
252            }
253            ColorizationPattern::ByColumn => {
254                colorize_by_column(&self.colors, count_rows, count_columns, cfg)
255            }
256            ColorizationPattern::Chess => {
257                colorize_diogonals(&self.colors, count_rows, count_columns, cfg)
258            }
259        }
260    }
261}
262
263fn colorize_columns(colors: &[Color], count_columns: usize, cfg: &mut ColoredConfig) {
264    for (col, color) in (0..count_columns).zip(colors.iter().cycle()) {
265        colorize_entity(color, Entity::Column(col), cfg);
266    }
267}
268
269fn colorize_rows(colors: &[Color], count_rows: usize, cfg: &mut ColoredConfig) {
270    for (row, color) in (0..count_rows).zip(colors.iter().cycle()) {
271        colorize_entity(color, Entity::Row(row), cfg);
272    }
273}
274
275fn colorize_by_row(
276    colors: &[Color],
277    count_rows: usize,
278    count_columns: usize,
279    cfg: &mut ColoredConfig,
280) {
281    let mut color_peek = colors.iter().cycle();
282    for row in 0..count_rows {
283        for col in 0..count_columns {
284            let color = color_peek.next().unwrap();
285            colorize_entity(color, Entity::Cell(row, col), cfg);
286        }
287    }
288}
289
290fn colorize_by_column(
291    colors: &[Color],
292    count_rows: usize,
293    count_columns: usize,
294    cfg: &mut ColoredConfig,
295) {
296    let mut color_peek = colors.iter().cycle();
297    for col in 0..count_columns {
298        for row in 0..count_rows {
299            let color = color_peek.next().unwrap();
300            colorize_entity(color, Entity::Cell(row, col), cfg);
301        }
302    }
303}
304
305fn colorize_diogonals(
306    colors: &[Color],
307    count_rows: usize,
308    count_columns: usize,
309    cfg: &mut ColoredConfig,
310) {
311    let mut color_peek = colors.iter().cycle();
312    for mut row in 0..count_rows {
313        let color = color_peek.next().unwrap();
314        for col in 0..count_columns {
315            colorize_entity(color, Entity::Cell(row, col), cfg);
316
317            row += 1;
318            if row == count_rows {
319                break;
320            }
321        }
322    }
323
324    let _ = color_peek.next().unwrap();
325
326    for mut col in 1..count_columns {
327        let color = color_peek.next().unwrap();
328        for row in 0..count_rows {
329            colorize_entity(color, Entity::Cell(row, col), cfg);
330
331            col += 1;
332            if col == count_columns {
333                break;
334            }
335        }
336    }
337}
338
339fn colorize_entity(color: &Color, pos: Entity, cfg: &mut ColoredConfig) {
340    let ansi_color = ANSIBuf::from(color.clone());
341    let _ = cfg.set_color(pos, ansi_color.clone());
342    cfg.set_justification_color(pos, Some(ansi_color.clone()));
343    cfg.set_padding_color(
344        pos,
345        Sides::new(
346            Some(ansi_color.clone()),
347            Some(ansi_color.clone()),
348            Some(ansi_color.clone()),
349            Some(ansi_color),
350        ),
351    );
352}
353
354/// A colorization of a target [`Object`].
355///
356/// Can be created by [`Colorization::exact`].
357#[derive(Debug, Clone)]
358pub struct ExactColorization<O> {
359    colors: Vec<Color>,
360    target: O,
361}
362
363impl<O> ExactColorization<O> {
364    fn new(colors: Vec<Color>, target: O) -> Self {
365        Self { colors, target }
366    }
367}
368
369impl<R, D, O> TableOption<R, ColoredConfig, D> for ExactColorization<O>
370where
371    O: Object<R>,
372{
373    fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
374        if self.colors.is_empty() {
375            return;
376        }
377
378        let mut color_peek = self.colors.iter().cycle();
379        for pos in self.target.cells(records) {
380            let color = color_peek.next().unwrap();
381            colorize_entity(color, pos, cfg);
382        }
383    }
384}