use papergrid::{ color::AnsiColor, config::{Entity, Sides}, }; use crate::{ grid::{ config::ColoredConfig, records::{ExactRecords, Records}, }, settings::{object::Object, Color, TableOption}, }; /// [`Colorization`] sets a color for the whole table data (so it's not include the borders). /// /// You can colorize borders in a different round using [`BorderColor`] or [`RawStyle`] /// /// # Examples /// /// ``` /// use std::iter::FromIterator; /// /// use tabled::builder::Builder; /// use tabled::settings::{style::BorderColor, themes::Colorization, Color, Style}; /// /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]]; /// /// let color1 = Color::FG_BLACK | Color::BG_WHITE; /// let color2 = Color::BG_BLACK | Color::FG_WHITE; /// let color3 = Color::FG_RED | Color::BG_RED; /// /// let mut table = Builder::from_iter(data).build(); /// table /// .with(Colorization::chess(color1, color2)) /// .with(Style::modern()) /// .with(BorderColor::filled(color3)); /// /// println!("{table}"); /// ``` /// /// [`RawStyle`]: crate::settings::style::RawStyle /// [`BorderColor`]: crate::settings::style::BorderColor #[derive(Debug, Clone)] pub struct Colorization { pattern: ColorizationPattern, colors: Vec, } #[derive(Debug, Clone)] enum ColorizationPattern { Column, Row, ByRow, ByColumn, Chess, } impl Colorization { /// Creates a [`Colorization`] with a chess pattern. /// /// ``` /// use std::iter::FromIterator; /// /// use tabled::builder::Builder; /// use tabled::settings::{themes::Colorization, Color, Style}; /// /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]]; /// /// let color1 = Color::FG_BLACK | Color::BG_WHITE; /// let color2 = Color::BG_BLACK | Color::FG_WHITE; /// /// let mut table = Builder::from_iter(data).build(); /// table /// .with(Colorization::chess(color1, color2)) /// .with(Style::empty()); /// /// println!("{table}"); /// ``` pub fn chess(white: Color, black: Color) -> Self { Self::new(vec![white, black], ColorizationPattern::Chess) } /// Creates a [`Colorization`] with a target [`Object`]. /// /// ``` /// use std::iter::FromIterator; /// /// use tabled::builder::Builder; /// use tabled::settings::object::Rows; /// use tabled::settings::{themes::Colorization, Color, Style}; /// /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]]; /// /// let color1 = Color::FG_BLACK | Color::BG_WHITE; /// let color2 = Color::BG_BLACK | Color::FG_WHITE; /// /// let mut table = Builder::from_iter(data).build(); /// table /// .with(Colorization::exact([color1, color2], Rows::first())) /// .with(Style::empty()); /// /// println!("{table}"); /// ``` pub fn exact(colors: I, target: O) -> ExactColorization where I: IntoIterator, I::Item: Into, { let colors = colors.into_iter().map(Into::into).collect(); ExactColorization::new(colors, target) } /// Creates a [`Colorization`] with a pattern which changes row by row. /// /// ``` /// use std::iter::FromIterator; /// /// use tabled::builder::Builder; /// use tabled::settings::object::Rows; /// use tabled::settings::{themes::Colorization, Color, Style}; /// /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]]; /// /// let color1 = Color::FG_BLACK | Color::BG_WHITE; /// let color2 = Color::BG_BLACK | Color::FG_WHITE; /// /// let mut table = Builder::from_iter(data).build(); /// table /// .with(Colorization::rows([color1, color2])) /// .with(Style::empty()); /// /// println!("{table}"); /// ``` pub fn rows(colors: I) -> Self where I: IntoIterator, I::Item: Into, { Self::new(colors, ColorizationPattern::Row) } /// Creates a [`Colorization`] with a pattern which changes column by column. /// /// ``` /// use std::iter::FromIterator; /// /// use tabled::builder::Builder; /// use tabled::settings::object::Rows; /// use tabled::settings::{themes::Colorization, Color, Style}; /// /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]]; /// /// let color1 = Color::FG_BLACK | Color::BG_WHITE; /// let color2 = Color::BG_BLACK | Color::FG_WHITE; /// /// let mut table = Builder::from_iter(data).build(); /// table /// .with(Colorization::columns([color1, color2])) /// .with(Style::empty()); /// /// println!("{table}"); /// ``` pub fn columns(colors: I) -> Self where I: IntoIterator, I::Item: Into, { Self::new(colors, ColorizationPattern::Column) } /// Creates a [`Colorization`] with a pattern which peaks cells one by one iterating over rows. /// /// ``` /// use std::iter::FromIterator; /// /// use tabled::builder::Builder; /// use tabled::settings::object::Rows; /// use tabled::settings::{themes::Colorization, Color, Style}; /// /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]]; /// /// let color1 = Color::FG_BLACK | Color::BG_WHITE; /// let color2 = Color::BG_BLACK | Color::FG_WHITE; /// /// let mut table = Builder::from_iter(data).build(); /// table /// .with(Colorization::by_row([color1, color2])) /// .with(Style::empty()); /// /// println!("{table}"); /// ``` pub fn by_row(colors: I) -> Self where I: IntoIterator, I::Item: Into, { Self::new(colors, ColorizationPattern::ByRow) } /// Creates a [`Colorization`] with a pattern which peaks cells one by one iterating over columns. /// /// ``` /// use std::iter::FromIterator; /// /// use tabled::builder::Builder; /// use tabled::settings::object::Rows; /// use tabled::settings::{themes::Colorization, Color, Style}; /// /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]]; /// /// let color1 = Color::FG_BLACK | Color::BG_WHITE; /// let color2 = Color::BG_BLACK | Color::FG_WHITE; /// /// let mut table = Builder::from_iter(data).build(); /// table /// .with(Colorization::by_column([color1, color2])) /// .with(Style::empty()); /// /// println!("{table}"); /// ``` pub fn by_column(colors: I) -> Self where I: IntoIterator, I::Item: Into, { Self::new(colors, ColorizationPattern::ByColumn) } fn new(colors: I, pattern: ColorizationPattern) -> Self where I: IntoIterator, I::Item: Into, { let colors = colors.into_iter().map(Into::into).collect(); Self { colors, pattern } } } impl TableOption for Colorization where R: Records + ExactRecords, { fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) { if self.colors.is_empty() { return; } let count_columns = records.count_columns(); let count_rows = records.count_rows(); match self.pattern { ColorizationPattern::Column => colorize_columns(&self.colors, count_columns, cfg), ColorizationPattern::Row => colorize_rows(&self.colors, count_rows, cfg), ColorizationPattern::ByRow => { colorize_by_row(&self.colors, count_rows, count_columns, cfg) } ColorizationPattern::ByColumn => { colorize_by_column(&self.colors, count_rows, count_columns, cfg) } ColorizationPattern::Chess => { colorize_diogonals(&self.colors, count_rows, count_columns, cfg) } } } } fn colorize_columns(colors: &[Color], count_columns: usize, cfg: &mut ColoredConfig) { for (col, color) in (0..count_columns).zip(colors.iter().cycle()) { colorize_entity(color, Entity::Column(col), cfg); } } fn colorize_rows(colors: &[Color], count_rows: usize, cfg: &mut ColoredConfig) { for (row, color) in (0..count_rows).zip(colors.iter().cycle()) { colorize_entity(color, Entity::Row(row), cfg); } } fn colorize_by_row( colors: &[Color], count_rows: usize, count_columns: usize, cfg: &mut ColoredConfig, ) { let mut color_peek = colors.iter().cycle(); for row in 0..count_rows { for col in 0..count_columns { let color = color_peek.next().unwrap(); colorize_entity(color, Entity::Cell(row, col), cfg); } } } fn colorize_by_column( colors: &[Color], count_rows: usize, count_columns: usize, cfg: &mut ColoredConfig, ) { let mut color_peek = colors.iter().cycle(); for col in 0..count_columns { for row in 0..count_rows { let color = color_peek.next().unwrap(); colorize_entity(color, Entity::Cell(row, col), cfg); } } } fn colorize_diogonals( colors: &[Color], count_rows: usize, count_columns: usize, cfg: &mut ColoredConfig, ) { let mut color_peek = colors.iter().cycle(); for mut row in 0..count_rows { let color = color_peek.next().unwrap(); for col in 0..count_columns { colorize_entity(color, Entity::Cell(row, col), cfg); row += 1; if row == count_rows { break; } } } let _ = color_peek.next().unwrap(); for mut col in 1..count_columns { let color = color_peek.next().unwrap(); for row in 0..count_rows { colorize_entity(color, Entity::Cell(row, col), cfg); col += 1; if col == count_columns { break; } } } } fn colorize_entity(color: &Color, pos: Entity, cfg: &mut ColoredConfig) { let ansi_color = AnsiColor::from(color.clone()); let _ = cfg.set_color(pos, ansi_color.clone()); cfg.set_justification_color(pos, Some(ansi_color.clone())); cfg.set_padding_color( pos, Sides::new( Some(ansi_color.clone()), Some(ansi_color.clone()), Some(ansi_color.clone()), Some(ansi_color), ), ); } /// A colorization of a target [`Object`]. /// /// Can be created by [`Colorization::exact`]. #[derive(Debug, Clone)] pub struct ExactColorization { colors: Vec, target: O, } impl ExactColorization { fn new(colors: Vec, target: O) -> Self { Self { colors, target } } } impl TableOption for ExactColorization where O: Object, { fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) { if self.colors.is_empty() { return; } let mut color_peek = self.colors.iter().cycle(); for pos in self.target.cells(records) { let color = color_peek.next().unwrap(); colorize_entity(color, pos, cfg); } } }