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}