1 | use 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)] |
40 | pub struct Colorization { |
41 | pattern: ColorizationPattern, |
42 | colors: Vec<Color>, |
43 | } |
44 | |
45 | #[derive (Debug, Clone, PartialEq, Eq)] |
46 | enum ColorizationPattern { |
47 | Column, |
48 | Row, |
49 | ByRow, |
50 | ByColumn, |
51 | Chess, |
52 | } |
53 | |
54 | impl 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 | |
235 | impl<R, D> TableOption<R, ColoredConfig, D> for Colorization |
236 | where |
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: usize = records.count_columns(); |
245 | let count_rows: usize = 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 | |
263 | fn colorize_columns(colors: &[Color], count_columns: usize, cfg: &mut ColoredConfig) { |
264 | for (col: usize, color: &Color) in (0..count_columns).zip(colors.iter().cycle()) { |
265 | colorize_entity(color, pos:Entity::Column(col), cfg); |
266 | } |
267 | } |
268 | |
269 | fn colorize_rows(colors: &[Color], count_rows: usize, cfg: &mut ColoredConfig) { |
270 | for (row: usize, color: &Color) in (0..count_rows).zip(colors.iter().cycle()) { |
271 | colorize_entity(color, pos:Entity::Row(row), cfg); |
272 | } |
273 | } |
274 | |
275 | fn colorize_by_row( |
276 | colors: &[Color], |
277 | count_rows: usize, |
278 | count_columns: usize, |
279 | cfg: &mut ColoredConfig, |
280 | ) { |
281 | let mut color_peek: impl Iterator = colors.iter().cycle(); |
282 | for row: usize in 0..count_rows { |
283 | for col: usize in 0..count_columns { |
284 | let color: &Color = color_peek.next().unwrap(); |
285 | colorize_entity(color, pos:Entity::Cell(row, col), cfg); |
286 | } |
287 | } |
288 | } |
289 | |
290 | fn colorize_by_column( |
291 | colors: &[Color], |
292 | count_rows: usize, |
293 | count_columns: usize, |
294 | cfg: &mut ColoredConfig, |
295 | ) { |
296 | let mut color_peek: impl Iterator = colors.iter().cycle(); |
297 | for col: usize in 0..count_columns { |
298 | for row: usize in 0..count_rows { |
299 | let color: &Color = color_peek.next().unwrap(); |
300 | colorize_entity(color, pos:Entity::Cell(row, col), cfg); |
301 | } |
302 | } |
303 | } |
304 | |
305 | fn 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 | |
339 | fn colorize_entity(color: &Color, pos: Entity, cfg: &mut ColoredConfig) { |
340 | let ansi_color: ANSIBuf = ANSIBuf::from(color.clone()); |
341 | let _ = cfg.set_color(pos, ansi_color.clone()); |
342 | cfg.set_justification_color(entity:pos, color:Some(ansi_color.clone())); |
343 | cfg.set_padding_color( |
344 | entity:pos, |
345 | padding:Sides::new( |
346 | left:Some(ansi_color.clone()), |
347 | right:Some(ansi_color.clone()), |
348 | top:Some(ansi_color.clone()), |
349 | bottom: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)] |
358 | pub struct ExactColorization<O> { |
359 | colors: Vec<Color>, |
360 | target: O, |
361 | } |
362 | |
363 | impl<O> ExactColorization<O> { |
364 | fn new(colors: Vec<Color>, target: O) -> Self { |
365 | Self { colors, target } |
366 | } |
367 | } |
368 | |
369 | impl<R, D, O> TableOption<R, ColoredConfig, D> for ExactColorization<O> |
370 | where |
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: impl Iterator = self.colors.iter().cycle(); |
379 | for pos: Entity in self.target.cells(records) { |
380 | let color: &Color = color_peek.next().unwrap(); |
381 | colorize_entity(color, pos, cfg); |
382 | } |
383 | } |
384 | } |
385 | |