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