1 | use papergrid::{ |
2 | color::AnsiColor, |
3 | config::{Entity, Sides}, |
4 | }; |
5 | |
6 | use 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)] |
44 | pub struct Colorization { |
45 | pattern: ColorizationPattern, |
46 | colors: Vec<Color>, |
47 | } |
48 | |
49 | #[derive (Debug, Clone)] |
50 | enum ColorizationPattern { |
51 | Column, |
52 | Row, |
53 | ByRow, |
54 | ByColumn, |
55 | Chess, |
56 | } |
57 | |
58 | impl 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 | |
239 | impl<R, D> TableOption<R, D, ColoredConfig> for Colorization |
240 | where |
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 | |
267 | fn 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 | |
273 | fn 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 | |
279 | fn 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 | |
294 | fn 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 | |
309 | fn 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 | |
343 | fn 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)] |
362 | pub struct ExactColorization<O> { |
363 | colors: Vec<Color>, |
364 | target: O, |
365 | } |
366 | |
367 | impl<O> ExactColorization<O> { |
368 | fn new(colors: Vec<Color>, target: O) -> Self { |
369 | Self { colors, target } |
370 | } |
371 | } |
372 | |
373 | impl<R, D, O> TableOption<R, D, ColoredConfig> for ExactColorization<O> |
374 | where |
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 | |