blob: 41c72133081af46c65c25ae43a9c542e744c3e22 [file] [log] [blame]
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<Color>,
}
#[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<I, O>(colors: I, target: O) -> ExactColorization<O>
where
I: IntoIterator,
I::Item: Into<Color>,
{
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<I>(colors: I) -> Self
where
I: IntoIterator,
I::Item: Into<Color>,
{
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<I>(colors: I) -> Self
where
I: IntoIterator,
I::Item: Into<Color>,
{
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<I>(colors: I) -> Self
where
I: IntoIterator,
I::Item: Into<Color>,
{
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<I>(colors: I) -> Self
where
I: IntoIterator,
I::Item: Into<Color>,
{
Self::new(colors, ColorizationPattern::ByColumn)
}
fn new<I>(colors: I, pattern: ColorizationPattern) -> Self
where
I: IntoIterator,
I::Item: Into<Color>,
{
let colors = colors.into_iter().map(Into::into).collect();
Self { colors, pattern }
}
}
impl<R, D> TableOption<R, D, ColoredConfig> 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<O> {
colors: Vec<Color>,
target: O,
}
impl<O> ExactColorization<O> {
fn new(colors: Vec<Color>, target: O) -> Self {
Self { colors, target }
}
}
impl<R, D, O> TableOption<R, D, ColoredConfig> for ExactColorization<O>
where
O: Object<R>,
{
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);
}
}
}