From ef24de24a82fe681581cc130f342363c47c0969a Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 7 Jun 2024 07:48:48 +0200 Subject: Merging upstream version 1.75.0+dfsg1. Signed-off-by: Daniel Baumann --- vendor/tabled/src/settings/themes/colorization.rs | 388 ++++++++++++++++++++++ vendor/tabled/src/settings/themes/column_names.rs | 355 ++++++++++++++++++++ vendor/tabled/src/settings/themes/mod.rs | 9 + 3 files changed, 752 insertions(+) create mode 100644 vendor/tabled/src/settings/themes/colorization.rs create mode 100644 vendor/tabled/src/settings/themes/column_names.rs create mode 100644 vendor/tabled/src/settings/themes/mod.rs (limited to 'vendor/tabled/src/settings/themes') diff --git a/vendor/tabled/src/settings/themes/colorization.rs b/vendor/tabled/src/settings/themes/colorization.rs new file mode 100644 index 000000000..41c721330 --- /dev/null +++ b/vendor/tabled/src/settings/themes/colorization.rs @@ -0,0 +1,388 @@ +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); + } + } +} diff --git a/vendor/tabled/src/settings/themes/column_names.rs b/vendor/tabled/src/settings/themes/column_names.rs new file mode 100644 index 000000000..02848166d --- /dev/null +++ b/vendor/tabled/src/settings/themes/column_names.rs @@ -0,0 +1,355 @@ +use crate::{ + grid::{ + config::{AlignmentHorizontal, ColoredConfig}, + dimension::{CompleteDimensionVecRecords, Dimension, Estimate}, + records::{ + vec_records::{CellInfo, VecRecords}, + ExactRecords, PeekableRecords, Records, Resizable, + }, + util::string::string_width, + }, + settings::{ + style::{BorderText, Offset}, + Color, TableOption, + }, +}; + +/// [`ColumnNames`] sets strings on horizontal lines for the columns. +/// +/// Notice that using a [`Default`] would reuse a names from the first row. +/// +/// # Examples +/// +/// ``` +/// use std::iter::FromIterator; +/// use tabled::{Table, settings::themes::ColumnNames}; +/// +/// let data = vec![ +/// vec!["Hello", "World"], +/// vec!["Hello", "World"], +/// ]; +/// +/// let mut table = Table::from_iter(data); +/// table.with(ColumnNames::new(["head1", "head2"]).set_offset(3).set_line(2)); +/// +/// assert_eq!( +/// table.to_string(), +/// "+--------+--------+\n\ +/// | Hello | World |\n\ +/// +--------+--------+\n\ +/// | Hello | World |\n\ +/// +---head1+---head2+" +/// ); +/// ``` +/// +/// [`Default`] usage. +/// +/// ``` +/// use std::iter::FromIterator; +/// use tabled::{Table, settings::themes::ColumnNames}; +/// +/// let data = vec![ +/// vec!["Hello", "World"], +/// vec!["Hello", "World"], +/// ]; +/// +/// let mut table = Table::from_iter(data); +/// table.with(ColumnNames::default()); +/// +/// assert_eq!( +/// table.to_string(), +/// "+Hello--+World--+\n\ +/// | Hello | World |\n\ +/// +-------+-------+" +/// ); +/// ``` +#[derive(Debug, Clone)] +pub struct ColumnNames { + names: Option>, + colors: Vec>, + offset: usize, + line: usize, + alignment: AlignmentHorizontal, +} + +impl Default for ColumnNames { + fn default() -> Self { + Self { + names: Default::default(), + colors: Default::default(), + offset: Default::default(), + line: Default::default(), + alignment: AlignmentHorizontal::Left, + } + } +} + +impl ColumnNames { + /// Creates a [`ColumnNames`] with a given names. + /// + /// Using a [`Default`] would reuse a names from the first row. + /// + /// # Example + /// + /// ``` + /// use std::iter::FromIterator; + /// use tabled::{Table, settings::themes::ColumnNames}; + /// + /// let mut table = Table::from_iter(vec![vec!["Hello", "World"]]); + /// table.with(ColumnNames::new(["head1", "head2"])); + /// + /// assert_eq!( + /// table.to_string(), + /// "+head1--+head2--+\n\ + /// | Hello | World |\n\ + /// +-------+-------+" + /// ); + /// ``` + pub fn new(names: I) -> Self + where + I: IntoIterator, + I::Item: Into, + { + let names = names.into_iter().map(Into::into).collect::>(); + Self { + names: Some(names), + colors: Vec::new(), + offset: 0, + line: 0, + alignment: AlignmentHorizontal::Left, + } + } + + /// Set color for the column names. + /// + /// By default there's no colors. + /// + /// # Example + /// + /// ``` + /// use std::iter::FromIterator; + /// use tabled::Table; + /// use tabled::settings::{Color, themes::ColumnNames}; + /// + /// let mut table = Table::from_iter(vec![vec!["Hello", "World"]]); + /// table.with(ColumnNames::new(["head1", "head2"]).set_colors([Color::FG_RED])); + /// + /// assert_eq!( + /// table.to_string(), + /// "+\u{1b}[31mh\u{1b}[39m\u{1b}[31me\u{1b}[39m\u{1b}[31ma\u{1b}[39m\u{1b}[31md\u{1b}[39m\u{1b}[31m1\u{1b}[39m--+head2--+\n\ + /// | Hello | World |\n\ + /// +-------+-------+" + /// ); + /// ``` + pub fn set_colors(self, colors: I) -> Self + where + I: IntoIterator, + I::Item: Into>, + { + let colors = colors.into_iter().map(Into::into).collect::>(); + Self { + names: self.names, + offset: self.offset, + line: self.line, + alignment: self.alignment, + colors, + } + } + + /// Set a left offset after which the names will be used. + /// + /// By default there's no offset. + /// + /// # Example + /// + /// ``` + /// use std::iter::FromIterator; + /// use tabled::{Table, settings::themes::ColumnNames}; + /// + /// let mut table = Table::from_iter(vec![vec!["Hello", "World"]]); + /// table.with(ColumnNames::new(["head1", "head2"]).set_offset(1)); + /// + /// assert_eq!( + /// table.to_string(), + /// "+-head1-+-head2-+\n\ + /// | Hello | World |\n\ + /// +-------+-------+" + /// ); + /// ``` + pub fn set_offset(self, i: usize) -> Self { + Self { + names: self.names, + colors: self.colors, + line: self.line, + alignment: self.alignment, + offset: i, + } + } + + /// Set a horizontal line the names will be applied to. + /// + /// The default value is 0 (the top horizontal line). + /// + /// # Example + /// + /// ``` + /// use std::iter::FromIterator; + /// use tabled::{Table, settings::themes::ColumnNames}; + /// + /// let mut table = Table::from_iter(vec![vec!["Hello", "World"]]); + /// table.with(ColumnNames::new(["head1", "head2"]).set_line(1)); + /// + /// assert_eq!( + /// table.to_string(), + /// "+-------+-------+\n\ + /// | Hello | World |\n\ + /// +head1--+head2--+" + /// ); + /// ``` + pub fn set_line(self, i: usize) -> Self { + Self { + names: self.names, + colors: self.colors, + offset: self.offset, + alignment: self.alignment, + line: i, + } + } + + /// Set an alignment for the names. + /// + /// By default it's left aligned. + /// + /// # Example + /// + /// ``` + /// use std::iter::FromIterator; + /// use tabled::{ + /// Table, + /// settings::themes::ColumnNames, + /// grid::config::AlignmentHorizontal, + /// }; + /// + /// let mut table = Table::from_iter(vec![vec!["Hello", "World"]]); + /// table.with(ColumnNames::new(["head1", "head2"]).set_alignment(AlignmentHorizontal::Right)); + /// + /// assert_eq!( + /// table.to_string(), + /// "+--head1+--head2+\n\ + /// | Hello | World |\n\ + /// +-------+-------+" + /// ); + /// ``` + pub fn set_alignment(self, alignment: AlignmentHorizontal) -> Self { + Self { + names: self.names, + colors: self.colors, + offset: self.offset, + line: self.line, + alignment, + } + } +} + +impl TableOption>, CompleteDimensionVecRecords<'static>, ColoredConfig> + for ColumnNames +{ + fn change( + self, + records: &mut VecRecords>, + cfg: &mut ColoredConfig, + dims: &mut CompleteDimensionVecRecords<'static>, + ) { + let names = match self.names { + Some(names) => names, + None => { + if records.count_rows() == 0 || records.count_columns() == 0 { + return; + } + + let names = (0..records.count_columns()) + .map(|column| records.get_text((0, column))) + .map(ToString::to_string) + .collect::>(); + + records.remove_row(0); + + names + } + }; + + let names = names.iter().map(|name| name.lines().next().unwrap_or("")); + + dims.estimate(&*records, cfg); + + let mut widths = (0..records.count_columns()) + .map(|column| dims.get_width(column)) + .collect::>(); + + let names = names.take(widths.len()); + + let offset = self.offset; + widths + .iter_mut() + .zip(names.clone()) + .for_each(|(width, text)| { + let name_width = string_width(text) + offset; + *width = std::cmp::max(name_width, *width); + }); + + let _ = dims.set_widths(widths.clone()); + + let mut total_width = 0; + for (i, (width, name)) in widths.iter().zip(names).enumerate() { + let color = get_color(&self.colors, i); + let offset = total_width + 1; + let btext = get_border_text( + name, + offset, + *width, + self.alignment, + self.offset, + self.line, + color, + ); + btext.change(records, cfg, dims); + + total_width += width + 1; + } + } +} + +fn get_border_text( + text: &str, + offset: usize, + available: usize, + alignment: AlignmentHorizontal, + alignment_offset: usize, + line: usize, + color: Option<&Color>, +) -> BorderText { + let name = text.to_string(); + let left_indent = get_indent(text, alignment, alignment_offset, available); + let left_offset = Offset::Begin(offset + left_indent); + let mut btext = BorderText::new(name).horizontal(line).offset(left_offset); + if let Some(color) = color { + btext = btext.color(color.clone()); + } + + btext +} + +fn get_color(colors: &[Option], i: usize) -> Option<&Color> { + colors.get(i).and_then(|color| match color { + Some(color) => Some(color), + None => None, + }) +} + +fn get_indent(text: &str, align: AlignmentHorizontal, offset: usize, available: usize) -> usize { + match align { + AlignmentHorizontal::Left => offset, + AlignmentHorizontal::Right => available - string_width(text) - offset, + AlignmentHorizontal::Center => (available - string_width(text)) / 2, + } +} diff --git a/vendor/tabled/src/settings/themes/mod.rs b/vendor/tabled/src/settings/themes/mod.rs new file mode 100644 index 000000000..75cf458cc --- /dev/null +++ b/vendor/tabled/src/settings/themes/mod.rs @@ -0,0 +1,9 @@ +//! The module contains a varieity of configurations of table, which often +//! changes not a single setting. +//! As such they are making relatively big changes to the configuration. + +mod colorization; +mod column_names; + +pub use colorization::{Colorization, ExactColorization}; +pub use column_names::ColumnNames; -- cgit v1.2.3