summaryrefslogtreecommitdiffstats
path: root/vendor/tabled/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/tabled/src')
-rw-r--r--vendor/tabled/src/builder/index_builder.rs318
-rw-r--r--vendor/tabled/src/builder/mod.rs118
-rw-r--r--vendor/tabled/src/builder/table_builder.rs506
-rw-r--r--vendor/tabled/src/grid/colored_config.rs112
-rw-r--r--vendor/tabled/src/grid/compact_multiline_config.rs211
-rw-r--r--vendor/tabled/src/grid/dimension/complete_dimension.rs136
-rw-r--r--vendor/tabled/src/grid/dimension/complete_dimension_vec_records.rs128
-rw-r--r--vendor/tabled/src/grid/dimension/const_dimension.rs70
-rw-r--r--vendor/tabled/src/grid/dimension/mod.rs32
-rw-r--r--vendor/tabled/src/grid/dimension/peekable_dimension.rs335
-rw-r--r--vendor/tabled/src/grid/dimension/pool_table_dimension.rs36
-rw-r--r--vendor/tabled/src/grid/dimension/static_dimension.rs63
-rw-r--r--vendor/tabled/src/grid/mod.rs48
-rw-r--r--vendor/tabled/src/grid/records/empty_records.rs41
-rw-r--r--vendor/tabled/src/grid/records/into_records/buf_records.rs213
-rw-r--r--vendor/tabled/src/grid/records/into_records/either_string.rs22
-rw-r--r--vendor/tabled/src/grid/records/into_records/limit_column_records.rs82
-rw-r--r--vendor/tabled/src/grid/records/into_records/limit_row_records.rs59
-rw-r--r--vendor/tabled/src/grid/records/into_records/mod.rs26
-rw-r--r--vendor/tabled/src/grid/records/into_records/truncate_records.rs136
-rw-r--r--vendor/tabled/src/grid/records/mod.rs19
-rw-r--r--vendor/tabled/src/grid/records/records_mut.rs34
-rw-r--r--vendor/tabled/src/grid/records/resizable.rs217
-rw-r--r--vendor/tabled/src/lib.rs490
-rw-r--r--vendor/tabled/src/macros/col.rs50
-rw-r--r--vendor/tabled/src/macros/mod.rs6
-rw-r--r--vendor/tabled/src/macros/row.rs44
-rw-r--r--vendor/tabled/src/settings/alignment/mod.rs189
-rw-r--r--vendor/tabled/src/settings/cell_option.rs55
-rw-r--r--vendor/tabled/src/settings/color/mod.rs346
-rw-r--r--vendor/tabled/src/settings/concat/mod.rs171
-rw-r--r--vendor/tabled/src/settings/disable/mod.rs196
-rw-r--r--vendor/tabled/src/settings/duplicate/mod.rs150
-rw-r--r--vendor/tabled/src/settings/extract/mod.rs257
-rw-r--r--vendor/tabled/src/settings/format/format_config.rs14
-rw-r--r--vendor/tabled/src/settings/format/format_content.rs86
-rw-r--r--vendor/tabled/src/settings/format/format_positioned.rs51
-rw-r--r--vendor/tabled/src/settings/format/mod.rs144
-rw-r--r--vendor/tabled/src/settings/formatting/alignment_strategy.rs172
-rw-r--r--vendor/tabled/src/settings/formatting/charset.rs108
-rw-r--r--vendor/tabled/src/settings/formatting/justification.rs127
-rw-r--r--vendor/tabled/src/settings/formatting/mod.rs20
-rw-r--r--vendor/tabled/src/settings/formatting/tab_size.rs62
-rw-r--r--vendor/tabled/src/settings/formatting/trim_strategy.rs118
-rw-r--r--vendor/tabled/src/settings/height/cell_height_increase.rs98
-rw-r--r--vendor/tabled/src/settings/height/cell_height_limit.rs103
-rw-r--r--vendor/tabled/src/settings/height/height_list.rs47
-rw-r--r--vendor/tabled/src/settings/height/mod.rs228
-rw-r--r--vendor/tabled/src/settings/height/table_height_increase.rs90
-rw-r--r--vendor/tabled/src/settings/height/table_height_limit.rs123
-rw-r--r--vendor/tabled/src/settings/height/util.rs22
-rw-r--r--vendor/tabled/src/settings/highlight/mod.rs452
-rw-r--r--vendor/tabled/src/settings/locator/mod.rs202
-rw-r--r--vendor/tabled/src/settings/margin/mod.rs137
-rw-r--r--vendor/tabled/src/settings/measurement/mod.rs161
-rw-r--r--vendor/tabled/src/settings/merge/mod.rs196
-rw-r--r--vendor/tabled/src/settings/mod.rs135
-rw-r--r--vendor/tabled/src/settings/modify.rs81
-rw-r--r--vendor/tabled/src/settings/object/cell.rs70
-rw-r--r--vendor/tabled/src/settings/object/columns.rs209
-rw-r--r--vendor/tabled/src/settings/object/frame.rs69
-rw-r--r--vendor/tabled/src/settings/object/mod.rs765
-rw-r--r--vendor/tabled/src/settings/object/rows.rs213
-rw-r--r--vendor/tabled/src/settings/object/segment.rs151
-rw-r--r--vendor/tabled/src/settings/object/util.rs22
-rw-r--r--vendor/tabled/src/settings/padding/mod.rs164
-rw-r--r--vendor/tabled/src/settings/panel/footer.rs32
-rw-r--r--vendor/tabled/src/settings/panel/header.rs32
-rw-r--r--vendor/tabled/src/settings/panel/horizontal_panel.rs80
-rw-r--r--vendor/tabled/src/settings/panel/mod.rs127
-rw-r--r--vendor/tabled/src/settings/panel/vertical_panel.rs83
-rw-r--r--vendor/tabled/src/settings/peaker/mod.rs94
-rw-r--r--vendor/tabled/src/settings/rotate/mod.rs155
-rw-r--r--vendor/tabled/src/settings/settings_list.rs71
-rw-r--r--vendor/tabled/src/settings/shadow/mod.rs195
-rw-r--r--vendor/tabled/src/settings/span/column.rs125
-rw-r--r--vendor/tabled/src/settings/span/mod.rs68
-rw-r--r--vendor/tabled/src/settings/span/row.rs126
-rw-r--r--vendor/tabled/src/settings/split/mod.rs420
-rw-r--r--vendor/tabled/src/settings/style/border.rs159
-rw-r--r--vendor/tabled/src/settings/style/border_char.rs99
-rw-r--r--vendor/tabled/src/settings/style/border_color.rs155
-rw-r--r--vendor/tabled/src/settings/style/border_text.rs253
-rw-r--r--vendor/tabled/src/settings/style/builder.rs1208
-rw-r--r--vendor/tabled/src/settings/style/horizontal_line.rs69
-rw-r--r--vendor/tabled/src/settings/style/line.rs91
-rw-r--r--vendor/tabled/src/settings/style/mod.rs127
-rw-r--r--vendor/tabled/src/settings/style/offset.rs19
-rw-r--r--vendor/tabled/src/settings/style/raw_style.rs438
-rw-r--r--vendor/tabled/src/settings/style/span_border_correction.rs216
-rw-r--r--vendor/tabled/src/settings/style/vertical_line.rs50
-rw-r--r--vendor/tabled/src/settings/table_option.rs30
-rw-r--r--vendor/tabled/src/settings/themes/colorization.rs388
-rw-r--r--vendor/tabled/src/settings/themes/column_names.rs355
-rw-r--r--vendor/tabled/src/settings/themes/mod.rs9
-rw-r--r--vendor/tabled/src/settings/width/justify.rs96
-rw-r--r--vendor/tabled/src/settings/width/min_width.rs205
-rw-r--r--vendor/tabled/src/settings/width/mod.rs163
-rw-r--r--vendor/tabled/src/settings/width/truncate.rs505
-rw-r--r--vendor/tabled/src/settings/width/util.rs265
-rw-r--r--vendor/tabled/src/settings/width/width_list.rs50
-rw-r--r--vendor/tabled/src/settings/width/wrap.rs1468
-rw-r--r--vendor/tabled/src/tabled.rs150
-rw-r--r--vendor/tabled/src/tables/compact.rs309
-rw-r--r--vendor/tabled/src/tables/extended.rs338
-rw-r--r--vendor/tabled/src/tables/iter.rs344
-rw-r--r--vendor/tabled/src/tables/mod.rs48
-rw-r--r--vendor/tabled/src/tables/table.rs464
-rw-r--r--vendor/tabled/src/tables/table_pool.rs1607
-rw-r--r--vendor/tabled/src/tables/util/mod.rs2
-rw-r--r--vendor/tabled/src/tables/util/utf8_writer.rs29
111 files changed, 20873 insertions, 0 deletions
diff --git a/vendor/tabled/src/builder/index_builder.rs b/vendor/tabled/src/builder/index_builder.rs
new file mode 100644
index 000000000..081f580df
--- /dev/null
+++ b/vendor/tabled/src/builder/index_builder.rs
@@ -0,0 +1,318 @@
+use crate::Table;
+
+use super::Builder;
+
+/// [`IndexBuilder`] helps to add an index to the table.
+///
+/// Index is a column on the left of the table.
+///
+/// It also can be used to transpose the table.
+///
+/// Creates a new [`IndexBuilder`] instance.
+///
+/// It creates a default index a range from 0 to N. (N - count rows)
+/// It also sets a default columns to the range 0 .. N (N - count columns).
+///nfo<'a>
+/// # Example
+///
+/// ```
+/// use tabled::builder::Builder;
+///
+/// let mut builder = Builder::default();
+/// builder.set_header(["i", "col-1", "col-2"]);
+/// builder.push_record(["0", "value-1", "value-2"]);
+///
+/// let table = builder.index().build().to_string();
+///
+/// assert_eq!(
+/// table,
+/// "+---+---+---------+---------+\n\
+/// | | i | col-1 | col-2 |\n\
+/// +---+---+---------+---------+\n\
+/// | 0 | 0 | value-1 | value-2 |\n\
+/// +---+---+---------+---------+"
+/// )
+/// ```
+///
+/// # Example
+///
+/// ```
+/// use tabled::builder::Builder;
+///
+/// let table = Builder::default()
+/// .index()
+/// .build();
+/// ```
+#[derive(Debug, Clone)]
+pub struct IndexBuilder {
+ /// Index is an index data.
+ /// It's always set.
+ index: Vec<String>,
+ /// Name of an index
+ name: Option<String>,
+ /// A flag which checks if we need to actually use index.
+ ///
+ /// It might happen when it's only necessary to [`Self::transpose`] table.
+ print_index: bool,
+ /// A flag which checks if table was transposed.
+ transposed: bool,
+ /// Data originated in [`Builder`].
+ data: Vec<Vec<String>>,
+}
+
+impl IndexBuilder {
+ /// No flag makes builder to not use an index.
+ ///
+ /// It may be useful when only [`Self::transpose`] need to be used.
+ ///
+ /// ```
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::default();
+ /// builder.set_header(["i", "col-1", "col-2"]);
+ /// builder.push_record(["0", "value-1", "value-2"]);
+ /// builder.push_record(["2", "value-3", "value-4"]);
+ ///
+ /// let table = builder.index().hide().build().to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+---+---------+---------+\n\
+ /// | i | col-1 | col-2 |\n\
+ /// +---+---------+---------+\n\
+ /// | 0 | value-1 | value-2 |\n\
+ /// +---+---------+---------+\n\
+ /// | 2 | value-3 | value-4 |\n\
+ /// +---+---------+---------+"
+ /// )
+ /// ```
+ pub fn hide(mut self) -> Self {
+ self.print_index = false;
+ self
+ }
+
+ /// Set an index name.
+ ///
+ /// When [`None`] the name won't be used.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::default();
+ /// builder.set_header(["i", "column1", "column2"]);
+ /// builder.push_record(["0", "value1", "value2"]);
+ ///
+ /// let table = builder.index()
+ /// .column(1)
+ /// .name(Some(String::from("index")))
+ /// .build();
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+--------+---+---------+\n\
+ /// | | i | column2 |\n\
+ /// +--------+---+---------+\n\
+ /// | index | | |\n\
+ /// +--------+---+---------+\n\
+ /// | value1 | 0 | value2 |\n\
+ /// +--------+---+---------+"
+ /// )
+ /// ```
+ pub fn name(mut self, name: Option<String>) -> Self {
+ self.name = name;
+ self
+ }
+
+ /// Sets a index to the chosen column.
+ ///
+ /// Also sets a name of the index to the column name.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::default();
+ /// builder.set_header(["i", "column1", "column2"]);
+ /// builder.push_record(["0", "value1", "value2"]);
+ ///
+ /// let table = builder.index().column(1).build();
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+---------+---+---------+\n\
+ /// | | i | column2 |\n\
+ /// +---------+---+---------+\n\
+ /// | column1 | | |\n\
+ /// +---------+---+---------+\n\
+ /// | value1 | 0 | value2 |\n\
+ /// +---------+---+---------+"
+ /// )
+ /// ```
+ pub fn column(mut self, column: usize) -> Self {
+ if column >= matrix_count_columns(&self.data) {
+ return self;
+ }
+
+ self.index = get_column(&mut self.data, column);
+
+ let name = remove_or_default(&mut self.index, 0);
+ self.name = Some(name);
+
+ self
+ }
+
+ /// Transpose index and columns.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::default();
+ /// builder.set_header(["i", "column-1", "column-2", "column-3"]);
+ /// builder.push_record(["0", "value-1", "value-2", "value-3"]);
+ /// builder.push_record(["1", "value-4", "value-5", "value-6"]);
+ /// builder.push_record(["2", "value-7", "value-8", "value-9"]);
+ ///
+ /// let table = builder.index().column(1).transpose().build();
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+----------+---------+---------+---------+\n\
+ /// | column-1 | value-1 | value-4 | value-7 |\n\
+ /// +----------+---------+---------+---------+\n\
+ /// | i | 0 | 1 | 2 |\n\
+ /// +----------+---------+---------+---------+\n\
+ /// | column-2 | value-2 | value-5 | value-8 |\n\
+ /// +----------+---------+---------+---------+\n\
+ /// | column-3 | value-3 | value-6 | value-9 |\n\
+ /// +----------+---------+---------+---------+"
+ /// )
+ /// ```
+ pub fn transpose(mut self) -> Self {
+ let columns = &mut self.data[0];
+ std::mem::swap(&mut self.index, columns);
+
+ let columns = self.data.remove(0);
+
+ make_rows_columns(&mut self.data);
+
+ self.data.insert(0, columns);
+
+ self.transposed = !self.transposed;
+
+ self
+ }
+
+ /// Builds a table.
+ pub fn build(self) -> Table {
+ let builder: Builder = self.into();
+ builder.build()
+ }
+}
+
+impl From<Builder> for IndexBuilder {
+ fn from(builder: Builder) -> Self {
+ let has_header = builder.has_header();
+
+ let mut data: Vec<Vec<_>> = builder.into();
+
+ if !has_header {
+ let count_columns = matrix_count_columns(&data);
+ data.insert(0, build_range_index(count_columns));
+ }
+
+ // we exclude first row which contains a header
+ let data_len = data.len().saturating_sub(1);
+ let index = build_range_index(data_len);
+
+ Self {
+ index,
+ name: None,
+ print_index: true,
+ transposed: false,
+ data,
+ }
+ }
+}
+
+impl From<IndexBuilder> for Builder {
+ fn from(b: IndexBuilder) -> Self {
+ build_index(b)
+ }
+}
+
+fn build_index(mut b: IndexBuilder) -> Builder {
+ if b.index.is_empty() {
+ return Builder::default();
+ }
+
+ // add index column
+ if b.print_index {
+ b.index.insert(0, String::default());
+
+ insert_column(&mut b.data, b.index, 0);
+ }
+
+ if let Some(name) = b.name {
+ if b.transposed && b.print_index {
+ b.data[0][0] = name;
+ } else {
+ b.data.insert(1, vec![name]);
+ }
+ }
+
+ Builder::from(b.data)
+}
+
+fn build_range_index(n: usize) -> Vec<String> {
+ (0..n).map(|i| i.to_string()).collect()
+}
+
+fn remove_or_default<T: Default>(v: &mut Vec<T>, i: usize) -> T {
+ if v.len() > i {
+ v.remove(i)
+ } else {
+ T::default()
+ }
+}
+
+fn get_column<T: Default>(v: &mut [Vec<T>], col: usize) -> Vec<T> {
+ let mut column = Vec::with_capacity(v.len());
+ for row in v.iter_mut() {
+ let value = remove_or_default(row, col);
+ column.push(value);
+ }
+
+ column
+}
+
+fn make_rows_columns<T: Default>(v: &mut Vec<Vec<T>>) {
+ let count_columns = matrix_count_columns(v);
+
+ let mut columns = Vec::with_capacity(count_columns);
+ for _ in 0..count_columns {
+ let column = get_column(v, 0);
+ columns.push(column);
+ }
+
+ v.clear();
+
+ for column in columns {
+ v.push(column);
+ }
+}
+
+fn insert_column<T: Default>(v: &mut [Vec<T>], mut column: Vec<T>, col: usize) {
+ for row in v.iter_mut() {
+ let value = remove_or_default(&mut column, col);
+ row.insert(col, value);
+ }
+}
+
+fn matrix_count_columns<T>(v: &[Vec<T>]) -> usize {
+ v.first().map_or(0, |row| row.len())
+}
diff --git a/vendor/tabled/src/builder/mod.rs b/vendor/tabled/src/builder/mod.rs
new file mode 100644
index 000000000..9002ba237
--- /dev/null
+++ b/vendor/tabled/src/builder/mod.rs
@@ -0,0 +1,118 @@
+//! Builder module provides a [`Builder`] type which helps building
+//! a [`Table`] dynamically.
+//!
+//! It also contains [`IndexBuilder`] which can help to build a table with index.
+//!
+//! # Examples
+//!
+//! Here's an example of [`IndexBuilder`] usage
+//!
+#![cfg_attr(feature = "derive", doc = "```")]
+#![cfg_attr(not(feature = "derive"), doc = "```ignore")]
+//! use tabled::{Table, Tabled, settings::Style};
+//!
+//! #[derive(Tabled)]
+//! struct Mission {
+//! name: &'static str,
+//! #[tabled(inline)]
+//! status: Status,
+//! }
+//!
+//! #[derive(Tabled)]
+//! enum Status {
+//! Complete,
+//! Started,
+//! Ready,
+//! Unknown,
+//! }
+//!
+//! let data = [
+//! Mission { name: "Algebra", status: Status::Unknown },
+//! Mission { name: "Apolo", status: Status::Complete },
+//! ];
+//!
+//! let mut builder = Table::builder(&data)
+//! .index()
+//! .column(0)
+//! .name(None)
+//! .transpose();
+//!
+//! let mut table = builder.build();
+//! table.with(Style::modern());
+//!
+//! println!("{}", table);
+//!
+//! assert_eq!(
+//! table.to_string(),
+//! concat!(
+//! "┌──────────┬─────────┬───────┐\n",
+//! "│ │ Algebra │ Apolo │\n",
+//! "├──────────┼─────────┼───────┤\n",
+//! "│ Complete │ │ + │\n",
+//! "├──────────┼─────────┼───────┤\n",
+//! "│ Started │ │ │\n",
+//! "├──────────┼─────────┼───────┤\n",
+//! "│ Ready │ │ │\n",
+//! "├──────────┼─────────┼───────┤\n",
+//! "│ Unknown │ + │ │\n",
+//! "└──────────┴─────────┴───────┘",
+//! ),
+//! )
+//! ```
+//!
+//! Example when we don't want to show empty data of enum where not all variants are used.
+//!
+#![cfg_attr(feature = "derive", doc = "```")]
+#![cfg_attr(not(feature = "derive"), doc = "```ignore")]
+//! use tabled::{Table, Tabled, settings::Style};
+//!
+//! #[derive(Tabled)]
+//! enum Status {
+//! #[tabled(inline)]
+//! Complete {
+//! started_timestamp: usize,
+//! finihsed_timestamp: usize,
+//! },
+//! #[tabled(inline)]
+//! Started {
+//! timestamp: usize,
+//! },
+//! Ready,
+//! Unknown,
+//! }
+//!
+//! let data = [
+//! Status::Unknown,
+//! Status::Complete { started_timestamp: 123, finihsed_timestamp: 234 },
+//! ];
+//!
+//! let mut builder = Table::builder(&data);
+//! builder.clean();
+//!
+//! let table = builder.build()
+//! .with(Style::modern())
+//! .to_string();
+//!
+//! println!("{}", table);
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "┌───────────────────┬────────────────────┬─────────┐\n",
+//! "│ started_timestamp │ finihsed_timestamp │ Unknown │\n",
+//! "├───────────────────┼────────────────────┼─────────┤\n",
+//! "│ │ │ + │\n",
+//! "├───────────────────┼────────────────────┼─────────┤\n",
+//! "│ 123 │ 234 │ │\n",
+//! "└───────────────────┴────────────────────┴─────────┘",
+//! ),
+//! )
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+mod index_builder;
+mod table_builder;
+
+pub use index_builder::IndexBuilder;
+pub use table_builder::Builder;
diff --git a/vendor/tabled/src/builder/table_builder.rs b/vendor/tabled/src/builder/table_builder.rs
new file mode 100644
index 000000000..40316494e
--- /dev/null
+++ b/vendor/tabled/src/builder/table_builder.rs
@@ -0,0 +1,506 @@
+use std::iter::FromIterator;
+
+use crate::{grid::records::vec_records::CellInfo, Table};
+
+use super::IndexBuilder;
+
+/// Builder creates a [`Table`] from dynamic data set.
+///
+/// It useful when the amount of columns or rows is not known statically.
+///
+/// ```rust
+/// use tabled::builder::Builder;
+///
+/// let mut builder = Builder::default();
+/// builder.set_header(["index", "measure", "value"]);
+/// builder.push_record(["0", "weight", "0.443"]);
+///
+/// let table = builder.build();
+///
+/// println!("{}", table);
+/// ```
+///
+/// It may be useful to use [`FromIterator`] for building.
+///
+/// ```rust
+/// use tabled::builder::Builder;
+/// use std::iter::FromIterator;
+///
+/// let data = vec![
+/// ["column1", "column2"],
+/// ["data1", "data2"],
+/// ["data3", "data4"],
+/// ];
+///
+/// let table = Builder::from_iter(data).build();
+///
+/// println!("{}", table);
+/// ```
+#[derive(Debug, Default, Clone)]
+pub struct Builder {
+ /// A list of rows.
+ data: Vec<Vec<CellInfo<String>>>,
+ /// A columns row.
+ columns: Option<Vec<CellInfo<String>>>,
+ /// A number of columns.
+ count_columns: usize,
+ /// A flag that the rows are not consistent.
+ is_consistent: bool,
+ /// A content of cells which are created in case rows has different length.
+ empty_cell_text: Option<String>,
+}
+
+impl Builder {
+ /// Creates a [`Builder`] instance.
+ ///
+ /// ```
+ /// use tabled::builder::Builder;
+ ///
+ /// let builder = Builder::new();
+ /// ```
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Creates a [`Builder`] instance with a given row capacity.
+ ///
+ /// ```
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::with_capacity(2);
+ /// builder.push_record((0..3).map(|i| i.to_string()));
+ /// builder.push_record(["i", "surname", "lastname"]);
+ /// ```
+ pub fn with_capacity(capacity: usize) -> Self {
+ let mut b = Self::new();
+ b.data = Vec::with_capacity(capacity);
+
+ b
+ }
+
+ /// Sets a [`Table`] header.
+ ///
+ /// ```
+ /// # use tabled::builder::Builder;
+ /// let mut builder = Builder::default();
+ /// builder.set_header((0..3).map(|i| i.to_string()));
+ /// ```
+ pub fn set_header<H, T>(&mut self, columns: H) -> &mut Self
+ where
+ H: IntoIterator<Item = T>,
+ T: Into<String>,
+ {
+ let list = create_row(columns, self.count_columns);
+
+ self.update_size(list.len());
+ self.columns = Some(list);
+
+ self
+ }
+
+ /// Sets off a [`Table`] header.
+ ///
+ /// If not set its a nop.
+ ///
+ /// ```rust
+ /// use tabled::Table;
+ ///
+ /// let data = [("Hello", 1u8, false), ("World", 21u8, true)];
+ ///
+ /// let table = Table::builder(data).build().to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+-------+----+-------+\n\
+ /// | &str | u8 | bool |\n\
+ /// +-------+----+-------+\n\
+ /// | Hello | 1 | false |\n\
+ /// +-------+----+-------+\n\
+ /// | World | 21 | true |\n\
+ /// +-------+----+-------+"
+ /// );
+ ///
+ ///
+ /// let mut builder = Table::builder(data);
+ /// builder.remove_header();
+ /// let table = builder.build().to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+-------+----+-------+\n\
+ /// | Hello | 1 | false |\n\
+ /// +-------+----+-------+\n\
+ /// | World | 21 | true |\n\
+ /// +-------+----+-------+"
+ /// );
+ ///
+ /// ```
+ pub fn remove_header(&mut self) -> &mut Self {
+ self.columns = None;
+ self.count_columns = self.get_size();
+
+ self
+ }
+
+ /// Sets a content of cells which are created in case rows has different length.
+ ///
+ ///
+ /// ```rust
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::default();
+ /// builder
+ /// .set_default_text("undefined")
+ /// .set_header((0..3).map(|i| i.to_string()))
+ /// .push_record(["i"]);
+ /// ```
+ pub fn set_default_text<T>(&mut self, text: T) -> &mut Self
+ where
+ T: Into<String>,
+ {
+ self.empty_cell_text = Some(text.into());
+ self
+ }
+
+ /// Build creates a [`Table`] instance.
+ ///
+ /// ```rust
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::default();
+ /// builder.set_header(["i", "column1", "column2"]);
+ /// builder.push_record(["0", "value1", "value2"]);
+ /// ```
+ pub fn build(self) -> Table {
+ Table::from(self)
+ }
+
+ /// Add an index to the [`Table`].
+ ///
+ /// Default index is a range 0-N where N is amount of records.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::Table;
+ ///
+ /// let table = Table::builder(&["Hello", "World", "!"]).index().build();
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+---+-------+\n\
+ /// | | &str |\n\
+ /// +---+-------+\n\
+ /// | 0 | Hello |\n\
+ /// +---+-------+\n\
+ /// | 1 | World |\n\
+ /// +---+-------+\n\
+ /// | 2 | ! |\n\
+ /// +---+-------+"
+ /// )
+ /// ```
+ pub fn index(self) -> IndexBuilder {
+ IndexBuilder::from(self)
+ }
+
+ /// Adds a row to a [`Table`].
+ ///
+ /// ```
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::default();
+ /// builder.push_record((0..3).map(|i| i.to_string()));
+ /// builder.push_record(["i", "surname", "lastname"]);
+ /// ```
+ pub fn push_record<R, T>(&mut self, row: R) -> &mut Self
+ where
+ R: IntoIterator<Item = T>,
+ T: Into<String>,
+ {
+ let list = create_row(row, self.count_columns);
+
+ self.update_size(list.len());
+ self.data.push(list);
+
+ self
+ }
+
+ /// Insert a row into a specific position.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `index > count_rows`.
+ pub fn insert_record<R>(&mut self, index: usize, record: R) -> bool
+ where
+ R: IntoIterator,
+ R::Item: Into<String>,
+ {
+ let list = create_row(record, self.count_columns);
+
+ self.update_size(list.len());
+ self.data.insert(index, list);
+
+ true
+ }
+
+ /// Clean removes empty columns and rows.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::Table;
+ ///
+ /// let mut builder = Table::builder(&["Hello", "World", ""]);
+ /// builder.clean();
+ ///
+ /// let table = builder.build();
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+-------+\n\
+ /// | &str |\n\
+ /// +-------+\n\
+ /// | Hello |\n\
+ /// +-------+\n\
+ /// | World |\n\
+ /// +-------+"
+ /// )
+ /// ```
+ pub fn clean(&mut self) -> &mut Self {
+ self.clean_columns();
+ self.clean_rows();
+ self
+ }
+
+ /// Set a column size.
+ ///
+ /// If it make it lower then it was originally it is considered NOP.
+ pub fn hint_column_size(&mut self, size: usize) -> &mut Self {
+ self.count_columns = size;
+ self.is_consistent = true;
+ self
+ }
+
+ /// Returns an amount of columns which would be present in a built table.
+ pub fn count_columns(&self) -> usize {
+ self.count_columns
+ }
+
+ /// Returns an amount of rows which would be present in a built table.
+ pub fn count_rows(&self) -> usize {
+ self.data.len()
+ }
+
+ /// Checks whether a builder contains a header set.
+ pub fn has_header(&self) -> bool {
+ self.columns.is_some()
+ }
+
+ fn clean_columns(&mut self) {
+ let mut i = 0;
+ for col in 0..self.count_columns {
+ let col = col - i;
+
+ let mut is_empty = true;
+ for row in 0..self.data.len() {
+ let cell = &self.data[row][col];
+ if !cell.as_ref().is_empty() {
+ is_empty = false;
+ break;
+ }
+ }
+
+ if is_empty {
+ for row in 0..self.data.len() {
+ let _ = self.data[row].remove(col);
+ }
+
+ if let Some(columns) = self.columns.as_mut() {
+ if columns.len() > col {
+ let _ = columns.remove(col);
+ }
+ }
+
+ i += 1;
+ }
+ }
+
+ self.count_columns -= i;
+ }
+
+ fn clean_rows(&mut self) {
+ for row in (0..self.data.len()).rev() {
+ let mut is_empty = true;
+ for col in 0..self.count_columns {
+ let cell = &self.data[row][col];
+ if !cell.as_ref().is_empty() {
+ is_empty = false;
+ break;
+ }
+ }
+
+ if is_empty {
+ let _ = self.data.remove(row);
+ }
+
+ if row == 0 {
+ break;
+ }
+ }
+ }
+
+ fn update_size(&mut self, size: usize) {
+ use std::cmp::Ordering;
+
+ match size.cmp(&self.count_columns) {
+ Ordering::Less => {
+ if !self.data.is_empty() {
+ self.is_consistent = false;
+ }
+ }
+ Ordering::Greater => {
+ self.count_columns = size;
+
+ if !self.data.is_empty() || self.columns.is_some() {
+ self.is_consistent = false;
+ }
+ }
+ Ordering::Equal => (),
+ }
+ }
+
+ fn get_size(&mut self) -> usize {
+ let mut max = self.columns.as_ref().map_or(0, Vec::len);
+ let max_records = self.data.iter().map(Vec::len).max().unwrap_or(0);
+ max = std::cmp::max(max_records, max);
+
+ max
+ }
+
+ fn fix_rows(&mut self) {
+ let empty_cell = self.empty_cell_text.to_owned().unwrap_or_default();
+ let empty = CellInfo::new(empty_cell);
+
+ if let Some(header) = self.columns.as_mut() {
+ if self.count_columns > header.len() {
+ let count = self.count_columns - header.len();
+ append_vec(header, empty.clone(), count);
+ }
+ }
+
+ for row in &mut self.data {
+ if self.count_columns > row.len() {
+ let count = self.count_columns - row.len();
+ append_vec(row, empty.clone(), count);
+ }
+ }
+ }
+}
+
+impl From<Builder> for Vec<Vec<String>> {
+ fn from(mut builder: Builder) -> Self {
+ if !builder.is_consistent {
+ builder.fix_rows();
+ }
+
+ if let Some(columns) = builder.columns {
+ builder.data.insert(0, columns);
+ }
+
+ builder
+ .data
+ .into_iter()
+ .map(|row| row.into_iter().map(|c| c.into_inner()).collect())
+ .collect()
+ }
+}
+
+impl From<Builder> for Vec<Vec<CellInfo<String>>> {
+ fn from(mut builder: Builder) -> Self {
+ if !builder.is_consistent {
+ builder.fix_rows();
+ }
+
+ if let Some(columns) = builder.columns {
+ builder.data.insert(0, columns);
+ }
+
+ builder.data
+ }
+}
+
+impl<R, V> FromIterator<R> for Builder
+where
+ R: IntoIterator<Item = V>,
+ V: Into<String>,
+{
+ fn from_iter<T: IntoIterator<Item = R>>(iter: T) -> Self {
+ let mut builder = Self::default();
+ for row in iter {
+ let _ = builder.push_record(row);
+ }
+
+ builder
+ }
+}
+
+impl<D> Extend<D> for Builder
+where
+ D: Into<String>,
+{
+ fn extend<T: IntoIterator<Item = D>>(&mut self, iter: T) {
+ let _ = self.push_record(iter);
+ }
+}
+
+impl From<Vec<Vec<String>>> for Builder {
+ fn from(data: Vec<Vec<String>>) -> Self {
+ let count_columns = data.get(0).map_or(0, |row| row.len());
+
+ let data = data
+ .into_iter()
+ .map(|row| row.into_iter().map(CellInfo::new).collect())
+ .collect();
+
+ Self {
+ data,
+ count_columns,
+ columns: None,
+ is_consistent: false,
+ empty_cell_text: None,
+ }
+ }
+}
+
+impl From<Vec<Vec<CellInfo<String>>>> for Builder {
+ fn from(data: Vec<Vec<CellInfo<String>>>) -> Self {
+ let count_columns = data.get(0).map_or(0, |row| row.len());
+
+ Self {
+ data,
+ count_columns,
+ columns: None,
+ is_consistent: false,
+ empty_cell_text: None,
+ }
+ }
+}
+
+fn create_row<R, T>(row: R, size: usize) -> Vec<CellInfo<String>>
+where
+ R: IntoIterator<Item = T>,
+ T: Into<String>,
+{
+ let mut list = Vec::with_capacity(size);
+ for text in row {
+ let text = text.into();
+ let info = CellInfo::new(text);
+ list.push(info);
+ }
+
+ list
+}
+
+fn append_vec<T: Clone>(v: &mut Vec<T>, value: T, n: usize) {
+ v.extend((0..n).map(|_| value.clone()));
+}
diff --git a/vendor/tabled/src/grid/colored_config.rs b/vendor/tabled/src/grid/colored_config.rs
new file mode 100644
index 000000000..51a00fbf4
--- /dev/null
+++ b/vendor/tabled/src/grid/colored_config.rs
@@ -0,0 +1,112 @@
+use std::ops::{Deref, DerefMut};
+
+use crate::grid::{
+ color::AnsiColor,
+ config::{Entity, EntityMap, SpannedConfig},
+};
+
+/// A spanned configuration plus colors for cells.
+#[derive(Debug, Default, PartialEq, Eq, Clone)]
+pub struct ColoredConfig {
+ config: SpannedConfig,
+ colors: ColorMap,
+}
+
+impl ColoredConfig {
+ /// Create a new colored config.
+ pub fn new(config: SpannedConfig) -> Self {
+ Self {
+ config,
+ colors: ColorMap::default(),
+ }
+ }
+
+ /// Set a color for a given cell.
+ ///
+ /// The outcome is the same as if you'd use [`Format`] and added a color but it'd work only with `color` feature on.
+ /// While this method works in all contexts.
+ ///
+ /// [`Format`]: crate::settings::Format
+ pub fn set_color(&mut self, pos: Entity, color: AnsiColor<'static>) -> &mut Self {
+ match self.colors.0.as_mut() {
+ Some(map) => map.insert(pos, color),
+ None => {
+ let mut colors = EntityMap::default();
+ colors.insert(pos, color);
+ self.colors = ColorMap(Some(colors));
+ }
+ }
+
+ self
+ }
+
+ /// Set a list of colors.
+ pub fn set_colors(&mut self, colors: EntityMap<AnsiColor<'static>>) -> &mut Self {
+ self.colors = ColorMap(Some(colors));
+ self
+ }
+
+ /// Remove a color for a given cell.
+ pub fn remove_color(&mut self, pos: Entity) -> &mut Self {
+ if let Some(colors) = self.colors.0.as_mut() {
+ colors.remove(pos);
+ }
+
+ self
+ }
+
+ /// Returns a list of colors.
+ pub fn get_colors(&self) -> &ColorMap {
+ &self.colors
+ }
+
+ /// Returns an inner config.
+ pub fn into_inner(self) -> SpannedConfig {
+ self.config
+ }
+}
+
+impl Deref for ColoredConfig {
+ type Target = SpannedConfig;
+
+ fn deref(&self) -> &Self::Target {
+ &self.config
+ }
+}
+
+impl DerefMut for ColoredConfig {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.config
+ }
+}
+
+impl From<SpannedConfig> for ColoredConfig {
+ fn from(value: SpannedConfig) -> Self {
+ Self::new(value)
+ }
+}
+
+impl AsRef<SpannedConfig> for ColoredConfig {
+ fn as_ref(&self) -> &SpannedConfig {
+ &self.config
+ }
+}
+
+/// A colors structure for [`ColoredConfig`].
+#[derive(Debug, Default, PartialEq, Eq, Clone)]
+pub struct ColorMap(Option<EntityMap<AnsiColor<'static>>>);
+
+impl ColorMap {
+ /// Checks if any colors is set on.
+ pub fn is_empty(&self) -> bool {
+ self.0.is_none()
+ }
+}
+
+impl crate::grid::colors::Colors for ColorMap {
+ type Color = AnsiColor<'static>;
+
+ fn get_color(&self, (row, col): (usize, usize)) -> Option<&Self::Color> {
+ self.0.as_ref().map(|map| map.get(Entity::Cell(row, col)))
+ }
+}
diff --git a/vendor/tabled/src/grid/compact_multiline_config.rs b/vendor/tabled/src/grid/compact_multiline_config.rs
new file mode 100644
index 000000000..c9056c911
--- /dev/null
+++ b/vendor/tabled/src/grid/compact_multiline_config.rs
@@ -0,0 +1,211 @@
+use crate::grid::color::StaticColor;
+use crate::grid::config::{
+ AlignmentHorizontal, AlignmentVertical, Borders, CompactConfig, Indent, Line, Sides,
+};
+
+/// A [`CompactConfig`] configuration plus vertical alignment.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct CompactMultilineConfig {
+ config: CompactConfig,
+ alignment_vertical: AlignmentVertical,
+ formatting: Formatting,
+}
+
+impl CompactMultilineConfig {
+ /// Create a new colored config.
+ pub fn new(config: CompactConfig) -> Self {
+ Self::from(config)
+ }
+
+ /// Set a horizontal alignment.
+ pub const fn set_alignment_vertical(mut self, alignment: AlignmentVertical) -> Self {
+ self.alignment_vertical = alignment;
+ self
+ }
+
+ /// Get a alignment horizontal.
+ pub const fn get_alignment_vertical(&self) -> AlignmentVertical {
+ self.alignment_vertical
+ }
+
+ /// Set grid margin.
+ pub const fn set_margin(mut self, margin: Sides<Indent>) -> Self {
+ self.config = self.config.set_margin(margin);
+ self
+ }
+
+ /// Returns a grid margin.
+ pub const fn get_margin(&self) -> &Sides<Indent> {
+ self.config.get_margin()
+ }
+
+ /// Set the [`Borders`] value as correct one.
+ pub const fn set_borders(mut self, borders: Borders<char>) -> Self {
+ self.config = self.config.set_borders(borders);
+ self
+ }
+
+ /// Set the first horizontal line.
+ ///
+ /// It ignores the [`Borders`] horizontal value if set for 1st row.
+ pub const fn set_first_horizontal_line(mut self, line: Line<char>) -> Self {
+ self.config = self.config.set_first_horizontal_line(line);
+ self
+ }
+
+ /// Set the first horizontal line.
+ ///
+ /// It ignores the [`Borders`] horizontal value if set for 1st row.
+ pub const fn get_first_horizontal_line(&self) -> Option<Line<char>> {
+ self.config.get_first_horizontal_line()
+ }
+
+ /// Returns a current [`Borders`] structure.
+ pub const fn get_borders(&self) -> &Borders<char> {
+ self.config.get_borders()
+ }
+
+ /// Returns a current [`Borders`] structure.
+ pub const fn get_borders_color(&self) -> &Borders<StaticColor> {
+ self.config.get_borders_color()
+ }
+
+ /// Set a padding to a given cells.
+ pub const fn set_padding(mut self, padding: Sides<Indent>) -> Self {
+ self.config = self.config.set_padding(padding);
+ self
+ }
+
+ /// Get a padding for a given.
+ pub const fn get_padding(&self) -> &Sides<Indent> {
+ self.config.get_padding()
+ }
+
+ /// Set a horizontal alignment.
+ pub const fn set_alignment_horizontal(mut self, alignment: AlignmentHorizontal) -> Self {
+ self.config = self.config.set_alignment_horizontal(alignment);
+ self
+ }
+
+ /// Get a alignment horizontal.
+ pub const fn get_alignment_horizontal(&self) -> AlignmentHorizontal {
+ self.config.get_alignment_horizontal()
+ }
+
+ /// Sets colors of border carcass on the grid.
+ pub const fn set_borders_color(mut self, borders: Borders<StaticColor>) -> Self {
+ self.config = self.config.set_borders_color(borders);
+ self
+ }
+
+ /// Set colors for a margin.
+ pub const fn set_margin_color(mut self, color: Sides<StaticColor>) -> Self {
+ self.config = self.config.set_margin_color(color);
+ self
+ }
+
+ /// Returns a margin color.
+ pub const fn get_margin_color(&self) -> Sides<StaticColor> {
+ self.config.get_margin_color()
+ }
+
+ /// Set a padding color to all cells.
+ pub const fn set_padding_color(mut self, color: Sides<StaticColor>) -> Self {
+ self.config = self.config.set_padding_color(color);
+ self
+ }
+
+ /// get a padding color.
+ pub const fn get_padding_color(&self) -> Sides<StaticColor> {
+ self.config.get_padding_color()
+ }
+
+ /// Set formatting.
+ pub const fn set_formatting(mut self, formatting: Formatting) -> Self {
+ self.formatting = formatting;
+ self
+ }
+
+ /// Get formatting.
+ pub const fn get_formatting(&self) -> Formatting {
+ self.formatting
+ }
+}
+
+impl Default for CompactMultilineConfig {
+ fn default() -> Self {
+ Self {
+ config: Default::default(),
+ alignment_vertical: AlignmentVertical::Top,
+ formatting: Formatting::default(),
+ }
+ }
+}
+
+impl From<CompactConfig> for CompactMultilineConfig {
+ fn from(config: CompactConfig) -> Self {
+ Self {
+ config,
+ alignment_vertical: AlignmentVertical::Top,
+ formatting: Formatting::default(),
+ }
+ }
+}
+
+impl AsRef<CompactConfig> for CompactMultilineConfig {
+ fn as_ref(&self) -> &CompactConfig {
+ &self.config
+ }
+}
+
+impl AsMut<CompactConfig> for CompactMultilineConfig {
+ fn as_mut(&mut self) -> &mut CompactConfig {
+ &mut self.config
+ }
+}
+
+#[cfg(feature = "std")]
+impl From<CompactMultilineConfig> for crate::grid::config::SpannedConfig {
+ fn from(compact: CompactMultilineConfig) -> Self {
+ use crate::grid::config::Entity;
+
+ let mut cfg = crate::grid::config::SpannedConfig::from(compact.config);
+ cfg.set_alignment_vertical(Entity::Global, compact.alignment_vertical);
+ cfg.set_formatting(Entity::Global, compact.formatting.into());
+
+ cfg
+ }
+}
+
+/// Formatting represent a logic of formatting of a cell.
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Formatting {
+ /// An setting to allow horizontal trim.
+ pub horizontal_trim: bool,
+ /// An setting to allow vertical trim.
+ pub vertical_trim: bool,
+ /// An setting to allow alignment per line.
+ pub allow_lines_alignment: bool,
+}
+
+impl Formatting {
+ /// Creates a new [`Formatting`] structure.
+ pub fn new(horizontal_trim: bool, vertical_trim: bool, allow_lines_alignment: bool) -> Self {
+ Self {
+ horizontal_trim,
+ vertical_trim,
+ allow_lines_alignment,
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl From<Formatting> for crate::grid::config::Formatting {
+ fn from(val: Formatting) -> Self {
+ crate::grid::config::Formatting {
+ allow_lines_alignment: val.allow_lines_alignment,
+ horizontal_trim: val.horizontal_trim,
+ vertical_trim: val.vertical_trim,
+ }
+ }
+}
diff --git a/vendor/tabled/src/grid/dimension/complete_dimension.rs b/vendor/tabled/src/grid/dimension/complete_dimension.rs
new file mode 100644
index 000000000..3147cb27a
--- /dev/null
+++ b/vendor/tabled/src/grid/dimension/complete_dimension.rs
@@ -0,0 +1,136 @@
+use std::borrow::Cow;
+
+use crate::grid::{
+ config::{ColoredConfig, SpannedConfig},
+ dimension::{Dimension, Estimate, SpannedGridDimension},
+ records::Records,
+};
+
+/// CompleteDimension is a [`Dimension`] implementation for a [`Table`]
+///
+/// [`Table`]: crate::Table
+#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone)]
+pub struct CompleteDimension<'a> {
+ width: Option<Cow<'a, [usize]>>,
+ height: Option<Cow<'a, [usize]>>,
+}
+
+impl CompleteDimension<'_> {
+ /// Checks whether is the dimensions is set.
+ pub fn is_complete(&self) -> bool {
+ self.width.is_some() && self.height.is_some()
+ }
+
+ /// Checks whether is nothing was set.
+ pub fn is_empty(&self) -> bool {
+ self.width.is_none() && self.height.is_none()
+ }
+
+ /// Set column widths.
+ ///
+ /// In general the method is only considered to be useful to a [`TableOption`].
+ ///
+ /// BE CAREFUL WITH THIS METHOD as it supposed that the content is not bigger than the provided widths.
+ ///
+ /// [`TableOption`]: crate::settings::TableOption
+ pub fn set_widths(&mut self, columns: Vec<usize>) -> bool {
+ self.width = Some(Cow::Owned(columns));
+
+ true
+ }
+
+ /// Set rows heights.
+ ///
+ /// In general the method is only considered to be useful to a [`TableOption`].
+ ///
+ /// BE CAREFUL WITH THIS METHOD as it supposed that the content is not bigger than the provided heights.
+ ///
+ /// [`TableOption`]: crate::settings::TableOption
+ pub fn set_heights(&mut self, rows: Vec<usize>) -> bool {
+ self.height = Some(Cow::Owned(rows));
+
+ true
+ }
+
+ /// Force width estimation.
+ pub fn clear_width(&mut self) {
+ self.width = None;
+ }
+
+ /// Force height estimation.
+ pub fn clear_height(&mut self) {
+ self.height = None;
+ }
+
+ /// Copies a reference from self.
+ pub fn from_origin(&self) -> CompleteDimension<'_> {
+ let width = self.width.as_deref().map(Cow::Borrowed);
+ let height = self.height.as_deref().map(Cow::Borrowed);
+
+ CompleteDimension { width, height }
+ }
+}
+
+impl Dimension for CompleteDimension<'_> {
+ fn get_width(&self, column: usize) -> usize {
+ let width = self
+ .width
+ .as_ref()
+ .expect("It must always be Some at this point");
+
+ width[column]
+ }
+
+ fn get_height(&self, row: usize) -> usize {
+ let height = self
+ .height
+ .as_ref()
+ .expect("It must always be Some at this point");
+
+ height[row]
+ }
+}
+
+impl<R: Records> Estimate<R, SpannedConfig> for CompleteDimension<'_> {
+ fn estimate(&mut self, records: R, cfg: &SpannedConfig) {
+ match (self.width.is_some(), self.height.is_some()) {
+ (true, true) => {}
+ (true, false) => {
+ self.height = Some(Cow::Owned(SpannedGridDimension::height(records, cfg)));
+ }
+ (false, true) => {
+ self.width = Some(Cow::Owned(SpannedGridDimension::width(records, cfg)));
+ }
+ (false, false) => {
+ let mut dims = SpannedGridDimension::default();
+ dims.estimate(records, cfg);
+
+ let (width, height) = dims.get_values();
+ self.width = Some(Cow::Owned(width));
+ self.height = Some(Cow::Owned(height));
+ }
+ }
+ }
+}
+
+impl<R: Records> Estimate<R, ColoredConfig> for CompleteDimension<'_> {
+ fn estimate(&mut self, records: R, cfg: &ColoredConfig) {
+ match (self.width.is_some(), self.height.is_some()) {
+ (true, true) => {}
+ (true, false) => {
+ self.height = Some(Cow::Owned(SpannedGridDimension::height(records, cfg)));
+ }
+ (false, true) => {
+ self.width = Some(Cow::Owned(SpannedGridDimension::width(records, cfg)));
+ }
+ (false, false) => {
+ let mut dims = SpannedGridDimension::default();
+ dims.estimate(records, cfg);
+
+ let (width, height) = dims.get_values();
+ self.width = Some(Cow::Owned(width));
+ self.height = Some(Cow::Owned(height));
+ }
+ }
+ }
+}
diff --git a/vendor/tabled/src/grid/dimension/complete_dimension_vec_records.rs b/vendor/tabled/src/grid/dimension/complete_dimension_vec_records.rs
new file mode 100644
index 000000000..ddc806a45
--- /dev/null
+++ b/vendor/tabled/src/grid/dimension/complete_dimension_vec_records.rs
@@ -0,0 +1,128 @@
+use std::borrow::Cow;
+
+use papergrid::{
+ dimension::spanned_vec_records::SpannedVecRecordsDimension, records::vec_records::VecRecords,
+};
+
+use crate::grid::{
+ config::{ColoredConfig, SpannedConfig},
+ dimension::{Dimension, Estimate},
+ records::vec_records::Cell,
+};
+
+/// CompleteDimension is a [`Dimension`] implementation for a [`Table`]
+///
+/// [`Table`]: crate::Table
+#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone)]
+pub struct CompleteDimensionVecRecords<'a> {
+ width: Option<Cow<'a, [usize]>>,
+ height: Option<Cow<'a, [usize]>>,
+}
+
+impl CompleteDimensionVecRecords<'_> {
+ /// Checks whether is the dimensions is set.
+ pub fn is_complete(&self) -> bool {
+ self.width.is_some() && self.height.is_some()
+ }
+
+ /// Checks whether is nothing was set.
+ pub fn is_empty(&self) -> bool {
+ self.width.is_none() && self.height.is_none()
+ }
+
+ /// Set column widths.
+ ///
+ /// In general the method is only considered to be useful to a [`TableOption`].
+ ///
+ /// BE CAREFUL WITH THIS METHOD as it supposed that the content is not bigger than the provided widths.
+ ///
+ /// [`TableOption`]: crate::settings::TableOption
+ pub fn set_widths(&mut self, columns: Vec<usize>) -> bool {
+ self.width = Some(Cow::Owned(columns));
+
+ true
+ }
+
+ /// Set rows heights.
+ ///
+ /// In general the method is only considered to be useful to a [`TableOption`].
+ ///
+ /// BE CAREFUL WITH THIS METHOD as it supposed that the content is not bigger than the provided heights.
+ ///
+ /// [`TableOption`]: crate::settings::TableOption
+ pub fn set_heights(&mut self, rows: Vec<usize>) -> bool {
+ self.height = Some(Cow::Owned(rows));
+
+ true
+ }
+
+ /// Force width estimation.
+ pub fn clear_width(&mut self) {
+ self.width = None;
+ }
+
+ /// Force height estimation.
+ pub fn clear_height(&mut self) {
+ self.height = None;
+ }
+
+ /// Copies a reference from self.
+ pub fn from_origin(&self) -> CompleteDimensionVecRecords<'_> {
+ let width = self.width.as_deref().map(Cow::Borrowed);
+ let height = self.height.as_deref().map(Cow::Borrowed);
+
+ CompleteDimensionVecRecords { width, height }
+ }
+}
+
+impl Dimension for CompleteDimensionVecRecords<'_> {
+ fn get_width(&self, column: usize) -> usize {
+ let width = self
+ .width
+ .as_ref()
+ .expect("It must always be Some at this point");
+
+ width[column]
+ }
+
+ fn get_height(&self, row: usize) -> usize {
+ let height = self
+ .height
+ .as_ref()
+ .expect("It must always be Some at this point");
+
+ height[row]
+ }
+}
+
+impl<T: AsRef<str> + Cell> Estimate<&VecRecords<T>, SpannedConfig>
+ for CompleteDimensionVecRecords<'_>
+{
+ fn estimate(&mut self, records: &VecRecords<T>, cfg: &SpannedConfig) {
+ match (self.width.is_some(), self.height.is_some()) {
+ (true, true) => {}
+ (true, false) => {
+ self.height = Some(Cow::Owned(SpannedVecRecordsDimension::height(records, cfg)));
+ }
+ (false, true) => {
+ self.width = Some(Cow::Owned(SpannedVecRecordsDimension::width(records, cfg)));
+ }
+ (false, false) => {
+ let mut dims = SpannedVecRecordsDimension::default();
+ dims.estimate(records, cfg);
+
+ let (width, height) = dims.get_values();
+ self.width = Some(Cow::Owned(width));
+ self.height = Some(Cow::Owned(height));
+ }
+ }
+ }
+}
+
+impl<T: AsRef<str> + Cell> Estimate<&VecRecords<T>, ColoredConfig>
+ for CompleteDimensionVecRecords<'_>
+{
+ fn estimate(&mut self, records: &VecRecords<T>, cfg: &ColoredConfig) {
+ self.estimate(records, cfg.as_ref())
+ }
+}
diff --git a/vendor/tabled/src/grid/dimension/const_dimension.rs b/vendor/tabled/src/grid/dimension/const_dimension.rs
new file mode 100644
index 000000000..450b1abfe
--- /dev/null
+++ b/vendor/tabled/src/grid/dimension/const_dimension.rs
@@ -0,0 +1,70 @@
+//! Module contains a dimension estimator for [`CompactTable`]
+//!
+//! [`CompactTable`]: crate::tables::CompactTable
+
+use crate::grid::dimension::{Dimension, Estimate};
+
+/// A constant size dimension or a value dimension.
+#[derive(Debug, Clone, Copy)]
+pub struct ConstDimension<const COLUMNS: usize, const ROWS: usize> {
+ height: ConstSize<ROWS>,
+ width: ConstSize<COLUMNS>,
+}
+
+impl<const COLUMNS: usize, const ROWS: usize> ConstDimension<COLUMNS, ROWS> {
+ /// Returns a new dimension object with a given estimates.
+ pub const fn new(width: ConstSize<COLUMNS>, height: ConstSize<ROWS>) -> Self {
+ Self { width, height }
+ }
+}
+
+impl<const COLUMNS: usize, const ROWS: usize> Dimension for ConstDimension<COLUMNS, ROWS> {
+ fn get_width(&self, column: usize) -> usize {
+ match self.width {
+ ConstSize::List(list) => list[column],
+ ConstSize::Value(val) => val,
+ }
+ }
+
+ fn get_height(&self, row: usize) -> usize {
+ match self.height {
+ ConstSize::List(list) => list[row],
+ ConstSize::Value(val) => val,
+ }
+ }
+}
+
+impl<const COLUMNS: usize, const ROWS: usize> From<ConstDimension<COLUMNS, ROWS>>
+ for (ConstSize<COLUMNS>, ConstSize<ROWS>)
+{
+ fn from(value: ConstDimension<COLUMNS, ROWS>) -> Self {
+ (value.width, value.height)
+ }
+}
+
+impl<R, D, const COLUMNS: usize, const ROWS: usize> Estimate<R, D>
+ for ConstDimension<COLUMNS, ROWS>
+{
+ fn estimate(&mut self, _: R, _: &D) {}
+}
+
+/// Const size represents either a const array values or a single value which responsible for the whole list.
+#[derive(Debug, Clone, Copy)]
+pub enum ConstSize<const N: usize> {
+ /// A constant array of estimates.
+ List([usize; N]),
+ /// A value which act as a single estimate for all entries.
+ Value(usize),
+}
+
+impl From<usize> for ConstSize<0> {
+ fn from(value: usize) -> Self {
+ ConstSize::Value(value)
+ }
+}
+
+impl<const N: usize> From<[usize; N]> for ConstSize<N> {
+ fn from(value: [usize; N]) -> Self {
+ ConstSize::List(value)
+ }
+}
diff --git a/vendor/tabled/src/grid/dimension/mod.rs b/vendor/tabled/src/grid/dimension/mod.rs
new file mode 100644
index 000000000..2f67b402f
--- /dev/null
+++ b/vendor/tabled/src/grid/dimension/mod.rs
@@ -0,0 +1,32 @@
+//! Module contains a list of implementations of [`Estimate`] and [`Dimension`].
+
+mod const_dimension;
+mod pool_table_dimension;
+
+#[cfg(feature = "std")]
+mod complete_dimension;
+#[cfg(feature = "std")]
+mod complete_dimension_vec_records;
+#[cfg(feature = "std")]
+mod peekable_dimension;
+#[cfg(feature = "std")]
+mod static_dimension;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use self::{
+ complete_dimension::CompleteDimension,
+ complete_dimension_vec_records::CompleteDimensionVecRecords,
+ peekable_dimension::PeekableDimension,
+ static_dimension::{DimensionValue, StaticDimension},
+};
+pub use const_dimension::{ConstDimension, ConstSize};
+pub use papergrid::dimension::{Dimension, Estimate};
+pub use pool_table_dimension::{DimensionPriority, PoolTableDimension};
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use papergrid::dimension::{
+ compact::CompactGridDimension, spanned::SpannedGridDimension,
+ spanned_vec_records::SpannedVecRecordsDimension,
+};
diff --git a/vendor/tabled/src/grid/dimension/peekable_dimension.rs b/vendor/tabled/src/grid/dimension/peekable_dimension.rs
new file mode 100644
index 000000000..1ba6fda21
--- /dev/null
+++ b/vendor/tabled/src/grid/dimension/peekable_dimension.rs
@@ -0,0 +1,335 @@
+use papergrid::records::vec_records::{CellInfo, VecRecords};
+
+use crate::grid::{
+ config::SpannedConfig,
+ dimension::{Dimension, Estimate},
+ records::Records,
+};
+
+/// PeekableDimension is a [`Dimension`] implementation for a [`Table`]
+///
+/// [`Table`]: crate::Table
+#[derive(Debug, Default, Clone)]
+pub struct PeekableDimension {
+ width: Vec<usize>,
+ height: Vec<usize>,
+}
+
+impl PeekableDimension {
+ /// Calculates height of rows.
+ pub fn height<T: AsRef<str>>(
+ records: &VecRecords<CellInfo<T>>,
+ cfg: &SpannedConfig,
+ ) -> Vec<usize> {
+ estimation::build_height(records, cfg)
+ }
+
+ /// Calculates width of columns.
+ pub fn width<T: AsRef<str>>(
+ records: &VecRecords<CellInfo<T>>,
+ cfg: &SpannedConfig,
+ ) -> Vec<usize> {
+ estimation::build_width(records, cfg)
+ }
+
+ /// Return width and height lists.
+ pub fn get_values(self) -> (Vec<usize>, Vec<usize>) {
+ (self.width, self.height)
+ }
+}
+
+impl Dimension for PeekableDimension {
+ fn get_width(&self, column: usize) -> usize {
+ self.width[column]
+ }
+
+ fn get_height(&self, row: usize) -> usize {
+ self.height[row]
+ }
+}
+
+impl<T> Estimate<&VecRecords<CellInfo<T>>, SpannedConfig> for PeekableDimension
+where
+ T: AsRef<str>,
+{
+ fn estimate(&mut self, records: &VecRecords<CellInfo<T>>, cfg: &SpannedConfig) {
+ let (width, height) = estimation::build_dimensions(records, cfg);
+ self.width = width;
+ self.height = height;
+ }
+}
+
+mod estimation {
+ use core::cmp::{max, Ordering};
+ use std::collections::HashMap;
+
+ use papergrid::{
+ config::Position,
+ records::vec_records::{Cell, CellInfo, VecRecords},
+ };
+
+ use super::*;
+
+ pub(super) fn build_dimensions<T: AsRef<str>>(
+ records: &VecRecords<CellInfo<T>>,
+ cfg: &SpannedConfig,
+ ) -> (Vec<usize>, Vec<usize>) {
+ let count_columns = records.count_columns();
+
+ let mut widths = vec![0; count_columns];
+ let mut heights = vec![];
+
+ let mut vspans = HashMap::new();
+ let mut hspans = HashMap::new();
+
+ for (row, columns) in records.iter_rows().enumerate() {
+ let mut row_height = 0;
+ for (col, cell) in columns.iter().enumerate() {
+ let pos = (row, col);
+ if !cfg.is_cell_visible(pos) {
+ continue;
+ }
+
+ let height = cell.count_lines();
+ let width = cell.width();
+
+ let pad = cfg.get_padding(pos.into());
+ let width = width + pad.left.size + pad.right.size;
+ let height = height + pad.top.size + pad.bottom.size;
+
+ match cfg.get_column_span(pos) {
+ Some(n) if n > 1 => {
+ let _ = vspans.insert(pos, (n, width));
+ }
+ _ => widths[col] = max(widths[col], width),
+ }
+
+ match cfg.get_row_span(pos) {
+ Some(n) if n > 1 => {
+ let _ = hspans.insert(pos, (n, height));
+ }
+ _ => row_height = max(row_height, height),
+ }
+ }
+
+ heights.push(row_height);
+ }
+
+ let count_rows = heights.len();
+
+ adjust_vspans(cfg, count_columns, &vspans, &mut widths);
+ adjust_hspans(cfg, count_rows, &hspans, &mut heights);
+
+ (widths, heights)
+ }
+
+ fn adjust_hspans(
+ cfg: &SpannedConfig,
+ len: usize,
+ spans: &HashMap<Position, (usize, usize)>,
+ heights: &mut [usize],
+ ) {
+ if spans.is_empty() {
+ return;
+ }
+
+ let mut spans_ordered = spans
+ .iter()
+ .map(|(k, v)| ((k.0, k.1), *v))
+ .collect::<Vec<_>>();
+ spans_ordered.sort_unstable_by(|(arow, acol), (brow, bcol)| match arow.cmp(brow) {
+ Ordering::Equal => acol.cmp(bcol),
+ ord => ord,
+ });
+
+ for ((row, _), (span, height)) in spans_ordered {
+ adjust_row_range(cfg, height, len, row, row + span, heights);
+ }
+ }
+
+ fn adjust_row_range(
+ cfg: &SpannedConfig,
+ max_span_height: usize,
+ len: usize,
+ start: usize,
+ end: usize,
+ heights: &mut [usize],
+ ) {
+ let range_height = range_height(cfg, len, start, end, heights);
+ if range_height >= max_span_height {
+ return;
+ }
+
+ inc_range(heights, max_span_height - range_height, start, end);
+ }
+
+ fn range_height(
+ cfg: &SpannedConfig,
+ len: usize,
+ start: usize,
+ end: usize,
+ heights: &[usize],
+ ) -> usize {
+ let count_borders = count_horizontal_borders(cfg, len, start, end);
+ let range_height = heights[start..end].iter().sum::<usize>();
+ count_borders + range_height
+ }
+
+ fn count_horizontal_borders(
+ cfg: &SpannedConfig,
+ len: usize,
+ start: usize,
+ end: usize,
+ ) -> usize {
+ (start..end)
+ .skip(1)
+ .filter(|&i| cfg.has_horizontal(i, len))
+ .count()
+ }
+
+ fn inc_range(list: &mut [usize], size: usize, start: usize, end: usize) {
+ if list.is_empty() {
+ return;
+ }
+
+ let span = end - start;
+ let one = size / span;
+ let rest = size - span * one;
+
+ let mut i = start;
+ while i < end {
+ if i == start {
+ list[i] += one + rest;
+ } else {
+ list[i] += one;
+ }
+
+ i += 1;
+ }
+ }
+
+ fn adjust_vspans(
+ cfg: &SpannedConfig,
+ len: usize,
+ spans: &HashMap<Position, (usize, usize)>,
+ widths: &mut [usize],
+ ) {
+ if spans.is_empty() {
+ return;
+ }
+
+ // The overall width distribution will be different depend on the order.
+ //
+ // We sort spans in order to prioritize the smaller spans first.
+ let mut spans_ordered = spans
+ .iter()
+ .map(|(k, v)| ((k.0, k.1), *v))
+ .collect::<Vec<_>>();
+ spans_ordered.sort_unstable_by(|a, b| match a.1 .0.cmp(&b.1 .0) {
+ Ordering::Equal => a.0.cmp(&b.0),
+ o => o,
+ });
+
+ for ((_, col), (span, width)) in spans_ordered {
+ adjust_column_range(cfg, width, len, col, col + span, widths);
+ }
+ }
+
+ fn adjust_column_range(
+ cfg: &SpannedConfig,
+ max_span_width: usize,
+ len: usize,
+ start: usize,
+ end: usize,
+ widths: &mut [usize],
+ ) {
+ let range_width = range_width(cfg, len, start, end, widths);
+ if range_width >= max_span_width {
+ return;
+ }
+
+ inc_range(widths, max_span_width - range_width, start, end);
+ }
+
+ fn range_width(
+ cfg: &SpannedConfig,
+ len: usize,
+ start: usize,
+ end: usize,
+ widths: &[usize],
+ ) -> usize {
+ let count_borders = count_vertical_borders(cfg, len, start, end);
+ let range_width = widths[start..end].iter().sum::<usize>();
+ count_borders + range_width
+ }
+
+ fn count_vertical_borders(cfg: &SpannedConfig, len: usize, start: usize, end: usize) -> usize {
+ (start..end)
+ .skip(1)
+ .filter(|&i| cfg.has_vertical(i, len))
+ .count()
+ }
+
+ pub(super) fn build_height<T: AsRef<str>>(
+ records: &VecRecords<CellInfo<T>>,
+ cfg: &SpannedConfig,
+ ) -> Vec<usize> {
+ let mut heights = vec![];
+ let mut hspans = HashMap::new();
+
+ for (row, columns) in records.iter_rows().enumerate() {
+ let mut row_height = 0;
+ for (col, cell) in columns.iter().enumerate() {
+ let pos = (row, col);
+ if !cfg.is_cell_visible(pos) {
+ continue;
+ }
+
+ let height = cell.count_lines();
+ match cfg.get_row_span(pos) {
+ Some(n) if n > 1 => {
+ let _ = hspans.insert(pos, (n, height));
+ }
+ _ => row_height = max(row_height, height),
+ }
+ }
+
+ heights.push(row_height);
+ }
+
+ adjust_hspans(cfg, heights.len(), &hspans, &mut heights);
+
+ heights
+ }
+
+ pub(super) fn build_width<T: AsRef<str>>(
+ records: &VecRecords<CellInfo<T>>,
+ cfg: &SpannedConfig,
+ ) -> Vec<usize> {
+ let count_columns = records.count_columns();
+
+ let mut widths = vec![0; count_columns];
+ let mut vspans = HashMap::new();
+
+ for (row, columns) in records.iter_rows().enumerate() {
+ for (col, cell) in columns.iter().enumerate() {
+ let pos = (row, col);
+ if !cfg.is_cell_visible(pos) {
+ continue;
+ }
+
+ let width = cell.width();
+ match cfg.get_column_span(pos) {
+ Some(n) if n > 1 => {
+ let _ = vspans.insert(pos, (n, width));
+ }
+ _ => widths[col] = max(widths[col], width),
+ }
+ }
+ }
+
+ adjust_vspans(cfg, count_columns, &vspans, &mut widths);
+
+ widths
+ }
+}
diff --git a/vendor/tabled/src/grid/dimension/pool_table_dimension.rs b/vendor/tabled/src/grid/dimension/pool_table_dimension.rs
new file mode 100644
index 000000000..9909c661d
--- /dev/null
+++ b/vendor/tabled/src/grid/dimension/pool_table_dimension.rs
@@ -0,0 +1,36 @@
+/// PoolTableDimension is a dimension resolve strategy for [`PoolTable`]
+///
+/// [`PoolTable`]: crate::tables::PoolTable
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+pub struct PoolTableDimension {
+ width: DimensionPriority,
+ height: DimensionPriority,
+}
+
+impl PoolTableDimension {
+ /// Creates a new object.
+ pub fn new(width: DimensionPriority, height: DimensionPriority) -> Self {
+ Self { width, height }
+ }
+
+ /// Return a width priority.
+ pub fn width(&self) -> DimensionPriority {
+ self.width
+ }
+
+ /// Return a height priority.
+ pub fn height(&self) -> DimensionPriority {
+ self.height
+ }
+}
+
+/// A control of width/height logic for situations where we must increase some cell to align columns/row.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum DimensionPriority {
+ /// Increase first cell width/height in a row/column.
+ First,
+ /// Increase last cell width/height in a row/column.
+ Last,
+ /// Increase cells width/height 1 by 1 in a row/column.
+ List,
+}
diff --git a/vendor/tabled/src/grid/dimension/static_dimension.rs b/vendor/tabled/src/grid/dimension/static_dimension.rs
new file mode 100644
index 000000000..f9474f212
--- /dev/null
+++ b/vendor/tabled/src/grid/dimension/static_dimension.rs
@@ -0,0 +1,63 @@
+use crate::grid::dimension::{Dimension, Estimate};
+
+/// A constant dimension.
+#[derive(Debug, Clone)]
+pub struct StaticDimension {
+ width: DimensionValue,
+ height: DimensionValue,
+}
+
+impl StaticDimension {
+ /// Creates a constant dimension.
+ pub fn new(width: DimensionValue, height: DimensionValue) -> Self {
+ Self { width, height }
+ }
+}
+
+impl From<StaticDimension> for (DimensionValue, DimensionValue) {
+ fn from(value: StaticDimension) -> Self {
+ (value.width, value.height)
+ }
+}
+
+impl Dimension for StaticDimension {
+ fn get_width(&self, column: usize) -> usize {
+ self.width.get(column)
+ }
+
+ fn get_height(&self, row: usize) -> usize {
+ self.height.get(row)
+ }
+}
+
+impl<R, C> Estimate<R, C> for StaticDimension {
+ fn estimate(&mut self, _: R, _: &C) {}
+}
+
+/// A dimension value.
+#[derive(Debug, Clone)]
+pub enum DimensionValue {
+ /// Const width value.
+ Exact(usize),
+ /// A list of width values for columns.
+ List(Vec<usize>),
+ /// A list of width values for columns and a value for the rest.
+ Partial(Vec<usize>, usize),
+}
+
+impl DimensionValue {
+ /// Get a width by column.
+ pub fn get(&self, col: usize) -> usize {
+ match self {
+ DimensionValue::Exact(val) => *val,
+ DimensionValue::List(cols) => cols[col],
+ DimensionValue::Partial(cols, val) => {
+ if cols.len() > col {
+ cols[col]
+ } else {
+ *val
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/tabled/src/grid/mod.rs b/vendor/tabled/src/grid/mod.rs
new file mode 100644
index 000000000..cdd8c55c0
--- /dev/null
+++ b/vendor/tabled/src/grid/mod.rs
@@ -0,0 +1,48 @@
+//! Module is responsible for tables underlyign grid.
+//!
+//! It might be used when implementing your own [`TableOption`] and [`CellOption`].
+//!
+//! [`TableOption`]: crate::settings::TableOption
+//! [`CellOption`]: crate::settings::CellOption
+#[cfg(feature = "std")]
+mod colored_config;
+
+mod compact_multiline_config;
+
+pub mod dimension;
+pub mod records;
+
+pub use papergrid::color;
+pub use papergrid::colors;
+pub use papergrid::util;
+
+pub mod config {
+ //! Module contains a list of configs for varios tables/grids.
+
+ pub use papergrid::config::{
+ compact::CompactConfig, AlignmentHorizontal, AlignmentVertical, Border, Borders, Entity,
+ EntityIterator, Indent, Line, Position, Sides,
+ };
+
+ #[cfg(feature = "std")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+ pub use papergrid::config::spanned::{
+ EntityMap, Formatting, HorizontalLine, Offset, SpannedConfig, VerticalLine,
+ };
+
+ #[cfg(feature = "std")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+ pub use super::colored_config::{ColorMap, ColoredConfig};
+
+ pub use super::compact_multiline_config::CompactMultilineConfig;
+}
+
+pub use papergrid::grid::compact::CompactGrid;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use papergrid::grid::iterable::Grid;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use papergrid::grid::peekable::PeekableGrid;
diff --git a/vendor/tabled/src/grid/records/empty_records.rs b/vendor/tabled/src/grid/records/empty_records.rs
new file mode 100644
index 000000000..77ebc812d
--- /dev/null
+++ b/vendor/tabled/src/grid/records/empty_records.rs
@@ -0,0 +1,41 @@
+//! An empty [`Records`] implementation.
+
+use core::iter::{repeat, Repeat, Take};
+
+use super::Records;
+
+/// Empty representation of [`Records`].
+#[derive(Debug, Default, Clone)]
+pub struct EmptyRecords {
+ rows: usize,
+ cols: usize,
+}
+
+impl EmptyRecords {
+ /// Constructs an empty representation of [`Records`] with a given shape.
+ pub fn new(rows: usize, cols: usize) -> Self {
+ Self { rows, cols }
+ }
+}
+
+impl From<(usize, usize)> for EmptyRecords {
+ fn from((count_rows, count_columns): (usize, usize)) -> Self {
+ Self::new(count_rows, count_columns)
+ }
+}
+
+impl Records for EmptyRecords {
+ type Iter = Take<Repeat<Take<Repeat<&'static str>>>>;
+
+ fn iter_rows(self) -> Self::Iter {
+ repeat(repeat("").take(self.cols)).take(self.rows)
+ }
+
+ fn count_columns(&self) -> usize {
+ self.cols
+ }
+
+ fn hint_count_rows(&self) -> Option<usize> {
+ Some(self.rows)
+ }
+}
diff --git a/vendor/tabled/src/grid/records/into_records/buf_records.rs b/vendor/tabled/src/grid/records/into_records/buf_records.rs
new file mode 100644
index 000000000..2db3e45bf
--- /dev/null
+++ b/vendor/tabled/src/grid/records/into_records/buf_records.rs
@@ -0,0 +1,213 @@
+//! A module contains [`BufRows`] and [`BufColumns`] iterators.
+//!
+//! Almoust always they both can be used interchangeably but [`BufRows`] is supposed to be lighter cause it
+//! does not reads columns.
+
+use crate::grid::records::IntoRecords;
+
+use super::either_string::EitherString;
+
+/// BufRecords inspects [`IntoRecords`] iterator and keeps read data buffered.
+/// So it can be checking before hand.
+#[derive(Debug)]
+pub struct BufRows<I, T> {
+ iter: I,
+ buf: Vec<T>,
+}
+
+impl BufRows<(), ()> {
+ /// Creates a new [`BufRows`] structure, filling the buffer.
+ pub fn new<I: IntoRecords>(
+ records: I,
+ sniff: usize,
+ ) -> BufRows<<I::IterRows as IntoIterator>::IntoIter, I::IterColumns> {
+ let mut buf = vec![];
+
+ let mut iter = records.iter_rows().into_iter();
+ for _ in 0..sniff {
+ match iter.next() {
+ Some(row) => buf.push(row),
+ None => break,
+ }
+ }
+
+ BufRows { iter, buf }
+ }
+}
+
+impl<I, T> BufRows<I, T> {
+ /// Returns a slice of a record buffer.
+ pub fn as_slice(&self) -> &[T] {
+ &self.buf
+ }
+}
+
+impl<I, T> From<BufRows<I, T>> for BufColumns<I>
+where
+ T: IntoIterator,
+ T::Item: AsRef<str>,
+{
+ fn from(value: BufRows<I, T>) -> Self {
+ let buf = value
+ .buf
+ .into_iter()
+ .map(|row| row.into_iter().map(|s| s.as_ref().to_string()).collect())
+ .collect();
+
+ BufColumns {
+ iter: value.iter,
+ buf,
+ }
+ }
+}
+
+impl<I, T> IntoRecords for BufRows<I, T>
+where
+ I: Iterator<Item = T>,
+ T: IntoIterator,
+ T::Item: AsRef<str>,
+{
+ type Cell = T::Item;
+ type IterColumns = T;
+ type IterRows = BufRowIter<I, T>;
+
+ fn iter_rows(self) -> Self::IterRows {
+ BufRowIter {
+ buf: self.buf.into_iter(),
+ iter: self.iter,
+ }
+ }
+}
+
+/// Buffered [`Iterator`].
+#[derive(Debug)]
+pub struct BufRowIter<I, T> {
+ buf: std::vec::IntoIter<T>,
+ iter: I,
+}
+
+impl<I, T> Iterator for BufRowIter<I, T>
+where
+ I: Iterator<Item = T>,
+ T: IntoIterator,
+ T::Item: AsRef<str>,
+{
+ type Item = T;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.buf.next() {
+ Some(i) => Some(i),
+ None => self.iter.next(),
+ }
+ }
+}
+
+/// BufRecords inspects [`IntoRecords`] iterator and keeps read data buffered.
+/// So it can be checking before hand.
+///
+/// In contrast to [`BufRows`] it keeps records by columns.
+#[derive(Debug)]
+pub struct BufColumns<I> {
+ iter: I,
+ buf: Vec<Vec<String>>,
+}
+
+impl BufColumns<()> {
+ /// Creates new [`BufColumns`] structure, filling the buffer.
+ pub fn new<I: IntoRecords>(
+ records: I,
+ sniff: usize,
+ ) -> BufColumns<<I::IterRows as IntoIterator>::IntoIter> {
+ let mut buf = vec![];
+
+ let mut iter = records.iter_rows().into_iter();
+ for _ in 0..sniff {
+ match iter.next() {
+ Some(row) => {
+ let row = row
+ .into_iter()
+ .map(|cell| cell.as_ref().to_string())
+ .collect::<Vec<_>>();
+ buf.push(row)
+ }
+ None => break,
+ }
+ }
+
+ BufColumns { iter, buf }
+ }
+}
+
+impl<I> BufColumns<I> {
+ /// Returns a slice of a keeping buffer.
+ pub fn as_slice(&self) -> &[Vec<String>] {
+ &self.buf
+ }
+}
+
+impl<I> IntoRecords for BufColumns<I>
+where
+ I: Iterator,
+ I::Item: IntoIterator,
+ <I::Item as IntoIterator>::Item: AsRef<str>,
+{
+ type Cell = EitherString<<I::Item as IntoIterator>::Item>;
+ type IterColumns = EitherRowIterator<<I::Item as IntoIterator>::IntoIter>;
+ type IterRows = BufColumnIter<I>;
+
+ fn iter_rows(self) -> Self::IterRows {
+ BufColumnIter {
+ buf: self.buf.into_iter(),
+ iter: self.iter,
+ }
+ }
+}
+
+/// A row iterator for [`BufColumns`]
+#[derive(Debug)]
+pub struct BufColumnIter<I> {
+ buf: std::vec::IntoIter<Vec<String>>,
+ iter: I,
+}
+
+impl<I> Iterator for BufColumnIter<I>
+where
+ I: Iterator,
+ I::Item: IntoIterator,
+ <I::Item as IntoIterator>::Item: AsRef<str>,
+{
+ type Item = EitherRowIterator<<I::Item as IntoIterator>::IntoIter>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.buf.next() {
+ Some(i) => Some(EitherRowIterator::Owned(i.into_iter())),
+ None => self
+ .iter
+ .next()
+ .map(|i| EitherRowIterator::Some(i.into_iter())),
+ }
+ }
+}
+
+/// An iterator over some iterator or allocated buffer.
+#[derive(Debug)]
+pub enum EitherRowIterator<I> {
+ /// Allocated iterator.
+ Owned(std::vec::IntoIter<String>),
+ /// Given iterator.
+ Some(I),
+}
+
+impl<I> Iterator for EitherRowIterator<I>
+where
+ I: Iterator,
+{
+ type Item = EitherString<I::Item>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self {
+ EitherRowIterator::Owned(iter) => iter.next().map(EitherString::Owned),
+ EitherRowIterator::Some(iter) => iter.next().map(EitherString::Some),
+ }
+ }
+}
diff --git a/vendor/tabled/src/grid/records/into_records/either_string.rs b/vendor/tabled/src/grid/records/into_records/either_string.rs
new file mode 100644
index 000000000..f4d97290d
--- /dev/null
+++ b/vendor/tabled/src/grid/records/into_records/either_string.rs
@@ -0,0 +1,22 @@
+//! A module with a utility enum [`EitherString`].
+
+/// Either allocated string or some type which can be used as a string.
+#[derive(Debug)]
+pub enum EitherString<T> {
+ /// Allocated string.
+ Owned(String),
+ /// Something which can be used as a string.
+ Some(T),
+}
+
+impl<T> AsRef<str> for EitherString<T>
+where
+ T: AsRef<str>,
+{
+ fn as_ref(&self) -> &str {
+ match self {
+ EitherString::Owned(s) => s.as_ref(),
+ EitherString::Some(s) => s.as_ref(),
+ }
+ }
+}
diff --git a/vendor/tabled/src/grid/records/into_records/limit_column_records.rs b/vendor/tabled/src/grid/records/into_records/limit_column_records.rs
new file mode 100644
index 000000000..89b2b89ed
--- /dev/null
+++ b/vendor/tabled/src/grid/records/into_records/limit_column_records.rs
@@ -0,0 +1,82 @@
+//! The module contains [`LimitColumns`] records iterator.
+
+use crate::grid::records::IntoRecords;
+
+/// An iterator which limits amount of columns.
+#[derive(Debug)]
+pub struct LimitColumns<I> {
+ records: I,
+ limit: usize,
+}
+
+impl LimitColumns<()> {
+ /// Creates new [`LimitColumns`].
+ pub fn new<I: IntoRecords>(records: I, limit: usize) -> LimitColumns<I> {
+ LimitColumns { records, limit }
+ }
+}
+
+impl<I> IntoRecords for LimitColumns<I>
+where
+ I: IntoRecords,
+{
+ type Cell = I::Cell;
+ type IterColumns = LimitColumnsColumnsIter<<I::IterColumns as IntoIterator>::IntoIter>;
+ type IterRows = LimitColumnsIter<<I::IterRows as IntoIterator>::IntoIter>;
+
+ fn iter_rows(self) -> Self::IterRows {
+ LimitColumnsIter {
+ iter: self.records.iter_rows().into_iter(),
+ limit: self.limit,
+ }
+ }
+}
+
+/// An iterator over rows for [`LimitColumns`].
+#[derive(Debug)]
+pub struct LimitColumnsIter<I> {
+ iter: I,
+ limit: usize,
+}
+
+impl<I> Iterator for LimitColumnsIter<I>
+where
+ I: Iterator,
+ I::Item: IntoIterator,
+ <I::Item as IntoIterator>::Item: AsRef<str>,
+{
+ type Item = LimitColumnsColumnsIter<<I::Item as IntoIterator>::IntoIter>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let iter = self.iter.next()?;
+ Some(LimitColumnsColumnsIter {
+ iter: iter.into_iter(),
+ limit: self.limit,
+ })
+ }
+}
+
+/// An iterator over columns for [`LimitColumns`].
+#[derive(Debug)]
+pub struct LimitColumnsColumnsIter<I> {
+ iter: I,
+ limit: usize,
+}
+
+impl<I> Iterator for LimitColumnsColumnsIter<I>
+where
+ I: Iterator,
+ I::Item: AsRef<str>,
+{
+ type Item = I::Item;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.limit == 0 {
+ return None;
+ }
+
+ self.limit -= 1;
+
+ self.iter.next()
+ }
+}
diff --git a/vendor/tabled/src/grid/records/into_records/limit_row_records.rs b/vendor/tabled/src/grid/records/into_records/limit_row_records.rs
new file mode 100644
index 000000000..a461c6682
--- /dev/null
+++ b/vendor/tabled/src/grid/records/into_records/limit_row_records.rs
@@ -0,0 +1,59 @@
+//! The module contains [`LimitRows`] records iterator.
+
+use crate::grid::records::IntoRecords;
+
+/// [`LimitRows`] is an records iterator which limits amount of rows.
+#[derive(Debug)]
+pub struct LimitRows<I> {
+ records: I,
+ limit: usize,
+}
+
+impl LimitRows<()> {
+ /// Creates new [`LimitRows`] iterator.
+ pub fn new<I: IntoRecords>(records: I, limit: usize) -> LimitRows<I> {
+ LimitRows { records, limit }
+ }
+}
+
+impl<I> IntoRecords for LimitRows<I>
+where
+ I: IntoRecords,
+{
+ type Cell = I::Cell;
+ type IterColumns = I::IterColumns;
+ type IterRows = LimitRowsIter<<I::IterRows as IntoIterator>::IntoIter>;
+
+ fn iter_rows(self) -> Self::IterRows {
+ LimitRowsIter {
+ iter: self.records.iter_rows().into_iter(),
+ limit: self.limit,
+ }
+ }
+}
+
+/// A rows iterator for [`LimitRows`]
+#[derive(Debug)]
+pub struct LimitRowsIter<I> {
+ iter: I,
+ limit: usize,
+}
+
+impl<I> Iterator for LimitRowsIter<I>
+where
+ I: Iterator,
+ I::Item: IntoIterator,
+ <I::Item as IntoIterator>::Item: AsRef<str>,
+{
+ type Item = I::Item;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.limit == 0 {
+ return None;
+ }
+
+ self.limit -= 1;
+
+ self.iter.next()
+ }
+}
diff --git a/vendor/tabled/src/grid/records/into_records/mod.rs b/vendor/tabled/src/grid/records/into_records/mod.rs
new file mode 100644
index 000000000..0a52c41c1
--- /dev/null
+++ b/vendor/tabled/src/grid/records/into_records/mod.rs
@@ -0,0 +1,26 @@
+//! The module contains a list of helpers for [`IntoRecords`]
+//!
+//! [`IntoRecords`]: crate::grid::records::IntoRecords
+
+pub mod limit_column_records;
+pub mod limit_row_records;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod buf_records;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod either_string;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod truncate_records;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use buf_records::{BufColumns, BufRows};
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use truncate_records::TruncateContent;
+
+pub use limit_column_records::LimitColumns;
+pub use limit_row_records::LimitRows;
diff --git a/vendor/tabled/src/grid/records/into_records/truncate_records.rs b/vendor/tabled/src/grid/records/into_records/truncate_records.rs
new file mode 100644
index 000000000..17e7e533e
--- /dev/null
+++ b/vendor/tabled/src/grid/records/into_records/truncate_records.rs
@@ -0,0 +1,136 @@
+//! The module contains [`TruncateContent`] records iterator.
+
+use std::borrow::Cow;
+
+use crate::{
+ grid::records::IntoRecords, grid::util::string::string_width_multiline,
+ settings::width::Truncate,
+};
+
+use super::either_string::EitherString;
+
+/// A records iterator which truncates all cells to a given width.
+#[derive(Debug)]
+pub struct TruncateContent<'a, I> {
+ records: I,
+ width: ExactValue<'a>,
+}
+
+impl TruncateContent<'_, ()> {
+ /// Creates new [`TruncateContent`] object.
+ pub fn new<I: IntoRecords>(records: I, width: ExactValue<'_>) -> TruncateContent<'_, I> {
+ TruncateContent { records, width }
+ }
+}
+
+impl<'a, I> IntoRecords for TruncateContent<'a, I>
+where
+ I: IntoRecords,
+{
+ type Cell = EitherString<I::Cell>;
+ type IterColumns = TruncateContentColumnsIter<'a, <I::IterColumns as IntoIterator>::IntoIter>;
+ type IterRows = TruncateContentIter<'a, <I::IterRows as IntoIterator>::IntoIter>;
+
+ fn iter_rows(self) -> Self::IterRows {
+ TruncateContentIter {
+ iter: self.records.iter_rows().into_iter(),
+ width: self.width.clone(),
+ }
+ }
+}
+
+/// A row iterator for [`TruncateContent`].
+#[derive(Debug)]
+pub struct TruncateContentIter<'a, I> {
+ iter: I,
+ width: ExactValue<'a>,
+}
+
+impl<'a, I> Iterator for TruncateContentIter<'a, I>
+where
+ I: Iterator,
+ I::Item: IntoIterator,
+ <I::Item as IntoIterator>::Item: AsRef<str>,
+{
+ type Item = TruncateContentColumnsIter<'a, <I::Item as IntoIterator>::IntoIter>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let iter = self.iter.next()?;
+ Some(TruncateContentColumnsIter {
+ iter: iter.into_iter(),
+ current: 0,
+ width: self.width.clone(),
+ })
+ }
+}
+
+/// A column iterator for [`TruncateContent`].
+#[derive(Debug)]
+pub struct TruncateContentColumnsIter<'a, I> {
+ iter: I,
+ width: ExactValue<'a>,
+ current: usize,
+}
+
+impl<I> Iterator for TruncateContentColumnsIter<'_, I>
+where
+ I: Iterator,
+ I::Item: AsRef<str>,
+{
+ type Item = EitherString<I::Item>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let s = self.iter.next()?;
+
+ let width = self.width.get(self.current);
+ self.current += 1;
+
+ let text_width = string_width_multiline(s.as_ref());
+ if text_width <= width {
+ return Some(EitherString::Some(s));
+ }
+
+ let text = Truncate::truncate_text(s.as_ref(), width);
+ let text = text.into_owned();
+ let text = EitherString::Owned(text);
+
+ Some(text)
+ }
+}
+
+/// A width value.
+#[derive(Debug, Clone)]
+pub enum ExactValue<'a> {
+ /// Const width value.
+ Exact(usize),
+ /// A list of width values for columns.
+ List(Cow<'a, [usize]>),
+}
+
+impl<'a> From<&'a [usize]> for ExactValue<'a> {
+ fn from(value: &'a [usize]) -> Self {
+ Self::List(value.into())
+ }
+}
+
+impl From<Vec<usize>> for ExactValue<'_> {
+ fn from(value: Vec<usize>) -> Self {
+ Self::List(value.into())
+ }
+}
+
+impl From<usize> for ExactValue<'_> {
+ fn from(value: usize) -> Self {
+ Self::Exact(value)
+ }
+}
+
+impl ExactValue<'_> {
+ /// Get a width by column.
+ pub fn get(&self, col: usize) -> usize {
+ match self {
+ ExactValue::Exact(val) => *val,
+ ExactValue::List(cols) => cols[col],
+ }
+ }
+}
diff --git a/vendor/tabled/src/grid/records/mod.rs b/vendor/tabled/src/grid/records/mod.rs
new file mode 100644
index 000000000..494002346
--- /dev/null
+++ b/vendor/tabled/src/grid/records/mod.rs
@@ -0,0 +1,19 @@
+//! The module contains [`Records`], [`ExactRecords`], [`RecordsMut`], [`Resizable`] traits
+//! and its implementations.
+//!
+//! Also it provies a list of helpers for a user built [`Records`] via [`into_records`].
+
+mod empty_records;
+mod records_mut;
+mod resizable;
+
+pub mod into_records;
+
+pub use empty_records::EmptyRecords;
+pub use papergrid::records::{ExactRecords, IntoRecords, IterRecords, PeekableRecords, Records};
+pub use records_mut::RecordsMut;
+pub use resizable::Resizable;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use papergrid::records::vec_records;
diff --git a/vendor/tabled/src/grid/records/records_mut.rs b/vendor/tabled/src/grid/records/records_mut.rs
new file mode 100644
index 000000000..38c42d2c7
--- /dev/null
+++ b/vendor/tabled/src/grid/records/records_mut.rs
@@ -0,0 +1,34 @@
+use crate::grid::config::Position;
+#[cfg(feature = "std")]
+use crate::grid::records::vec_records::{CellInfo, VecRecords};
+
+/// A [`Records`] representation which can modify cell by (row, column) index.
+///
+/// [`Records`]: crate::grid::records::Records
+pub trait RecordsMut<Text> {
+ /// Sets a text to a given cell by index.
+ fn set(&mut self, pos: Position, text: Text);
+}
+
+impl<T, Text> RecordsMut<Text> for &'_ mut T
+where
+ T: RecordsMut<Text>,
+{
+ fn set(&mut self, pos: Position, text: Text) {
+ T::set(self, pos, text)
+ }
+}
+
+#[cfg(feature = "std")]
+impl RecordsMut<String> for VecRecords<CellInfo<String>> {
+ fn set(&mut self, (row, col): Position, text: String) {
+ self[row][col] = CellInfo::new(text);
+ }
+}
+
+#[cfg(feature = "std")]
+impl RecordsMut<&str> for VecRecords<CellInfo<String>> {
+ fn set(&mut self, (row, col): Position, text: &str) {
+ self[row][col] = CellInfo::new(text.to_string());
+ }
+}
diff --git a/vendor/tabled/src/grid/records/resizable.rs b/vendor/tabled/src/grid/records/resizable.rs
new file mode 100644
index 000000000..29ab38038
--- /dev/null
+++ b/vendor/tabled/src/grid/records/resizable.rs
@@ -0,0 +1,217 @@
+use papergrid::config::Position;
+
+#[cfg(feature = "std")]
+use crate::grid::records::vec_records::VecRecords;
+
+/// A records representation which can be modified by moving rows/columns around.
+pub trait Resizable {
+ /// Swap cells with one another.
+ fn swap(&mut self, lhs: Position, rhs: Position);
+ /// Swap rows with one another.
+ fn swap_row(&mut self, lhs: usize, rhs: usize);
+ /// Swap columns with one another.
+ fn swap_column(&mut self, lhs: usize, rhs: usize);
+ /// Adds a new row to a data set.
+ fn push_row(&mut self);
+ /// Adds a new column to a data set.
+ fn push_column(&mut self);
+ /// Removes a row from a data set by index.
+ fn remove_row(&mut self, row: usize);
+ /// Removes a column from a data set by index.
+ fn remove_column(&mut self, column: usize);
+ /// Inserts a row at index.
+ fn insert_row(&mut self, row: usize);
+ /// Inserts column at index.
+ fn insert_column(&mut self, column: usize);
+}
+
+impl<T> Resizable for &'_ mut T
+where
+ T: Resizable,
+{
+ fn swap(&mut self, lhs: Position, rhs: Position) {
+ T::swap(self, lhs, rhs)
+ }
+
+ fn swap_row(&mut self, lhs: usize, rhs: usize) {
+ T::swap_row(self, lhs, rhs)
+ }
+
+ fn swap_column(&mut self, lhs: usize, rhs: usize) {
+ T::swap_column(self, lhs, rhs)
+ }
+
+ fn push_row(&mut self) {
+ T::push_row(self)
+ }
+
+ fn push_column(&mut self) {
+ T::push_column(self)
+ }
+
+ fn remove_row(&mut self, row: usize) {
+ T::remove_row(self, row)
+ }
+
+ fn remove_column(&mut self, column: usize) {
+ T::remove_column(self, column)
+ }
+
+ fn insert_row(&mut self, row: usize) {
+ T::insert_row(self, row)
+ }
+
+ fn insert_column(&mut self, column: usize) {
+ T::insert_column(self, column)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<T> Resizable for Vec<Vec<T>>
+where
+ T: Default + Clone,
+{
+ fn swap(&mut self, lhs: Position, rhs: Position) {
+ if lhs == rhs {
+ return;
+ }
+
+ let t = std::mem::take(&mut self[lhs.0][lhs.1]);
+ let t = std::mem::replace(&mut self[rhs.0][rhs.1], t);
+ let _ = std::mem::replace(&mut self[lhs.0][lhs.1], t);
+ }
+
+ fn swap_row(&mut self, lhs: usize, rhs: usize) {
+ let t = std::mem::take(&mut self[lhs]);
+ let t = std::mem::replace(&mut self[rhs], t);
+ let _ = std::mem::replace(&mut self[lhs], t);
+ }
+
+ fn swap_column(&mut self, lhs: usize, rhs: usize) {
+ for row in self.iter_mut() {
+ row.swap(lhs, rhs);
+ }
+ }
+
+ fn push_row(&mut self) {
+ let count_columns = self.get(0).map(|l| l.len()).unwrap_or(0);
+ self.push(vec![T::default(); count_columns]);
+ }
+
+ fn push_column(&mut self) {
+ for row in self.iter_mut() {
+ row.push(T::default());
+ }
+ }
+
+ fn remove_row(&mut self, row: usize) {
+ let _ = self.remove(row);
+ }
+
+ fn remove_column(&mut self, column: usize) {
+ for row in self.iter_mut() {
+ let _ = row.remove(column);
+ }
+ }
+
+ fn insert_row(&mut self, row: usize) {
+ let count_columns = self.get(0).map(|l| l.len()).unwrap_or(0);
+ self.insert(row, vec![T::default(); count_columns]);
+ }
+
+ fn insert_column(&mut self, column: usize) {
+ for row in self {
+ row.insert(column, T::default());
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<T> Resizable for VecRecords<T>
+where
+ T: Default + Clone,
+{
+ fn swap(&mut self, lhs: Position, rhs: Position) {
+ if lhs == rhs {
+ return;
+ }
+
+ let t = std::mem::take(&mut self[lhs.0][lhs.1]);
+ let t = std::mem::replace(&mut self[rhs.0][rhs.1], t);
+ let _ = std::mem::replace(&mut self[lhs.0][lhs.1], t);
+ }
+
+ fn swap_row(&mut self, lhs: usize, rhs: usize) {
+ let t = std::mem::take(&mut self[lhs]);
+ let t = std::mem::replace(&mut self[rhs], t);
+ let _ = std::mem::replace(&mut self[lhs], t);
+ }
+
+ fn swap_column(&mut self, lhs: usize, rhs: usize) {
+ for row in self.iter_mut() {
+ row.swap(lhs, rhs);
+ }
+ }
+
+ fn push_row(&mut self) {
+ let records = std::mem::replace(self, VecRecords::new(vec![]));
+ let mut data: Vec<Vec<_>> = records.into();
+
+ let count_columns = data.get(0).map(|l| l.len()).unwrap_or(0);
+ data.push(vec![T::default(); count_columns]);
+
+ *self = VecRecords::new(data);
+ }
+
+ fn push_column(&mut self) {
+ let records = std::mem::replace(self, VecRecords::new(vec![]));
+ let mut data: Vec<Vec<_>> = records.into();
+
+ for row in &mut data {
+ row.push(T::default());
+ }
+
+ *self = VecRecords::new(data);
+ }
+
+ fn remove_row(&mut self, row: usize) {
+ let records = std::mem::replace(self, VecRecords::new(vec![]));
+ let mut data: Vec<Vec<_>> = records.into();
+
+ let _ = data.remove(row);
+
+ *self = VecRecords::new(data);
+ }
+
+ fn remove_column(&mut self, column: usize) {
+ let records = std::mem::replace(self, VecRecords::new(vec![]));
+ let mut data: Vec<Vec<_>> = records.into();
+
+ for row in &mut data {
+ let _ = row.remove(column);
+ }
+
+ *self = VecRecords::new(data);
+ }
+
+ fn insert_row(&mut self, row: usize) {
+ let records = std::mem::replace(self, VecRecords::new(vec![]));
+ let mut data: Vec<Vec<_>> = records.into();
+
+ let count_columns = data.get(0).map(|l| l.len()).unwrap_or(0);
+ data.insert(row, vec![T::default(); count_columns]);
+
+ *self = VecRecords::new(data);
+ }
+
+ fn insert_column(&mut self, column: usize) {
+ let records = std::mem::replace(self, VecRecords::new(vec![]));
+ let mut data: Vec<Vec<_>> = records.into();
+
+ for row in &mut data {
+ row.insert(column, T::default());
+ }
+
+ *self = VecRecords::new(data);
+ }
+}
diff --git a/vendor/tabled/src/lib.rs b/vendor/tabled/src/lib.rs
new file mode 100644
index 000000000..fb008b302
--- /dev/null
+++ b/vendor/tabled/src/lib.rs
@@ -0,0 +1,490 @@
+//! An easy to use library for pretty print tables of Rust `struct`s and `enum`s.
+//!
+//! The library supports different approaches of table building.
+//! You can use [`Tabled`] trait if the data type is known.
+//! Or you can use [`Builder`] to construct the table from scratch.
+//!
+//! ## Usage
+//!
+//! If you want to build a table for your custom type.
+//! A starting point is to a anotate your type with `#[derive(Tabled)]`.
+//!
+//! Then to provide your collection to [`Table::new`] and you will be set to render table.
+//!
+#![cfg_attr(all(feature = "derive", feature = "std"), doc = "```")]
+#![cfg_attr(not(all(feature = "derive", feature = "std")), doc = "```ignore")]
+//! use tabled::{Tabled, Table};
+//!
+//! #[derive(Tabled)]
+//! struct Language {
+//! name: &'static str,
+//! designed_by: &'static str,
+//! invented_year: usize,
+//! }
+//!
+//! let languages = vec![
+//! Language{
+//! name: "C",
+//! designed_by: "Dennis Ritchie",
+//! invented_year: 1972
+//! },
+//! Language{
+//! name: "Rust",
+//! designed_by: "Graydon Hoare",
+//! invented_year: 2010
+//! },
+//! Language{
+//! name: "Go",
+//! designed_by: "Rob Pike",
+//! invented_year: 2009
+//! },
+//! ];
+//!
+//! let table = Table::new(languages).to_string();
+//!
+//! let expected = "+------+----------------+---------------+\n\
+//! | name | designed_by | invented_year |\n\
+//! +------+----------------+---------------+\n\
+//! | C | Dennis Ritchie | 1972 |\n\
+//! +------+----------------+---------------+\n\
+//! | Rust | Graydon Hoare | 2010 |\n\
+//! +------+----------------+---------------+\n\
+//! | Go | Rob Pike | 2009 |\n\
+//! +------+----------------+---------------+";
+//!
+//! assert_eq!(table, expected);
+//! ```
+//!
+//! Not all types can derive [`Tabled`] trait though.
+//! The example below can't be compiled.
+//!
+//! ```rust,compile_fail
+//! # use tabled::Tabled;
+//! #[derive(Tabled)]
+//! struct SomeType {
+//! field1: SomeOtherType,
+//! }
+//!
+//! struct SomeOtherType;
+//! ```
+//!
+//! Because `tabled` must know what we're up to print as a field, so
+//! each (almoust) field must implement [`std::fmt::Display`].
+//!
+//! ### Default implementations
+//!
+//! [`Table`] can be build from vast majority of Rust's standard types.
+//! This allows you to run the following code.
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Tabled, Table};
+//! let table = Table::new(&[1, 2, 3]);
+//! # let expected = "+-----+\n\
+//! # | i32 |\n\
+//! # +-----+\n\
+//! # | 1 |\n\
+//! # +-----+\n\
+//! # | 2 |\n\
+//! # +-----+\n\
+//! # | 3 |\n\
+//! # +-----+";
+//! # assert_eq!(table.to_string(), expected);
+//! ```
+//!
+//! ### Dynamic table
+//!
+//! When you data scheme is not known at compile time.
+//! You most likely will not able to relay on [`Tabled`] trait.
+//!
+//! So one option would be is to use [`Builder`].
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use std::iter;
+//!
+//! use tabled::{
+//! builder::Builder,
+//! settings::{Modify, object::Rows, Alignment, Style}
+//! };
+//!
+//! let (x, y) = (3, 10);
+//!
+//! let mut builder = Builder::default();
+//!
+//! let header = iter::once(String::from("i"))
+//! .chain((0..y)
+//! .map(|i| i.to_string()));
+//! builder.set_header(header);
+//!
+//! for i in 0..x {
+//! let row = iter::once(i)
+//! .chain((0..y).map(|j| i * j))
+//! .map(|i| i.to_string());
+//! builder.push_record(row);
+//! }
+//!
+//! let table = builder.build()
+//! .with(Style::rounded())
+//! .with(Modify::new(Rows::new(1..)).with(Alignment::left()))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "╭───┬───┬───┬───┬───┬───┬────┬────┬────┬────┬────╮\n",
+//! "│ i │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │\n",
+//! "├───┼───┼───┼───┼───┼───┼────┼────┼────┼────┼────┤\n",
+//! "│ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │\n",
+//! "│ 1 │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │\n",
+//! "│ 2 │ 0 │ 2 │ 4 │ 6 │ 8 │ 10 │ 12 │ 14 │ 16 │ 18 │\n",
+//! "╰───┴───┴───┴───┴───┴───┴────┴────┴────┴────┴────╯",
+//! )
+//! );
+//! ```
+//!
+//! ### Build table using [`row!`] and [`col!`] macros.
+//!
+#![cfg_attr(all(feature = "macros", feature = "std"), doc = "```")]
+#![cfg_attr(not(all(feature = "macros", feature = "std")), doc = "```ignore")]
+//! use tabled::{row, col};
+//!
+//! let table = row![
+//! col!["Hello", "World", "!"],
+//! col!["Hello"; 3],
+//! col!["World"; 3],
+//! ];
+//!
+//! assert_eq!(
+//! table.to_string(),
+//! concat!(
+//! "+-----------+-----------+-----------+\n",
+//! "| +-------+ | +-------+ | +-------+ |\n",
+//! "| | Hello | | | Hello | | | World | |\n",
+//! "| +-------+ | +-------+ | +-------+ |\n",
+//! "| | World | | | Hello | | | World | |\n",
+//! "| +-------+ | +-------+ | +-------+ |\n",
+//! "| | ! | | | Hello | | | World | |\n",
+//! "| +-------+ | +-------+ | +-------+ |\n",
+//! "+-----------+-----------+-----------+",
+//! )
+//! );
+//! ```
+//!
+//! ### Settings
+//!
+//! You can use many settings which is found in [`tabled::settings`] module.
+//!
+//! # Advanced
+//!
+//! ## Alloc
+//!
+//! [`Table`] keeps data buffered, which sometimes not ideal choise.
+//! For such reason there is [`IterTable`] and [`CompactTable`].
+//!
+//! ### Less allocations
+//!
+//! [`IterTable`] stands on a middle ground between [`Table`] and [`CompactTable`].
+//!
+//! It does allocate memory but in a much smaller chunks that a [`Table`] does.
+//! The benefit is that it can be used interchangebly with [`Table`].
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::tables::IterTable;
+//!
+//! let iterator = (0..3).map(|row| (0..4).map(move |col| format!("{}-{}", row, col)));
+//!
+//! let table = IterTable::new(iterator).to_string();
+//!
+//! assert_eq!(
+//! table,
+//! "+-----+-----+-----+-----+\n\
+//! | 0-0 | 0-1 | 0-2 | 0-3 |\n\
+//! +-----+-----+-----+-----+\n\
+//! | 1-0 | 1-1 | 1-2 | 1-3 |\n\
+//! +-----+-----+-----+-----+\n\
+//! | 2-0 | 2-1 | 2-2 | 2-3 |\n\
+//! +-----+-----+-----+-----+",
+//! );
+//! ```
+//!
+//! ## Alloc free (`#nostd`)
+//!
+//! [`CompactTable`] can be configured ('1) to not make any allocations.
+//! But the price is that the set of settings which can be applied to it is limited.
+//!
+//! It also can be printed directly to [`fmt::Write`] to not have any intermidiaries.
+//!
+//! '1. It does not make any allocations in case you provide it with `width` and `count_rows`.
+//!
+//! ```
+//! use tabled::{settings::Style, tables::CompactTable};
+//! use core::fmt::{Write, Result};
+//!
+//! struct StubWriter;
+//!
+//! impl Write for StubWriter {
+//! fn write_str(&mut self, _: &str) -> Result {
+//! Ok(())
+//! }
+//! }
+//!
+//! let data = [
+//! ["FreeBSD", "1993", "William and Lynne Jolitz", "?"],
+//! ["OpenBSD", "1995", "Theo de Raadt", ""],
+//! ["HardenedBSD", "2014", "Oliver Pinter and Shawn Webb", ""],
+//! ];
+//!
+//! let table = CompactTable::from(data).with(Style::psql());
+//!
+//! table.fmt(StubWriter);
+//! ```
+//!
+//! ## More information
+//!
+//! You can find more examples of settings and attributes in
+//! [README.md](https://github.com/zhiburt/tabled/blob/master/README.md)
+//!
+//! [`Builder`]: crate::builder::Builder
+//! [`IterTable`]: crate::tables::IterTable
+//! [`CompactTable`]: crate::tables::CompactTable
+//! [`fmt::Write`]: core::fmt::Write
+//! [`row!`]: crate::row
+//! [`col!`]: crate::col
+
+#![cfg_attr(not(any(feature = "std", test)), no_std)]
+#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
+#![doc(
+ html_logo_url = "https://raw.githubusercontent.com/zhiburt/tabled/86ac146e532ce9f7626608d7fd05072123603a2e/assets/tabled-gear.svg"
+)]
+#![deny(unused_must_use)]
+#![warn(
+ missing_docs,
+ rust_2018_idioms,
+ rust_2018_compatibility,
+ missing_debug_implementations,
+ unreachable_pub,
+ future_incompatible,
+ single_use_lifetimes,
+ trivial_casts,
+ trivial_numeric_casts,
+ unused_extern_crates,
+ unused_import_braces,
+ unused_qualifications,
+ unused_results,
+ unused_variables,
+ variant_size_differences
+)]
+#![allow(clippy::uninlined_format_args)]
+
+#[cfg(feature = "std")]
+mod tabled;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod builder;
+pub mod settings;
+pub mod tables;
+
+#[cfg(feature = "macros")]
+#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
+pub mod macros;
+
+pub mod grid;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use crate::{tabled::Tabled, tables::Table};
+
+/// A derive to implement a [`Tabled`] trait.
+///
+/// The macros available only when `derive` feature in turned on (and it is by default).
+///
+/// To be able to use the derive each field must implement `std::fmt::Display`.
+/// The following example will cause a error because of that.
+///
+/// ```rust,compile_fail
+/// use tabled::Tabled;
+/// #[derive(Tabled)]
+/// struct SomeType {
+/// field1: SomeOtherType,
+/// }
+///
+/// struct SomeOtherType;
+/// ```
+///
+/// Bellow you'll find available options for it.
+///
+/// ### Override a column name
+///
+/// You can use a `#[tabled(rename = "")]` attribute to override a column name.
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// struct Person {
+/// #[tabled(rename = "Name")]
+/// first_name: &'static str,
+/// #[tabled(rename = "Surname")]
+/// last_name: &'static str,
+/// }
+/// ```
+///
+/// ### Hide a column
+///
+/// You can mark fields as hidden in which case they fill be ignored and not be present on a sheet.
+///
+/// A similar affect could be achieved by the means of a `Disable` setting.
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// struct Person {
+/// id: u8,
+/// #[tabled(skip)]
+/// number: &'static str,
+/// name: &'static str,
+/// }
+/// ```
+///
+/// ### Set column order
+///
+/// You can change the order in which they will be displayed in table.
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// struct Person {
+/// id: u8,
+/// #[tabled(order = 0)]
+/// number: &'static str,
+/// #[tabled(order = 1)]
+/// name: &'static str,
+/// }
+/// ```
+///
+/// ### Format fields
+///
+/// As was said already, using `#[derive(Tabled)]` is possible only when all fields implement a `Display` trait.
+/// However, this may be often not the case for example when a field uses the `Option` type. There's 2 common ways how to solve this:
+///
+/// - Implement `Tabled` trait manually for a type.
+/// - Wrap `Option` to something like `DisplayedOption<T>(Option<T>)` and implement a Display trait for it.
+///
+/// Alternatively, you can use the `#[tabled(display_with = "func")]` attribute for the field to specify a display function.
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// pub struct MyRecord {
+/// pub id: i64,
+/// #[tabled(display_with = "display_option")]
+/// pub valid: Option<bool>
+/// }
+///
+/// fn display_option(o: &Option<bool>) -> String {
+/// match o {
+/// Some(s) => format!("is valid thing = {}", s),
+/// None => format!("is not valid"),
+/// }
+/// }
+/// ```
+///
+/// It's also possible to change function argument to be `&self`,
+/// using `#[tabled(display_with("some_function", self))]`
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// pub struct MyRecord {
+/// pub id: i64,
+/// #[tabled(display_with("Self::display_valid", self))]
+/// pub valid: Option<bool>
+/// }
+///
+/// impl MyRecord {
+/// fn display_valid(&self) -> String {
+/// match self.valid {
+/// Some(s) => format!("is valid thing = {}", s),
+/// None => format!("is not valid"),
+/// }
+/// }
+/// }
+/// ```
+///
+/// ### Format headers
+///
+/// Beside `#[tabled(rename = "")]` you can change a format of a column name using
+/// `#[tabled(rename_all = "UPPERCASE")]`.
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// #[tabled(rename_all = "CamelCase")]
+/// struct Person {
+/// id: u8,
+/// number: &'static str,
+/// name: &'static str,
+/// #[tabled(rename_all = "snake_case")]
+/// middle_name: &'static str,
+/// }
+/// ```
+///
+/// ### Inline
+///
+/// It's possible to inline internal data if it implements the `Tabled` trait using `#[tabled(inline)]`.
+/// You can also set a prefix which will be used for all inlined elements by `#[tabled(inline("prefix>>"))]`.
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// struct Person {
+/// id: u8,
+/// name: &'static str,
+/// #[tabled(inline)]
+/// ed: Education,
+/// }
+///
+/// #[derive(Tabled)]
+/// struct Education {
+/// uni: &'static str,
+/// graduated: bool,
+/// }
+/// ```
+///
+/// And it works for enums as well.
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// enum Vehicle {
+/// #[tabled(inline("Auto::"))]
+/// Auto {
+/// model: &'static str,
+/// engine: &'static str,
+/// },
+/// #[tabled(inline)]
+/// Bikecycle(
+/// &'static str,
+/// #[tabled(inline)] Bike,
+/// ),
+/// }
+///
+/// #[derive(Tabled)]
+/// struct Bike {
+/// brand: &'static str,
+/// price: f32,
+/// }
+/// ```
+#[cfg(feature = "derive")]
+#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
+pub use tabled_derive::Tabled;
diff --git a/vendor/tabled/src/macros/col.rs b/vendor/tabled/src/macros/col.rs
new file mode 100644
index 000000000..1fd67de8a
--- /dev/null
+++ b/vendor/tabled/src/macros/col.rs
@@ -0,0 +1,50 @@
+/// Creates a [`Table`] with [`Display`] arguments nested within.
+///
+/// The macros allows several tables to be displayed vertically.
+///
+/// Companion to [`row!`].
+///
+/// # Examples
+/// ```rust,no_run
+/// # use tabled::{row, col, Table};
+/// # let (table1, table2, table3) = (Table::new(&[String::new()]), Table::new(&[String::new()]), Table::new(&[String::new()]));
+/// let new_table = col![table1, table2];
+/// let new_table_of_clones = col![table1; 3];
+/// let columns_and_rows = col![
+/// table1,
+/// row![table2, table3]
+/// ];
+/// ```
+///
+/// [`row!`]: crate::row
+/// [`Table`]: crate::Table
+/// [`Display`]: std::fmt::Display
+#[macro_export]
+#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
+macro_rules! col {
+ // Vertical
+ ( $($table:expr), * $(,)? ) => {{
+ let mut builder = $crate::builder::Builder::default();
+
+ $(
+ builder.push_record([$table.to_string()]);
+ )*
+
+ builder.build()
+ }};
+
+ // Duplicate single item
+ ( $table:expr; $N:expr) => {{
+ let mut builder = $crate::builder::Builder::default();
+
+ let n = $N;
+ if n > 0 {
+ let t = $table.to_string();
+ for _ in 0..$N {
+ builder.push_record([t.clone()]);
+ }
+ }
+
+ builder.build()
+ }};
+}
diff --git a/vendor/tabled/src/macros/mod.rs b/vendor/tabled/src/macros/mod.rs
new file mode 100644
index 000000000..7db0a9caa
--- /dev/null
+++ b/vendor/tabled/src/macros/mod.rs
@@ -0,0 +1,6 @@
+//! This module contains macro functions for dynamic [`Table`] construction.
+//!
+//! [`Table`]: crate::Table
+
+mod col;
+mod row;
diff --git a/vendor/tabled/src/macros/row.rs b/vendor/tabled/src/macros/row.rs
new file mode 100644
index 000000000..23775b90d
--- /dev/null
+++ b/vendor/tabled/src/macros/row.rs
@@ -0,0 +1,44 @@
+/// Creates a [`Table`] with [`Display`] arguments nested within.
+///
+/// The macros allows several tables to be displayed horizontally.
+///
+/// Companion to [`col!`].
+///
+/// # Examples
+/// ```rust,no_run
+/// # use tabled::{row, col, Table};
+/// # let (table1, table2, table3) = (Table::new(&[String::new()]), Table::new(&[String::new()]), Table::new(&[String::new()]));
+/// let new_table = row![table1, table2];
+/// let new_table_of_clones = row![table1; 3];
+/// let rows_and_columns = row![
+/// table1,
+/// col![table2, table3]
+/// ];
+/// ```
+///
+/// [`col!`]: crate::col
+/// [`Table`]: crate::Table
+/// [`Display`]: std::fmt::Display
+#[macro_export]
+#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
+macro_rules! row {
+ // Horizontal Display
+ ( $($table:expr), * $(,)? ) => {{
+ let mut builder = $crate::builder::Builder::default();
+
+ let record = [ $($table.to_string(),)* ];
+ builder.push_record(record);
+
+ builder.build()
+ }};
+
+ // Duplicate single item
+ ( $table:expr; $N:expr) => {{
+ let mut builder = $crate::builder::Builder::default();
+
+ let duplicates = vec![$table.to_string(); $N];
+ builder.push_record(duplicates);
+
+ builder.build()
+ }};
+}
diff --git a/vendor/tabled/src/settings/alignment/mod.rs b/vendor/tabled/src/settings/alignment/mod.rs
new file mode 100644
index 000000000..81d1717a6
--- /dev/null
+++ b/vendor/tabled/src/settings/alignment/mod.rs
@@ -0,0 +1,189 @@
+//! This module contains an [`Alignment`] setting for cells on the [`Table`].
+//!
+//! # Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! # use tabled::{Table, settings::{Alignment, Modify, object::Rows}};
+//! # let data: Vec<&'static str> = Vec::new();
+//! let mut table = Table::new(&data);
+//! table.with(Modify::new(Rows::single(0)).with(Alignment::center()));
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use crate::{
+ grid::config::CompactConfig,
+ grid::config::{AlignmentHorizontal, AlignmentVertical, CompactMultilineConfig},
+ settings::TableOption,
+};
+
+use AlignmentInner::*;
+
+#[cfg(feature = "std")]
+use crate::grid::config::{ColoredConfig, Entity};
+
+/// Alignment represent a horizontal and vertical alignment setting for any cell on a [`Table`].
+///
+/// An alignment strategy can be set by [`AlignmentStrategy`].
+///
+/// # Example
+///
+#[cfg_attr(feature = "std", doc = "```")]
+#[cfg_attr(not(feature = "std"), doc = "```ignore")]
+/// use tabled::{
+/// Table,
+/// settings::{
+/// formatting::AlignmentStrategy,
+/// object::Segment, Alignment, Modify, Style,
+/// }
+/// };
+///
+/// let data = [
+/// ["1", "2", "3"],
+/// ["Some\nMulti\nLine\nText", "and a line", "here"],
+/// ["4", "5", "6"],
+/// ];
+///
+/// let mut table = Table::new(&data);
+/// table
+/// .with(Style::modern())
+/// .with(
+/// Modify::new(Segment::all())
+/// .with(Alignment::right())
+/// .with(Alignment::center())
+/// .with(AlignmentStrategy::PerCell)
+/// );
+///
+/// assert_eq!(
+/// table.to_string(),
+/// concat!(
+/// "┌───────┬────────────┬──────┐\n",
+/// "│ 0 │ 1 │ 2 │\n",
+/// "├───────┼────────────┼──────┤\n",
+/// "│ 1 │ 2 │ 3 │\n",
+/// "├───────┼────────────┼──────┤\n",
+/// "│ Some │ and a line │ here │\n",
+/// "│ Multi │ │ │\n",
+/// "│ Line │ │ │\n",
+/// "│ Text │ │ │\n",
+/// "├───────┼────────────┼──────┤\n",
+/// "│ 4 │ 5 │ 6 │\n",
+/// "└───────┴────────────┴──────┘",
+/// ),
+/// )
+/// ```
+///
+/// [`Table`]: crate::Table
+/// [`AlignmentStrategy`]: crate::settings::formatting::AlignmentStrategy
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Alignment {
+ inner: AlignmentInner,
+}
+
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
+enum AlignmentInner {
+ /// A horizontal alignment.
+ Horizontal(AlignmentHorizontal),
+ /// A vertical alignment.
+ Vertical(AlignmentVertical),
+}
+
+impl Alignment {
+ /// Left constructs a horizontal alignment to [`AlignmentHorizontal::Left`]
+ pub fn left() -> Self {
+ Self::horizontal(AlignmentHorizontal::Left)
+ }
+
+ /// Right constructs a horizontal alignment to [`AlignmentHorizontal::Right`]
+ ///
+ /// ## Notice
+ ///
+ /// When you use [`MinWidth`] the alignment might not work as you expected.
+ /// You could try to apply [`TrimStrategy`] which may help.
+ ///
+ /// [`MinWidth`]: crate::settings::width::MinWidth
+ /// [`TrimStrategy`]: crate::settings::formatting::TrimStrategy
+ pub fn right() -> Self {
+ Self::horizontal(AlignmentHorizontal::Right)
+ }
+
+ /// Center constructs a horizontal alignment to [`AlignmentHorizontal::Center`]
+ ///
+ /// ## Notice
+ ///
+ /// When you use [`MinWidth`] the alignment might not work as you expected.
+ /// You could try to apply [`TrimStrategy`] which may help.
+ ///
+ /// [`MinWidth`]: crate::settings::width::MinWidth
+ /// [`TrimStrategy`]: crate::settings::formatting::TrimStrategy
+ pub const fn center() -> Self {
+ Self::horizontal(AlignmentHorizontal::Center)
+ }
+
+ /// Top constructs a vertical alignment to [`AlignmentVertical::Top`]
+ pub const fn top() -> Self {
+ Self::vertical(AlignmentVertical::Top)
+ }
+
+ /// Bottom constructs a vertical alignment to [`AlignmentVertical::Bottom`]
+ pub const fn bottom() -> Self {
+ Self::vertical(AlignmentVertical::Bottom)
+ }
+
+ /// `Center_vertical` constructs a vertical alignment to [`AlignmentVertical::Center`]
+ pub const fn center_vertical() -> Self {
+ Self::vertical(AlignmentVertical::Center)
+ }
+
+ /// Returns an alignment with the given horizontal alignment.
+ const fn horizontal(alignment: AlignmentHorizontal) -> Self {
+ Self::new(Horizontal(alignment))
+ }
+
+ /// Returns an alignment with the given vertical alignment.
+ const fn vertical(alignment: AlignmentVertical) -> Self {
+ Self::new(Vertical(alignment))
+ }
+
+ const fn new(inner: AlignmentInner) -> Self {
+ Self { inner }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R> crate::settings::CellOption<R, ColoredConfig> for Alignment {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ match self.inner {
+ Horizontal(a) => cfg.set_alignment_horizontal(entity, a),
+ Vertical(a) => cfg.set_alignment_vertical(entity, a),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, D> TableOption<R, D, ColoredConfig> for Alignment {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ match self.inner {
+ Horizontal(a) => cfg.set_alignment_horizontal(Entity::Global, a),
+ Vertical(a) => cfg.set_alignment_vertical(Entity::Global, a),
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, CompactConfig> for Alignment {
+ fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
+ if let Horizontal(a) = self.inner {
+ *cfg = cfg.set_alignment_horizontal(a)
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, CompactMultilineConfig> for Alignment {
+ fn change(self, _: &mut R, cfg: &mut CompactMultilineConfig, _: &mut D) {
+ match self.inner {
+ Horizontal(a) => *cfg = cfg.set_alignment_horizontal(a),
+ Vertical(a) => *cfg = cfg.set_alignment_vertical(a),
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/cell_option.rs b/vendor/tabled/src/settings/cell_option.rs
new file mode 100644
index 000000000..f589ac47f
--- /dev/null
+++ b/vendor/tabled/src/settings/cell_option.rs
@@ -0,0 +1,55 @@
+use crate::grid::{
+ config::Entity,
+ records::{ExactRecords, Records, RecordsMut},
+};
+
+/// A trait for configuring a single cell.
+///
+/// ~~~~ Where cell represented by 'row' and 'column' indexes. ~~~~
+///
+/// A cell can be targeted by [`Cell`].
+///
+/// [`Cell`]: crate::object::Cell
+pub trait CellOption<R, C> {
+ /// Modification function of a single cell.
+ fn change(self, records: &mut R, cfg: &mut C, entity: Entity);
+}
+
+#[cfg(feature = "std")]
+impl<R, C> CellOption<R, C> for String
+where
+ R: Records + ExactRecords + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, cfg: &mut C, entity: Entity) {
+ (&self).change(records, cfg, entity);
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, C> CellOption<R, C> for &String
+where
+ R: Records + ExactRecords + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, _: &mut C, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_cols) {
+ records.set(pos, self.clone());
+ }
+ }
+}
+
+impl<'a, R, C> CellOption<R, C> for &'a str
+where
+ R: Records + ExactRecords + RecordsMut<&'a str>,
+{
+ fn change(self, records: &mut R, _: &mut C, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_cols) {
+ records.set(pos, self);
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/color/mod.rs b/vendor/tabled/src/settings/color/mod.rs
new file mode 100644
index 000000000..f4bfa5c32
--- /dev/null
+++ b/vendor/tabled/src/settings/color/mod.rs
@@ -0,0 +1,346 @@
+//! This module contains a configuration of a [`Border`] or a [`Table`] to set its borders color via [`Color`].
+//!
+//! [`Border`]: crate::settings::Border
+//! [`Table`]: crate::Table
+
+use std::{borrow::Cow, ops::BitOr};
+
+use crate::{
+ grid::{
+ color::{AnsiColor, StaticColor},
+ config::{ColoredConfig, Entity},
+ },
+ settings::{CellOption, TableOption},
+};
+
+/// Color represents a color which can be set to things like [`Border`], [`Padding`] and [`Margin`].
+///
+/// # Example
+///
+/// ```
+/// use tabled::{settings::Color, Table};
+///
+/// let data = [
+/// (0u8, "Hello"),
+/// (1u8, "World"),
+/// ];
+///
+/// let table = Table::new(data)
+/// .with(Color::BG_BLUE)
+/// .to_string();
+///
+/// println!("{}", table);
+/// ```
+///
+/// [`Padding`]: crate::settings::Padding
+/// [`Margin`]: crate::settings::Margin
+/// [`Border`]: crate::settings::Border
+#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Color(AnsiColor<'static>);
+
+// todo: Add | operation to combine colors
+
+#[rustfmt::skip]
+impl Color {
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BLACK: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[30m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BLUE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[34m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_BLACK: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[90m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_BLUE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[94m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_CYAN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[96m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_GREEN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[92m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_MAGENTA: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[95m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_RED: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[91m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_WHITE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[97m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_YELLOW: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[93m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_CYAN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[36m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_GREEN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[32m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_MAGENTA: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[35m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_RED: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[31m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_WHITE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[37m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_YELLOW: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[33m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+
+ pub const BG_BLACK: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[40m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BLUE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[44m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_BLACK: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[100m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_BLUE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[104m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_CYAN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[106m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_GREEN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[102m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_MAGENTA: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[105m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_RED: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[101m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_WHITE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[107m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_YELLOW: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[103m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_CYAN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[46m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_GREEN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[42m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_MAGENTA: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[45m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_RED: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[41m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_WHITE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[47m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_YELLOW: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[43m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BOLD: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[1m"), Cow::Borrowed("\u{1b}[22m")));
+}
+
+impl Color {
+ /// Creates a new [`Color`]` instance, with ANSI prefix and ANSI suffix.
+ /// You can use [`TryFrom`] to construct it from [`String`].
+ pub fn new(prefix: String, suffix: String) -> Self {
+ Self(AnsiColor::new(prefix.into(), suffix.into()))
+ }
+}
+
+impl From<Color> for AnsiColor<'static> {
+ fn from(c: Color) -> Self {
+ c.0
+ }
+}
+
+impl From<AnsiColor<'static>> for Color {
+ fn from(c: AnsiColor<'static>) -> Self {
+ Self(c)
+ }
+}
+
+impl From<StaticColor> for Color {
+ fn from(c: StaticColor) -> Self {
+ Self(AnsiColor::new(
+ Cow::Borrowed(c.get_prefix()),
+ Cow::Borrowed(c.get_suffix()),
+ ))
+ }
+}
+
+impl BitOr for Color {
+ type Output = Color;
+
+ fn bitor(self, rhs: Self) -> Self::Output {
+ let l_prefix = self.0.get_prefix();
+ let l_suffix = self.0.get_suffix();
+ let r_prefix = rhs.0.get_prefix();
+ let r_suffix = rhs.0.get_suffix();
+
+ let mut prefix = l_prefix.to_string();
+ if l_prefix != r_prefix {
+ prefix.push_str(r_prefix);
+ }
+
+ let mut suffix = l_suffix.to_string();
+ if l_suffix != r_suffix {
+ suffix.push_str(r_suffix);
+ }
+
+ Self::new(prefix, suffix)
+ }
+}
+
+#[cfg(feature = "color")]
+impl std::convert::TryFrom<&str> for Color {
+ type Error = ();
+
+ fn try_from(value: &str) -> Result<Self, Self::Error> {
+ AnsiColor::try_from(value).map(Color)
+ }
+}
+
+#[cfg(feature = "color")]
+impl std::convert::TryFrom<String> for Color {
+ type Error = ();
+
+ fn try_from(value: String) -> Result<Self, Self::Error> {
+ AnsiColor::try_from(value).map(Color)
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for Color {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let _ = cfg.set_color(Entity::Global, self.0.clone());
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for Color {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let _ = cfg.set_color(entity, self.0.clone());
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for &Color {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let _ = cfg.set_color(entity, self.0.clone());
+ }
+}
+
+impl crate::grid::color::Color for Color {
+ fn fmt_prefix<W: std::fmt::Write>(&self, f: &mut W) -> std::fmt::Result {
+ self.0.fmt_prefix(f)
+ }
+
+ fn fmt_suffix<W: std::fmt::Write>(&self, f: &mut W) -> std::fmt::Result {
+ self.0.fmt_suffix(f)
+ }
+
+ fn colorize<W: std::fmt::Write>(&self, f: &mut W, text: &str) -> std::fmt::Result {
+ self.0.colorize(f, text)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[cfg(feature = "color")]
+ use ::{owo_colors::OwoColorize, std::convert::TryFrom};
+
+ #[test]
+ fn test_xor_operation() {
+ assert_eq!(
+ Color::FG_BLACK | Color::FG_BLUE,
+ Color::new(
+ String::from("\u{1b}[30m\u{1b}[34m"),
+ String::from("\u{1b}[39m")
+ )
+ );
+ assert_eq!(
+ Color::FG_BRIGHT_GREEN | Color::BG_BLUE,
+ Color::new(
+ String::from("\u{1b}[92m\u{1b}[44m"),
+ String::from("\u{1b}[39m\u{1b}[49m")
+ )
+ );
+ assert_eq!(
+ Color::new(String::from("..."), String::from("!!!"))
+ | Color::new(String::from("@@@"), String::from("###")),
+ Color::new(String::from("...@@@"), String::from("!!!###"))
+ );
+ assert_eq!(
+ Color::new(String::from("..."), String::from("!!!"))
+ | Color::new(String::from("@@@"), String::from("###"))
+ | Color::new(String::from("$$$"), String::from("%%%")),
+ Color::new(String::from("...@@@$$$"), String::from("!!!###%%%"))
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn test_try_from() {
+ assert_eq!(Color::try_from(""), Err(()));
+ assert_eq!(Color::try_from("".red().on_green().to_string()), Err(()));
+ assert_eq!(
+ Color::try_from("."),
+ Ok(Color::new(String::new(), String::new()))
+ );
+ assert_eq!(
+ Color::try_from("...."),
+ Ok(Color::new(String::new(), String::new()))
+ );
+ assert_eq!(
+ Color::try_from(".".red().on_green().to_string()),
+ Ok(Color::new(
+ String::from("\u{1b}[31m\u{1b}[42m"),
+ String::from("\u{1b}[39m\u{1b}[49m")
+ ))
+ );
+ assert_eq!(
+ Color::try_from("....".red().on_green().to_string()),
+ Ok(Color::new(
+ String::from("\u{1b}[31m\u{1b}[42m"),
+ String::from("\u{1b}[39m\u{1b}[49m")
+ ))
+ );
+ }
+}
diff --git a/vendor/tabled/src/settings/concat/mod.rs b/vendor/tabled/src/settings/concat/mod.rs
new file mode 100644
index 000000000..23b0dcda1
--- /dev/null
+++ b/vendor/tabled/src/settings/concat/mod.rs
@@ -0,0 +1,171 @@
+//! This module contains a [`Concat`] primitive which can be in order to combine 2 [`Table`]s into 1.
+//!
+//! # Example
+//!
+//! ```
+//! use tabled::{Table, settings::Concat};
+//! let table1 = Table::new([0, 1, 2, 3]);
+//! let table2 = Table::new(["A", "B", "C", "D"]);
+//!
+//! let mut table3 = table1;
+//! table3.with(Concat::horizontal(table2));
+//! ```
+
+use std::borrow::Cow;
+
+use crate::{
+ grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut, Resizable},
+ settings::TableOption,
+ Table,
+};
+
+/// [`Concat`] concatenates tables along a particular axis [Horizontal | Vertical].
+/// It doesn't do any key or column comparisons like SQL's join does.
+///
+/// When the tables has different sizes, empty cells will be created by default.
+///
+/// [`Concat`] in horizontal mode has similar behaviour to tuples `(a, b)`.
+/// But it behaves on tables rather than on an actual data.
+///
+/// # Example
+///
+///
+#[cfg_attr(feature = "derive", doc = "```")]
+#[cfg_attr(not(feature = "derive"), doc = "```ignore")]
+/// use tabled::{Table, Tabled, settings::{Style, Concat}};
+///
+/// #[derive(Tabled)]
+/// struct Message {
+/// id: &'static str,
+/// text: &'static str,
+/// }
+///
+/// #[derive(Tabled)]
+/// struct Department(#[tabled(rename = "department")] &'static str);
+///
+/// let messages = [
+/// Message { id: "0", text: "Hello World" },
+/// Message { id: "1", text: "Do do do something", },
+/// ];
+///
+/// let departments = [
+/// Department("Admins"),
+/// Department("DevOps"),
+/// Department("R&D"),
+/// ];
+///
+/// let mut table = Table::new(messages);
+/// table
+/// .with(Concat::horizontal(Table::new(departments)))
+/// .with(Style::extended());
+///
+/// assert_eq!(
+/// table.to_string(),
+/// concat!(
+/// "╔════╦════════════════════╦════════════╗\n",
+/// "║ id ║ text ║ department ║\n",
+/// "╠════╬════════════════════╬════════════╣\n",
+/// "║ 0 ║ Hello World ║ Admins ║\n",
+/// "╠════╬════════════════════╬════════════╣\n",
+/// "║ 1 ║ Do do do something ║ DevOps ║\n",
+/// "╠════╬════════════════════╬════════════╣\n",
+/// "║ ║ ║ R&D ║\n",
+/// "╚════╩════════════════════╩════════════╝",
+/// )
+/// )
+/// ```
+
+#[derive(Debug)]
+pub struct Concat {
+ table: Table,
+ mode: ConcatMode,
+ default_cell: Cow<'static, str>,
+}
+
+#[derive(Debug)]
+enum ConcatMode {
+ Vertical,
+ Horizontal,
+}
+
+impl Concat {
+ fn new(table: Table, mode: ConcatMode) -> Self {
+ Self {
+ table,
+ mode,
+ default_cell: Cow::Borrowed(""),
+ }
+ }
+
+ /// Concatenate 2 tables horizontally (along axis=0)
+ pub fn vertical(table: Table) -> Self {
+ Self::new(table, ConcatMode::Vertical)
+ }
+
+ /// Concatenate 2 tables vertically (along axis=1)
+ pub fn horizontal(table: Table) -> Self {
+ Self::new(table, ConcatMode::Horizontal)
+ }
+
+ /// Sets a cell's content for cases where 2 tables has different sizes.
+ pub fn default_cell(mut self, cell: impl Into<Cow<'static, str>>) -> Self {
+ self.default_cell = cell.into();
+ self
+ }
+}
+
+impl<R, D, C> TableOption<R, D, C> for Concat
+where
+ R: Records + ExactRecords + Resizable + PeekableRecords + RecordsMut<String>,
+{
+ fn change(mut self, records: &mut R, _: &mut C, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ let rhs = &mut self.table;
+ match self.mode {
+ ConcatMode::Horizontal => {
+ for _ in 0..rhs.count_columns() {
+ records.push_column();
+ }
+
+ for row in count_rows..rhs.count_rows() {
+ records.push_row();
+
+ for col in 0..records.count_columns() {
+ records.set((row, col), self.default_cell.to_string());
+ }
+ }
+
+ for row in 0..rhs.shape().0 {
+ for col in 0..rhs.shape().1 {
+ let text = rhs.get_records().get_text((row, col)).to_string();
+ let col = col + count_cols;
+ records.set((row, col), text);
+ }
+ }
+ }
+ ConcatMode::Vertical => {
+ for _ in 0..rhs.count_rows() {
+ records.push_row();
+ }
+
+ for col in count_cols..rhs.shape().1 {
+ records.push_column();
+
+ for row in 0..records.count_rows() {
+ records.set((row, col), self.default_cell.to_string());
+ }
+ }
+
+ for row in 0..rhs.shape().0 {
+ for col in 0..rhs.shape().1 {
+ let text = rhs.get_records().get_text((row, col)).to_string();
+ let row = row + count_rows;
+ records.set((row, col), text);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/disable/mod.rs b/vendor/tabled/src/settings/disable/mod.rs
new file mode 100644
index 000000000..bfbf03db0
--- /dev/null
+++ b/vendor/tabled/src/settings/disable/mod.rs
@@ -0,0 +1,196 @@
+//! This module contains a [`Disable`] structure which helps to
+//! remove an etheir column or row from a [`Table`].
+//!
+//! # Example
+//!
+//! ```rust,no_run
+//! # use tabled::{Table, settings::{Disable, object::Rows}};
+//! # let data: Vec<&'static str> = Vec::new();
+//! let table = Table::new(&data).with(Disable::row(Rows::first()));
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use std::marker::PhantomData;
+
+use crate::{
+ grid::records::{ExactRecords, Records, Resizable},
+ settings::{locator::Locator, TableOption},
+};
+
+/// Disable removes particular rows/columns from a [`Table`].
+///
+/// It tries to keeps track of style changes which may occur.
+/// But it's not guaranteed will be the way you would expect it to be.
+///
+/// Generally you should avoid use of [`Disable`] because it's a slow function and modifies the underlying records.
+/// Providing correct data right away is better.
+///
+/// # Example
+///
+/// ```
+/// use tabled::{Table, settings::{Disable, object::Rows}};
+///
+/// let data = vec!["Hello", "World", "!!!"];
+///
+/// let table = Table::new(data).with(Disable::row(Rows::new(1..2))).to_string();
+///
+/// assert_eq!(
+/// table,
+/// "+-------+\n\
+/// | &str |\n\
+/// +-------+\n\
+/// | World |\n\
+/// +-------+\n\
+/// | !!! |\n\
+/// +-------+"
+/// );
+///
+/// ```
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Disable<L, Target> {
+ locator: L,
+ target: PhantomData<Target>,
+}
+
+impl<L> Disable<L, TargetColumn> {
+ /// Disable columns.
+ ///
+ /// Available locators are:
+ ///
+ /// - [`Columns`]
+ /// - [`Column`]
+ /// - [`FirstColumn`]
+ /// - [`LastColumn`]
+ /// - [`ByColumnName`]
+ ///
+ /// ```rust
+ /// use tabled::{builder::Builder, settings::{Disable, locator::ByColumnName, object::Columns}};
+ ///
+ /// let mut builder = Builder::default();
+ ///
+ /// builder.push_record(["col1", "col2", "col3"]);
+ /// builder.push_record(["Hello", "World", "1"]);
+ ///
+ /// let table = builder.build()
+ /// .with(Disable::column(ByColumnName::new("col3")))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+-------+-------+\n\
+ /// | col1 | col2 |\n\
+ /// +-------+-------+\n\
+ /// | Hello | World |\n\
+ /// +-------+-------+"
+ /// );
+ /// ```
+ ///
+ /// [`Columns`]: crate::settings::object::Columns
+ /// [`Column`]: crate::settings::object::Column
+ /// [`FirstColumn`]: crate::settings::object::FirstColumn
+ /// [`LastColumn`]: crate::settings::object::LastColumn
+ /// [`ByColumnName`]: crate::settings::locator::ByColumnName
+ pub fn column(locator: L) -> Self {
+ Self {
+ locator,
+ target: PhantomData,
+ }
+ }
+}
+
+impl<L> Disable<L, TargetRow> {
+ /// Disable rows.
+ ///
+ /// Available locators are:
+ ///
+ /// - [`Rows`]
+ /// - [`Row`]
+ /// - [`FirstRow`]
+ /// - [`LastRow`]
+ ///
+ /// ```rust
+ /// use tabled::{settings::{Disable, object::Rows}, builder::Builder};
+ ///
+ /// let mut builder = Builder::default();
+ /// builder.push_record(["col1", "col2", "col3"]);
+ /// builder.push_record(["Hello", "World", "1"]);
+ ///
+ /// let table = builder.build()
+ /// .with(Disable::row(Rows::first()))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+-------+-------+---+\n\
+ /// | Hello | World | 1 |\n\
+ /// +-------+-------+---+"
+ /// );
+ /// ```
+ ///
+ /// [`Rows`]: crate::settings::object::Rows
+ /// [`Row`]: crate::settings::object::Row
+ /// [`FirstRow`]: crate::settings::object::FirstRow
+ /// [`LastRow`]: crate::settings::object::LastRow
+ pub fn row(locator: L) -> Self {
+ Self {
+ locator,
+ target: PhantomData,
+ }
+ }
+}
+
+/// A marker struct for [`Disable`].
+#[derive(Debug)]
+pub struct TargetRow;
+
+/// A marker struct for [`Disable`].
+#[derive(Debug)]
+pub struct TargetColumn;
+
+impl<L, R, D, C> TableOption<R, D, C> for Disable<L, TargetColumn>
+where
+ for<'a> L: Locator<&'a R, Coordinate = usize>,
+ R: Records + Resizable,
+{
+ fn change(mut self, records: &mut R, _: &mut C, _: &mut D) {
+ let columns = self.locator.locate(records).into_iter().collect::<Vec<_>>();
+
+ let mut shift = 0;
+ for col in columns.into_iter() {
+ if col - shift > records.count_columns() {
+ continue;
+ }
+
+ records.remove_column(col - shift);
+ shift += 1;
+ }
+
+ // fixme: I am pretty sure that we violate span constrains by removing rows/cols
+ // Because span may be bigger then the max number of rows/cols
+ }
+}
+
+impl<L, R, D, C> TableOption<R, D, C> for Disable<L, TargetRow>
+where
+ for<'a> L: Locator<&'a R, Coordinate = usize>,
+ R: ExactRecords + Resizable,
+{
+ fn change(mut self, records: &mut R, _: &mut C, _: &mut D) {
+ let rows = self.locator.locate(records).into_iter().collect::<Vec<_>>();
+
+ let mut shift = 0;
+ for row in rows.into_iter() {
+ if row - shift > records.count_rows() {
+ continue;
+ }
+
+ records.remove_row(row - shift);
+ shift += 1;
+ }
+
+ // fixme: I am pretty sure that we violate span constrains by removing rows/cols
+ // Because span may be bigger then the max number of rows/cols
+ }
+}
diff --git a/vendor/tabled/src/settings/duplicate/mod.rs b/vendor/tabled/src/settings/duplicate/mod.rs
new file mode 100644
index 000000000..dec967bd6
--- /dev/null
+++ b/vendor/tabled/src/settings/duplicate/mod.rs
@@ -0,0 +1,150 @@
+//! This module contains an [`Dup`] setting the [`Table`].
+//!
+//! # Example
+//!
+//! ```
+//! # use tabled::{Table, settings::{Dup, object::{Columns, Rows}}};
+//! # let data: Vec<&'static str> = Vec::new();
+//! let mut table = Table::new(&data);
+//! table.with(Dup::new(Rows::first(), Columns::first()));
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use papergrid::config::Position;
+
+use crate::{
+ grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ settings::{object::Object, TableOption},
+};
+
+/// [`Dup`] duplicates a given set of cells into another set of ones [`Table`].
+///
+/// # Example
+///
+/// ```
+/// use tabled::{Table, settings::{object::Rows, Dup}};
+///
+/// let data = [
+/// ["1", "2", "3"],
+/// ["Some\nMulti\nLine\nText", "and a line", "here"],
+/// ["4", "5", "6"],
+/// ];
+///
+/// let mut table = Table::new(&data);
+/// table.with(Dup::new(Rows::single(1), Rows::single(2)));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+-------+------------+------+\n\
+/// | 0 | 1 | 2 |\n\
+/// +-------+------------+------+\n\
+/// | Some | and a line | here |\n\
+/// | Multi | | |\n\
+/// | Line | | |\n\
+/// | Text | | |\n\
+/// +-------+------------+------+\n\
+/// | Some | and a line | here |\n\
+/// | Multi | | |\n\
+/// | Line | | |\n\
+/// | Text | | |\n\
+/// +-------+------------+------+\n\
+/// | 4 | 5 | 6 |\n\
+/// +-------+------------+------+",
+/// )
+/// ```
+///
+/// [`Table`]: crate::Table
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Dup<Dst, Src> {
+ src: Src,
+ dst: Dst,
+}
+
+impl<Dst, Src> Dup<Dst, Src> {
+ /// New creates a new [`Dup`] modifier.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use tabled::{Table, settings::{Dup, object::{Columns, Rows}}};
+ /// # let data: Vec<&'static str> = Vec::new();
+ /// let mut table = Table::new(&data);
+ /// table.with(Dup::new(Rows::first(), Columns::last()));
+ /// ```
+ pub fn new(dst: Dst, src: Src) -> Self {
+ Self { src, dst }
+ }
+}
+
+impl<Dst, Src, R, D, C> TableOption<R, D, C> for Dup<Dst, Src>
+where
+ Dst: Object<R>,
+ Src: Object<R>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, _: &mut C, _: &mut D) {
+ let input = collect_input(records, self.src);
+ set_cells(records, &input, self.dst);
+ }
+}
+
+fn collect_input<R, O>(records: &mut R, src: O) -> Vec<String>
+where
+ O: Object<R>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ let mut input = Vec::new();
+ for entity in src.cells(records) {
+ for pos in entity.iter(count_rows, count_columns) {
+ if !is_valid_cell(pos, count_rows, count_columns) {
+ continue;
+ }
+
+ let text = records.get_text(pos).to_owned();
+ input.push(text);
+ }
+ }
+
+ input
+}
+
+fn set_cells<R, O>(records: &mut R, src: &[String], dst: O)
+where
+ O: Object<R>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ if src.is_empty() {
+ return;
+ }
+
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ for entity in dst.cells(records) {
+ let mut source = src.iter().cycle();
+ for pos in entity.iter(count_rows, count_columns) {
+ if !is_valid_cell(pos, count_rows, count_columns) {
+ continue;
+ }
+
+ let text = source.next().unwrap().clone();
+ records.set(pos, text);
+ }
+ }
+}
+
+fn is_valid_cell((row, col): Position, count_rows: usize, count_columns: usize) -> bool {
+ if row > count_rows {
+ return false;
+ }
+
+ if col > count_columns {
+ return false;
+ }
+
+ true
+}
diff --git a/vendor/tabled/src/settings/extract/mod.rs b/vendor/tabled/src/settings/extract/mod.rs
new file mode 100644
index 000000000..bba90a0db
--- /dev/null
+++ b/vendor/tabled/src/settings/extract/mod.rs
@@ -0,0 +1,257 @@
+//! This module contains an [`Extract`] structure which is used to
+//! obtain an ordinary segment from the [`Table`].
+//!
+//! There's a similar structure [`Highlight`] which does a highlighting a of segments.
+//!
+//! [`Table`]: crate::Table
+//! [`Highlight`]: crate::settings::highlight::Highlight
+
+use core::cmp::min;
+use core::ops::{Bound, RangeBounds, RangeFull};
+
+use crate::{
+ grid::records::{ExactRecords, Records, Resizable},
+ settings::TableOption,
+};
+
+/// Returns a new [`Table`] that reflects a segment of the referenced [`Table`]
+///
+/// # Example
+///
+#[cfg_attr(feature = "std", doc = "```")]
+#[cfg_attr(not(feature = "std"), doc = "```ignore")]
+/// use tabled::{Table, settings::{Format, object::Rows, Modify, Extract}};
+///
+/// let data = vec![
+/// (0, "Grodno", true),
+/// (1, "Minsk", true),
+/// (2, "Hamburg", false),
+/// (3, "Brest", true),
+/// ];
+///
+/// let table = Table::new(&data)
+/// .with(Modify::new(Rows::new(1..)).with(Format::content(|s| format!(": {} :", s))))
+/// .with(Extract::segment(1..=2, 1..))
+/// .to_string();
+///
+/// assert_eq!(table, "+------------+----------+\n\
+/// | : Grodno : | : true : |\n\
+/// +------------+----------+\n\
+/// | : Minsk : | : true : |\n\
+/// +------------+----------+");
+/// ```
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Extract<R, C> {
+ rows: R,
+ columns: C,
+}
+
+impl<R, C> Extract<R, C>
+where
+ R: RangeBounds<usize>,
+ C: RangeBounds<usize>,
+{
+ /// Returns a new [`Table`] that reflects a segment of the referenced [`Table`]
+ ///
+ /// ```rust,no_run
+ /// # use tabled::settings::Extract;
+ /// let rows = 1..3;
+ /// let columns = 1..;
+ /// Extract::segment(rows, columns);
+ /// ```
+ ///
+ /// # Range
+ ///
+ /// A [`RangeBounds`] argument can be less than or equal to the shape of a [`Table`]
+ ///
+ /// If a [`RangeBounds`] argument is malformed or too large the thread will panic
+ ///
+ /// ```text
+ /// // Empty Full Out of bounds
+ /// Extract::segment(0..0, 0..0) Extract::segment(.., ..) Extract::segment(0..1, ..4)
+ /// []. . . [O O O [O O O X] //ERROR
+ /// . . . O O O . . .
+ /// . . . O O O] . . .
+ /// ```
+ ///
+ /// [`Table`]: crate::Table
+ pub fn segment(rows: R, columns: C) -> Self {
+ Extract { rows, columns }
+ }
+}
+
+impl<R> Extract<R, RangeFull>
+where
+ R: RangeBounds<usize>,
+{
+ /// Returns a new [`Table`] that reflects a segment of the referenced [`Table`]
+ ///
+ /// The segment is defined by [`RangeBounds`] for Rows
+ ///
+ /// ```rust,no_run
+ /// # use tabled::settings::Extract;
+ /// Extract::rows(1..3);
+ /// ```
+ ///
+ /// # Range
+ ///
+ /// A [`RangeBounds`] argument can be less than or equal to the shape of a [`Table`]
+ ///
+ /// If a [`RangeBounds`] argument is malformed or too large the thread will panic
+ ///
+ /// ```text
+ /// // Empty Full Out of bounds
+ /// Extract::rows(0..0) Extract::rows(..) Extract::rows(0..4)
+ /// []. . . [O O O [O O O
+ /// . . . O O O O O O
+ /// . . . O O O] O O O
+ /// X X X] // ERROR
+ /// ```
+ ///
+ /// [`Table`]: crate::Table
+ pub fn rows(rows: R) -> Self {
+ Extract { rows, columns: .. }
+ }
+}
+
+impl<C> Extract<RangeFull, C>
+where
+ C: RangeBounds<usize>,
+{
+ /// Returns a new [`Table`] that reflects a segment of the referenced [`Table`]
+ ///
+ /// The segment is defined by [`RangeBounds`] for columns.
+ ///
+ /// ```rust,no_run
+ /// # use tabled::settings::Extract;
+ /// Extract::columns(1..3);
+ /// ```
+ ///
+ /// # Range
+ ///
+ /// A [`RangeBounds`] argument can be less than or equal to the shape of a [`Table`]
+ ///
+ /// If a [`RangeBounds`] argument is malformed or too large the thread will panic
+ ///
+ /// ```text
+ /// // Empty Full Out of bounds
+ /// Extract::columns(0..0) Extract::columns(..) Extract::columns(0..4)
+ /// []. . . [O O O [O O O X
+ /// . . . O O O O O O X
+ /// . . . O O O] O O O X] // ERROR
+ /// ```
+ ///
+ /// [`Table`]: crate::Table
+ pub fn columns(columns: C) -> Self {
+ Extract { rows: .., columns }
+ }
+}
+
+impl<R, C, RR, D, Cfg> TableOption<RR, D, Cfg> for Extract<R, C>
+where
+ R: RangeBounds<usize> + Clone,
+ C: RangeBounds<usize> + Clone,
+ RR: Records + ExactRecords + Resizable,
+{
+ fn change(self, records: &mut RR, _: &mut Cfg, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ let mut rows = bounds_to_usize(self.rows.start_bound(), self.rows.end_bound(), count_rows);
+ let mut cols = bounds_to_usize(
+ self.columns.start_bound(),
+ self.columns.end_bound(),
+ count_columns,
+ );
+
+ // Cleanup table in case if boundaries are exceeded.
+ //
+ // todo: can be optimized by adding a clear() method to Resizable
+ rows.0 = min(rows.0, count_rows);
+ cols.0 = min(cols.0, count_columns);
+
+ extract(records, (count_rows, count_columns), rows, cols);
+ }
+}
+
+/// Returns a new [`Grid`] that reflects a segment of the referenced [`Grid`].
+///
+/// # Example
+///
+/// ```text
+/// grid
+/// +---+---+---+
+/// |0-0|0-1|0-2|
+/// +---+---+---+
+/// |1-0|1-1|1-2|
+/// +---+---+---+
+/// |2-0|2-1|2-2|
+/// +---+---+---+
+///
+/// let rows = ..;
+/// let columns = ..1;
+/// grid.extract(rows, columns)
+///
+/// grid
+/// +---+
+/// |0-0|
+/// +---+
+/// |1-0|
+/// +---+
+/// |2-0|
+/// +---+
+/// ```
+fn extract<R>(
+ records: &mut R,
+ (count_rows, count_cols): (usize, usize),
+ (start_row, end_row): (usize, usize),
+ (start_col, end_col): (usize, usize),
+) where
+ R: Resizable,
+{
+ for (i, row) in (0..start_row).enumerate() {
+ let row = row - i;
+ records.remove_row(row);
+ }
+
+ let count_rows = count_rows - start_row;
+ let end_row = end_row - start_row;
+ for (i, row) in (end_row..count_rows).enumerate() {
+ let row = row - i;
+ records.remove_row(row);
+ }
+
+ for (i, col) in (0..start_col).enumerate() {
+ let col = col - i;
+ records.remove_column(col);
+ }
+
+ let count_cols = count_cols - start_col;
+ let end_col = end_col - start_col;
+ for (i, col) in (end_col..count_cols).enumerate() {
+ let col = col - i;
+ records.remove_column(col);
+ }
+}
+
+fn bounds_to_usize(
+ left: Bound<&usize>,
+ right: Bound<&usize>,
+ count_elements: usize,
+) -> (usize, usize) {
+ match (left, right) {
+ (Bound::Included(x), Bound::Included(y)) => (*x, y + 1),
+ (Bound::Included(x), Bound::Excluded(y)) => (*x, *y),
+ (Bound::Included(x), Bound::Unbounded) => (*x, count_elements),
+ (Bound::Unbounded, Bound::Unbounded) => (0, count_elements),
+ (Bound::Unbounded, Bound::Included(y)) => (0, y + 1),
+ (Bound::Unbounded, Bound::Excluded(y)) => (0, *y),
+ (Bound::Excluded(_), Bound::Unbounded)
+ | (Bound::Excluded(_), Bound::Included(_))
+ | (Bound::Excluded(_), Bound::Excluded(_)) => {
+ unreachable!("A start bound can't be excluded")
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/format/format_config.rs b/vendor/tabled/src/settings/format/format_config.rs
new file mode 100644
index 000000000..247ead344
--- /dev/null
+++ b/vendor/tabled/src/settings/format/format_config.rs
@@ -0,0 +1,14 @@
+use crate::settings::TableOption;
+
+/// This is a struct wrapper for a lambda which changes config.
+#[derive(Debug)]
+pub struct FormatConfig<F>(pub(crate) F);
+
+impl<F, R, D, C> TableOption<R, D, C> for FormatConfig<F>
+where
+ F: FnMut(&mut C),
+{
+ fn change(mut self, _: &mut R, cfg: &mut C, _: &mut D) {
+ (self.0)(cfg);
+ }
+}
diff --git a/vendor/tabled/src/settings/format/format_content.rs b/vendor/tabled/src/settings/format/format_content.rs
new file mode 100644
index 000000000..c14eee64f
--- /dev/null
+++ b/vendor/tabled/src/settings/format/format_content.rs
@@ -0,0 +1,86 @@
+use crate::{
+ grid::config::Entity,
+ grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ settings::{CellOption, TableOption},
+};
+
+/// A lambda which formats cell content.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct FormatContent<F> {
+ f: F,
+ multiline: bool,
+}
+
+impl<F> FormatContent<F> {
+ pub(crate) fn new(f: F) -> Self {
+ Self {
+ f,
+ multiline: false,
+ }
+ }
+}
+
+impl<F> FormatContent<F> {
+ /// Multiline a helper function for changing multiline content of cell.
+ /// Using this formatting applied for all rows not to a string as a whole.
+ ///
+ /// ```rust,no_run
+ /// use tabled::{Table, settings::{Format, object::Segment, Modify}};
+ ///
+ /// let data: Vec<&'static str> = Vec::new();
+ /// let table = Table::new(&data)
+ /// .with(Modify::new(Segment::all()).with(Format::content(|s| s.to_string()).multiline()))
+ /// .to_string();
+ /// ```
+ pub fn multiline(mut self) -> Self {
+ self.multiline = true;
+ self
+ }
+}
+
+impl<F, R, D, C> TableOption<R, D, C> for FormatContent<F>
+where
+ F: FnMut(&str) -> String + Clone,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, cfg: &mut C, _: &mut D) {
+ CellOption::change(self, records, cfg, Entity::Global);
+ }
+}
+
+impl<F, R, C> CellOption<R, C> for FormatContent<F>
+where
+ F: FnMut(&str) -> String + Clone,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ fn change(mut self, records: &mut R, _: &mut C, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_cols) {
+ let is_valid_pos = pos.0 < count_rows && pos.1 < count_cols;
+ if !is_valid_pos {
+ continue;
+ }
+
+ let content = records.get_text(pos);
+ let content = if self.multiline {
+ multiline(self.f.clone())(content)
+ } else {
+ (self.f)(content)
+ };
+ records.set(pos, content);
+ }
+ }
+}
+
+fn multiline<F: FnMut(&str) -> String>(mut f: F) -> impl FnMut(&str) -> String {
+ move |s: &str| {
+ let mut v = Vec::new();
+ for line in s.lines() {
+ v.push(f(line));
+ }
+
+ v.join("\n")
+ }
+}
diff --git a/vendor/tabled/src/settings/format/format_positioned.rs b/vendor/tabled/src/settings/format/format_positioned.rs
new file mode 100644
index 000000000..e121f007e
--- /dev/null
+++ b/vendor/tabled/src/settings/format/format_positioned.rs
@@ -0,0 +1,51 @@
+use crate::{
+ grid::config::Entity,
+ grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ settings::{CellOption, TableOption},
+};
+
+/// [`FormatContentPositioned`] is like a [`FormatContent`] an abstraction over a function you can use against a cell.
+///
+/// It different from [`FormatContent`] that it provides a row and column index.
+///
+/// [`FormatContent`]: crate::settings::format::FormatContent
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct FormatContentPositioned<F>(F);
+
+impl<F> FormatContentPositioned<F> {
+ pub(crate) fn new(f: F) -> Self {
+ Self(f)
+ }
+}
+
+impl<F, R, D, C> TableOption<R, D, C> for FormatContentPositioned<F>
+where
+ F: FnMut(&str, (usize, usize)) -> String,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, cfg: &mut C, _: &mut D) {
+ CellOption::change(self, records, cfg, Entity::Global);
+ }
+}
+
+impl<F, R, C> CellOption<R, C> for FormatContentPositioned<F>
+where
+ F: FnMut(&str, (usize, usize)) -> String,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ fn change(mut self, records: &mut R, _: &mut C, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_cols) {
+ let is_valid_pos = pos.0 < count_rows && pos.1 < count_cols;
+ if !is_valid_pos {
+ continue;
+ }
+
+ let content = records.get_text(pos);
+ let content = (self.0)(content, pos);
+ records.set(pos, content);
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/format/mod.rs b/vendor/tabled/src/settings/format/mod.rs
new file mode 100644
index 000000000..e8221ae32
--- /dev/null
+++ b/vendor/tabled/src/settings/format/mod.rs
@@ -0,0 +1,144 @@
+//! This module contains a list of primitives to help to modify a [`Table`].
+//!
+//! [`Table`]: crate::Table
+
+mod format_config;
+mod format_content;
+mod format_positioned;
+
+pub use format_config::FormatConfig;
+pub use format_content::FormatContent;
+pub use format_positioned::FormatContentPositioned;
+
+/// A formatting function of particular cells on a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Format;
+
+impl Format {
+ /// This function creates a new [`Format`] instance, so
+ /// it can be used as a grid setting.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{Table, settings::{Format, object::Rows, Modify}};
+ ///
+ /// let data = vec![
+ /// (0, "Grodno", true),
+ /// (1, "Minsk", true),
+ /// (2, "Hamburg", false),
+ /// (3, "Brest", true),
+ /// ];
+ ///
+ /// let table = Table::new(&data)
+ /// .with(Modify::new(Rows::new(1..)).with(Format::content(|s| format!(": {} :", s))))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+-------+-------------+-----------+\n\
+ /// | i32 | &str | bool |\n\
+ /// +-------+-------------+-----------+\n\
+ /// | : 0 : | : Grodno : | : true : |\n\
+ /// +-------+-------------+-----------+\n\
+ /// | : 1 : | : Minsk : | : true : |\n\
+ /// +-------+-------------+-----------+\n\
+ /// | : 2 : | : Hamburg : | : false : |\n\
+ /// +-------+-------------+-----------+\n\
+ /// | : 3 : | : Brest : | : true : |\n\
+ /// +-------+-------------+-----------+"
+ /// );
+ /// ```
+ pub fn content<F>(f: F) -> FormatContent<F>
+ where
+ F: FnMut(&str) -> String,
+ {
+ FormatContent::new(f)
+ }
+
+ /// This function creates a new [`FormatContentPositioned`], so
+ /// it can be used as a grid setting.
+ ///
+ /// It's different from [`Format::content`] as it also provides a row and column index.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{Table, settings::{Format, object::Rows, Modify}};
+ ///
+ /// let data = vec![
+ /// (0, "Grodno", true),
+ /// (1, "Minsk", true),
+ /// (2, "Hamburg", false),
+ /// (3, "Brest", true),
+ /// ];
+ ///
+ /// let table = Table::new(&data)
+ /// .with(Modify::new(Rows::single(0)).with(Format::positioned(|_, (_, col)| col.to_string())))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+---+---------+-------+\n\
+ /// | 0 | 1 | 2 |\n\
+ /// +---+---------+-------+\n\
+ /// | 0 | Grodno | true |\n\
+ /// +---+---------+-------+\n\
+ /// | 1 | Minsk | true |\n\
+ /// +---+---------+-------+\n\
+ /// | 2 | Hamburg | false |\n\
+ /// +---+---------+-------+\n\
+ /// | 3 | Brest | true |\n\
+ /// +---+---------+-------+"
+ /// );
+ /// ```
+ pub fn positioned<F>(f: F) -> FormatContentPositioned<F>
+ where
+ F: FnMut(&str, (usize, usize)) -> String,
+ {
+ FormatContentPositioned::new(f)
+ }
+
+ /// This function creates [`FormatConfig`] function to modify a table config.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{
+ /// Table,
+ /// settings::{Format, object::Rows, Modify},
+ /// grid::config::ColoredConfig,
+ /// };
+ ///
+ /// let data = vec![
+ /// (0, "Grodno", true),
+ /// (1, "Minsk", true),
+ /// (2, "Hamburg", false),
+ /// (3, "Brest", true),
+ /// ];
+ ///
+ /// let table = Table::new(&data)
+ /// .with(Format::config(|cfg: &mut ColoredConfig| cfg.set_justification((0,1).into(), '.')))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+-----+---------+-------+\n\
+ /// | i32 | &str... | bool |\n\
+ /// +-----+---------+-------+\n\
+ /// | 0 | Grodno | true |\n\
+ /// +-----+---------+-------+\n\
+ /// | 1 | Minsk | true |\n\
+ /// +-----+---------+-------+\n\
+ /// | 2 | Hamburg | false |\n\
+ /// +-----+---------+-------+\n\
+ /// | 3 | Brest | true |\n\
+ /// +-----+---------+-------+"
+ /// );
+ /// ```
+ pub fn config<F>(f: F) -> FormatConfig<F> {
+ FormatConfig(f)
+ }
+}
diff --git a/vendor/tabled/src/settings/formatting/alignment_strategy.rs b/vendor/tabled/src/settings/formatting/alignment_strategy.rs
new file mode 100644
index 000000000..c95facc5c
--- /dev/null
+++ b/vendor/tabled/src/settings/formatting/alignment_strategy.rs
@@ -0,0 +1,172 @@
+use crate::{
+ grid::config::{ColoredConfig, CompactMultilineConfig, Entity},
+ settings::{CellOption, TableOption},
+};
+
+/// `AlignmentStrategy` is a responsible for a flow how we apply an alignment.
+/// It mostly matters for multiline strings.
+///
+/// # Examples
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{
+/// Style, Modify, Alignment, object::Segment,
+/// formatting::{AlignmentStrategy, TrimStrategy}
+/// }
+/// };
+///
+/// // sample_from: https://opensource.adobe.com/Spry/samples/data_region/JSONDataSetSample.html
+/// let json = r#"
+/// {
+/// "id": "0001",
+/// "type": "donut",
+/// "name": "Cake",
+/// "ppu": 0.55,
+/// "batters": {
+/// "batter": [
+/// { "id": "1001", "type": "Regular" },
+/// { "id": "1002", "type": "Chocolate" },
+/// ]
+/// },
+/// "topping": [
+/// { "id": "5001", "type": "None" },
+/// { "id": "5006", "type": "Chocolate with Sprinkles" },
+/// { "id": "5003", "type": "Chocolate" },
+/// { "id": "5004", "type": "Maple" }
+/// ]
+/// }"#;
+///
+/// let mut table = Table::new(&[json]);
+/// table
+/// .with(Style::modern())
+/// .with(Modify::new(Segment::all()).with(Alignment::right()))
+/// .with(Modify::new(Segment::all()).with(TrimStrategy::None));
+///
+/// println!("{}", table);
+///
+/// assert_eq!(
+/// format!("\n{}", table),
+/// r#"
+/// ┌───────────────────────────────────────────────────────────────┐
+/// │ &str │
+/// ├───────────────────────────────────────────────────────────────┤
+/// │ │
+/// │ { │
+/// │ "id": "0001", │
+/// │ "type": "donut", │
+/// │ "name": "Cake", │
+/// │ "ppu": 0.55, │
+/// │ "batters": { │
+/// │ "batter": [ │
+/// │ { "id": "1001", "type": "Regular" }, │
+/// │ { "id": "1002", "type": "Chocolate" }, │
+/// │ ] │
+/// │ }, │
+/// │ "topping": [ │
+/// │ { "id": "5001", "type": "None" }, │
+/// │ { "id": "5006", "type": "Chocolate with Sprinkles" }, │
+/// │ { "id": "5003", "type": "Chocolate" }, │
+/// │ { "id": "5004", "type": "Maple" } │
+/// │ ] │
+/// │ } │
+/// └───────────────────────────────────────────────────────────────┘"#);
+///
+/// table
+/// .with(Modify::new(Segment::all()).with(AlignmentStrategy::PerCell))
+/// .with(Modify::new(Segment::all()).with(TrimStrategy::Horizontal));
+///
+/// assert_eq!(
+/// format!("\n{}", table),
+/// r#"
+/// ┌───────────────────────────────────────────────────────────────┐
+/// │ &str │
+/// ├───────────────────────────────────────────────────────────────┤
+/// │ │
+/// │ { │
+/// │ "id": "0001", │
+/// │ "type": "donut", │
+/// │ "name": "Cake", │
+/// │ "ppu": 0.55, │
+/// │ "batters": { │
+/// │ "batter": [ │
+/// │ { "id": "1001", "type": "Regular" }, │
+/// │ { "id": "1002", "type": "Chocolate" }, │
+/// │ ] │
+/// │ }, │
+/// │ "topping": [ │
+/// │ { "id": "5001", "type": "None" }, │
+/// │ { "id": "5006", "type": "Chocolate with Sprinkles" }, │
+/// │ { "id": "5003", "type": "Chocolate" }, │
+/// │ { "id": "5004", "type": "Maple" } │
+/// │ ] │
+/// │ } │
+/// └───────────────────────────────────────────────────────────────┘"#);
+///
+/// table.with(Modify::new(Segment::all()).with(AlignmentStrategy::PerLine));
+///
+/// assert_eq!(
+/// format!("\n{}", table),
+/// r#"
+/// ┌───────────────────────────────────────────────────────────────┐
+/// │ &str │
+/// ├───────────────────────────────────────────────────────────────┤
+/// │ │
+/// │ { │
+/// │ "id": "0001", │
+/// │ "type": "donut", │
+/// │ "name": "Cake", │
+/// │ "ppu": 0.55, │
+/// │ "batters": { │
+/// │ "batter": [ │
+/// │ { "id": "1001", "type": "Regular" }, │
+/// │ { "id": "1002", "type": "Chocolate" }, │
+/// │ ] │
+/// │ }, │
+/// │ "topping": [ │
+/// │ { "id": "5001", "type": "None" }, │
+/// │ { "id": "5006", "type": "Chocolate with Sprinkles" }, │
+/// │ { "id": "5003", "type": "Chocolate" }, │
+/// │ { "id": "5004", "type": "Maple" } │
+/// │ ] │
+/// │ } │
+/// └───────────────────────────────────────────────────────────────┘"#);
+/// ```
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum AlignmentStrategy {
+ /// Apply alignment for cell content as a whole.
+ PerCell,
+ /// Apply alignment for each line of a cell content as a whole.
+ PerLine,
+}
+
+impl<R> CellOption<R, ColoredConfig> for AlignmentStrategy {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let mut formatting = *cfg.get_formatting(entity);
+ match &self {
+ AlignmentStrategy::PerCell => formatting.allow_lines_alignment = false,
+ AlignmentStrategy::PerLine => formatting.allow_lines_alignment = true,
+ }
+
+ cfg.set_formatting(entity, formatting);
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for AlignmentStrategy {
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ <Self as CellOption<R, ColoredConfig>>::change(self, records, cfg, Entity::Global)
+ }
+}
+
+impl<R, D> TableOption<R, D, CompactMultilineConfig> for AlignmentStrategy {
+ fn change(self, _: &mut R, cfg: &mut CompactMultilineConfig, _: &mut D) {
+ let mut f = cfg.get_formatting();
+ match &self {
+ AlignmentStrategy::PerCell => f.allow_lines_alignment = false,
+ AlignmentStrategy::PerLine => f.allow_lines_alignment = true,
+ }
+
+ *cfg = cfg.set_formatting(f);
+ }
+}
diff --git a/vendor/tabled/src/settings/formatting/charset.rs b/vendor/tabled/src/settings/formatting/charset.rs
new file mode 100644
index 000000000..c58effcd8
--- /dev/null
+++ b/vendor/tabled/src/settings/formatting/charset.rs
@@ -0,0 +1,108 @@
+use papergrid::{
+ config::Entity,
+ records::{ExactRecords, PeekableRecords},
+};
+
+use crate::{
+ grid::records::{Records, RecordsMut},
+ settings::{CellOption, TableOption},
+};
+
+/// A structure to handle special chars.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Charset;
+
+impl Charset {
+ /// Returns [`CleanCharset`] which removes all `\t` and `\r` occurences.
+ ///
+ /// Notice that tab is just removed rather then being replaced with spaces.
+ /// You might be better call [`TabSize`] first if you not expect such behavior.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{Table, settings::formatting::Charset};
+ ///
+ /// let text = "Some\ttext\t\twith \\tabs";
+ ///
+ /// let mut table = Table::new([text]);
+ /// table.with(Charset::clean());
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+--------------------+\n\
+ /// | &str |\n\
+ /// +--------------------+\n\
+ /// | Sometextwith \\tabs |\n\
+ /// +--------------------+"
+ /// )
+ /// ```
+ ///
+ /// [`TabSize`]: crate::settings::formatting::TabSize
+ pub fn clean() -> CleanCharset {
+ CleanCharset
+ }
+}
+
+/// [`CleanCharset`] removes all `\t` and `\r` occurences.
+///
+/// # Example
+///
+/// ```
+/// use tabled::{Table, settings::formatting::Charset};
+///
+/// let text = "Some text which was created on windows \r\n yes they use this \\r\\n";
+///
+/// let mut builder = Table::builder([text]);
+/// builder.set_header(["win. text"]);
+///
+/// let mut table = builder.build();
+/// table.with(Charset::clean());
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+-----------------------------------------+\n\
+/// | win. text |\n\
+/// +-----------------------------------------+\n\
+/// | Some text which was created on windows |\n\
+/// | yes they use this \\r\\n |\n\
+/// +-----------------------------------------+"
+/// )
+/// ```
+#[derive(Debug, Default, Clone)]
+pub struct CleanCharset;
+
+impl<R, D, C> TableOption<R, D, C> for CleanCharset
+where
+ for<'a> &'a R: Records,
+ R: RecordsMut<String>,
+{
+ fn change(self, records: &mut R, _: &mut C, _: &mut D) {
+ let mut list = vec![];
+ for (row, cells) in records.iter_rows().into_iter().enumerate() {
+ for (col, text) in cells.into_iter().enumerate() {
+ let text = text.as_ref().replace(['\t', '\r'], "");
+ list.push(((row, col), text));
+ }
+ }
+
+ for (pos, text) in list {
+ records.set(pos, text);
+ }
+ }
+}
+
+impl<R, C> CellOption<R, C> for CleanCharset
+where
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, _: &mut C, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+ for pos in entity.iter(count_rows, count_cols) {
+ let text = records.get_text(pos);
+ let text = text.replace(['\t', '\r'], "");
+ records.set(pos, text);
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/formatting/justification.rs b/vendor/tabled/src/settings/formatting/justification.rs
new file mode 100644
index 000000000..a427eb95e
--- /dev/null
+++ b/vendor/tabled/src/settings/formatting/justification.rs
@@ -0,0 +1,127 @@
+use crate::{
+ grid::{
+ color::AnsiColor,
+ config::{ColoredConfig, Entity},
+ },
+ settings::{CellOption, Color, TableOption},
+};
+
+/// Set a justification character and a color.
+///
+/// Default value is `' '` (`<space>`) with no color.
+///
+/// # Examples
+///
+/// Setting a justification character.
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::formatting::Justification,
+/// };
+///
+/// let mut table = Table::new(&[("Hello", ""), ("", "World")]);
+/// table.with(Justification::new('#'));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+-------+-------+\n\
+/// | &str# | &str# |\n\
+/// +-------+-------+\n\
+/// | Hello | ##### |\n\
+/// +-------+-------+\n\
+/// | ##### | World |\n\
+/// +-------+-------+"
+/// );
+/// ```
+///
+/// Setting a justification color.
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{formatting::Justification, Color},
+/// };
+///
+/// let mut table = Table::new(&[("Hello", ""), ("", "World")]);
+/// table.with(Justification::default().color(Color::BG_BRIGHT_RED));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+-------+-------+\n\
+/// | &str\u{1b}[101m \u{1b}[49m | &str\u{1b}[101m \u{1b}[49m |\n\
+/// +-------+-------+\n\
+/// | Hello | \u{1b}[101m \u{1b}[49m |\n\
+/// +-------+-------+\n\
+/// | \u{1b}[101m \u{1b}[49m | World |\n\
+/// +-------+-------+"
+/// );
+/// ```
+///
+/// Use different justification for different columns.
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{Modify, object::Columns, formatting::Justification},
+/// };
+///
+/// let mut table = Table::new(&[("Hello", ""), ("", "World")]);
+/// table.with(Modify::new(Columns::single(0)).with(Justification::new('#')));
+/// table.with(Modify::new(Columns::single(1)).with(Justification::new('@')));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+-------+-------+\n\
+/// | &str# | &str@ |\n\
+/// +-------+-------+\n\
+/// | Hello | @@@@@ |\n\
+/// +-------+-------+\n\
+/// | ##### | World |\n\
+/// +-------+-------+"
+/// );
+/// ```
+///
+#[derive(Debug, Default, Clone)]
+pub struct Justification {
+ c: Option<char>,
+ color: Option<AnsiColor<'static>>,
+}
+
+impl Justification {
+ /// Creates new [`Justification`] object.
+ pub fn new(c: char) -> Self {
+ Self {
+ c: Some(c),
+ color: None,
+ }
+ }
+
+ /// Sets a color for a justification.
+ pub fn color(self, color: Color) -> Self {
+ Self {
+ c: self.c,
+ color: Some(color.into()),
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for Justification {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let c = self.c.unwrap_or(' ');
+ let color = self.color;
+
+ cfg.set_justification(Entity::Global, c);
+ cfg.set_justification_color(Entity::Global, color);
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for Justification {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let c = self.c.unwrap_or(' ');
+ let color = self.color;
+
+ cfg.set_justification(entity, c);
+ cfg.set_justification_color(entity, color);
+ }
+}
diff --git a/vendor/tabled/src/settings/formatting/mod.rs b/vendor/tabled/src/settings/formatting/mod.rs
new file mode 100644
index 000000000..e3a56092f
--- /dev/null
+++ b/vendor/tabled/src/settings/formatting/mod.rs
@@ -0,0 +1,20 @@
+//! This module contains settings for render strategy of papergrid.
+//!
+//! - [`TrimStrategy`] and [`AlignmentStrategy`] allows to set [`Alignment`] settings.
+//! - [`TabSize`] sets a default tab size.
+//! - [`Charset`] responsible for special char treatment.
+//! - [`Justification`] responsible for justification space of content.
+//!
+//! [`Alignment`]: crate::settings::Alignment
+
+mod alignment_strategy;
+mod charset;
+mod justification;
+mod tab_size;
+mod trim_strategy;
+
+pub use alignment_strategy::AlignmentStrategy;
+pub use charset::{Charset, CleanCharset};
+pub use justification::Justification;
+pub use tab_size::TabSize;
+pub use trim_strategy::TrimStrategy;
diff --git a/vendor/tabled/src/settings/formatting/tab_size.rs b/vendor/tabled/src/settings/formatting/tab_size.rs
new file mode 100644
index 000000000..677d0d613
--- /dev/null
+++ b/vendor/tabled/src/settings/formatting/tab_size.rs
@@ -0,0 +1,62 @@
+use crate::{
+ grid::records::{Records, RecordsMut},
+ settings::TableOption,
+};
+
+/// Set a tab size.
+///
+/// The size is used in order to calculate width correctly.
+///
+/// Default value is 4 (basically 1 '\t' equals 4 spaces).
+///
+/// IMPORTANT: The tab character might be not present in output,
+/// it might be replaced by spaces.
+///
+/// # Example
+///
+/// ```
+/// use tabled::{Table, settings::formatting::TabSize};
+///
+/// let text = "Some\ttext\t\twith \\tabs";
+///
+/// let mut table = Table::new([text]);
+/// table.with(TabSize::new(4));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+--------------------------------+\n\
+/// | &str |\n\
+/// +--------------------------------+\n\
+/// | Some text with \\tabs |\n\
+/// +--------------------------------+"
+/// )
+/// ```
+#[derive(Debug, Default, Clone)]
+pub struct TabSize(usize);
+
+impl TabSize {
+ /// Creates new [`TabSize`] object.
+ pub fn new(size: usize) -> Self {
+ Self(size)
+ }
+}
+
+impl<R, D, C> TableOption<R, D, C> for TabSize
+where
+ for<'a> &'a R: Records,
+ R: RecordsMut<String>,
+{
+ fn change(self, records: &mut R, _: &mut C, _: &mut D) {
+ let mut list = vec![];
+ for (row, cells) in records.iter_rows().into_iter().enumerate() {
+ for (col, text) in cells.into_iter().enumerate() {
+ let text = text.as_ref().replace('\t', &" ".repeat(self.0));
+ list.push(((row, col), text));
+ }
+ }
+
+ for (pos, text) in list {
+ records.set(pos, text);
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/formatting/trim_strategy.rs b/vendor/tabled/src/settings/formatting/trim_strategy.rs
new file mode 100644
index 000000000..64ec7e10a
--- /dev/null
+++ b/vendor/tabled/src/settings/formatting/trim_strategy.rs
@@ -0,0 +1,118 @@
+use crate::{
+ grid::config::ColoredConfig,
+ grid::config::Entity,
+ settings::{CellOption, TableOption},
+};
+
+/// `TrimStrategy` determines if it's allowed to use empty space while doing [`Alignment`].
+///
+/// # Examples
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{
+/// Style, Modify, Alignment, object::Segment,
+/// formatting::{TrimStrategy, AlignmentStrategy}
+/// }
+/// };
+///
+/// let mut table = Table::new(&[" Hello World"]);
+/// table
+/// .with(Style::modern())
+/// .with(
+/// Modify::new(Segment::all())
+/// .with(Alignment::left())
+/// .with(TrimStrategy::Horizontal)
+/// );
+///
+/// // Note that nothing was changed exactly.
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "┌────────────────┐\n\
+/// │ &str │\n\
+/// ├────────────────┤\n\
+/// │ Hello World │\n\
+/// └────────────────┘"
+/// );
+///
+/// // To trim lines you would need also set [`AlignmentStrategy`].
+/// table.with(Modify::new(Segment::all()).with(AlignmentStrategy::PerLine));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "┌────────────────┐\n\
+/// │ &str │\n\
+/// ├────────────────┤\n\
+/// │ Hello World │\n\
+/// └────────────────┘"
+/// );
+///
+/// let mut table = Table::new(&[" \n\n\n Hello World"]);
+/// table
+/// .with(Style::modern())
+/// .with(
+/// Modify::new(Segment::all())
+/// .with(Alignment::center())
+/// .with(Alignment::top())
+/// .with(TrimStrategy::Vertical)
+/// );
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "┌─────────────────┐\n\
+/// │ &str │\n\
+/// ├─────────────────┤\n\
+/// │ Hello World │\n\
+/// │ │\n\
+/// │ │\n\
+/// │ │\n\
+/// └─────────────────┘"
+/// );
+/// ```
+///
+/// [`Alignment`]: crate::settings::Alignment
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum TrimStrategy {
+ /// Allow vertical trim.
+ Vertical,
+ /// Allow horizontal trim.
+ Horizontal,
+ /// Allow horizontal and vertical trim.
+ Both,
+ /// Doesn't allow any trim.
+ None,
+}
+
+impl<R> CellOption<R, ColoredConfig> for TrimStrategy {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let mut formatting = *cfg.get_formatting(entity);
+
+ // todo: could be changed to be a struct an enum like consts in `impl` block.
+ match self {
+ TrimStrategy::Vertical => {
+ formatting.vertical_trim = true;
+ }
+ TrimStrategy::Horizontal => {
+ formatting.horizontal_trim = true;
+ }
+ TrimStrategy::Both => {
+ formatting.vertical_trim = true;
+ formatting.horizontal_trim = true;
+ }
+ TrimStrategy::None => {
+ formatting.vertical_trim = false;
+ formatting.horizontal_trim = false;
+ }
+ }
+
+ cfg.set_formatting(entity, formatting);
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for TrimStrategy {
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ <Self as CellOption<_, _>>::change(self, records, cfg, Entity::Global)
+ }
+}
diff --git a/vendor/tabled/src/settings/height/cell_height_increase.rs b/vendor/tabled/src/settings/height/cell_height_increase.rs
new file mode 100644
index 000000000..39887f9f6
--- /dev/null
+++ b/vendor/tabled/src/settings/height/cell_height_increase.rs
@@ -0,0 +1,98 @@
+use crate::{
+ grid::config::ColoredConfig,
+ grid::config::Entity,
+ grid::dimension::CompleteDimensionVecRecords,
+ grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ grid::util::string::count_lines,
+ settings::{measurement::Measurement, peaker::Peaker, CellOption, Height, TableOption},
+};
+
+use super::TableHeightIncrease;
+
+/// A modification for cell/table to increase its height.
+///
+/// If used for a [`Table`] [`PriorityNone`] is used.
+///
+/// [`PriorityNone`]: crate::settings::peaker::PriorityNone
+/// [`Table`]: crate::Table
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct CellHeightIncrease<W = usize> {
+ height: W,
+}
+
+impl<W> CellHeightIncrease<W> {
+ /// Creates a new object of the structure.
+ pub fn new(height: W) -> Self
+ where
+ W: Measurement<Height>,
+ {
+ Self { height }
+ }
+
+ /// The priority makes scence only for table, so the function
+ /// converts it to [`TableHeightIncrease`] with a given priority.
+ pub fn priority<P>(self) -> TableHeightIncrease<W, P>
+ where
+ P: Peaker,
+ W: Measurement<Height>,
+ {
+ TableHeightIncrease::new(self.height).priority::<P>()
+ }
+}
+
+impl<W, R> CellOption<R, ColoredConfig> for CellHeightIncrease<W>
+where
+ W: Measurement<Height>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let height = self.height.measure(&*records, cfg);
+
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_columns) {
+ let is_valid_pos = pos.0 < count_rows && pos.1 < count_columns;
+ if !is_valid_pos {
+ continue;
+ }
+
+ let text = records.get_text(pos);
+
+ let cell_height = count_lines(text);
+ if cell_height >= height {
+ continue;
+ }
+
+ let content = add_lines(text, height - cell_height);
+ records.set(pos, content);
+ }
+ }
+}
+
+impl<R, W> TableOption<R, CompleteDimensionVecRecords<'static>, ColoredConfig>
+ for CellHeightIncrease<W>
+where
+ W: Measurement<Height>,
+ R: Records + ExactRecords + PeekableRecords,
+ for<'a> &'a R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ let height = self.height.measure(&*records, cfg);
+ TableHeightIncrease::new(height).change(records, cfg, dims)
+ }
+}
+
+fn add_lines(s: &str, n: usize) -> String {
+ let mut text = String::with_capacity(s.len() + n);
+ text.push_str(s);
+ text.extend(std::iter::repeat('\n').take(n));
+
+ text
+}
diff --git a/vendor/tabled/src/settings/height/cell_height_limit.rs b/vendor/tabled/src/settings/height/cell_height_limit.rs
new file mode 100644
index 000000000..788f0300c
--- /dev/null
+++ b/vendor/tabled/src/settings/height/cell_height_limit.rs
@@ -0,0 +1,103 @@
+use crate::{
+ grid::{
+ config::{ColoredConfig, Entity},
+ dimension::CompleteDimensionVecRecords,
+ records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ util::string::{count_lines, get_lines},
+ },
+ settings::{measurement::Measurement, peaker::Peaker, CellOption, Height, TableOption},
+};
+
+use super::table_height_limit::TableHeightLimit;
+
+/// A modification for cell/table to increase its height.
+///
+/// If used for a [`Table`] [`PriorityNone`] is used.
+///
+/// [`PriorityNone`]: crate::settings::peaker::PriorityNone
+/// [`Table`]: crate::Table
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct CellHeightLimit<W = usize> {
+ height: W,
+}
+
+impl<W> CellHeightLimit<W> {
+ /// Constructs a new object.
+ pub fn new(height: W) -> Self
+ where
+ W: Measurement<Height>,
+ {
+ Self { height }
+ }
+
+ /// Set's a priority by which the limit logic will be applied.
+ pub fn priority<P>(self) -> TableHeightLimit<W, P>
+ where
+ P: Peaker,
+ W: Measurement<Height>,
+ {
+ TableHeightLimit::new(self.height).priority::<P>()
+ }
+}
+
+impl<W, R> CellOption<R, ColoredConfig> for CellHeightLimit<W>
+where
+ W: Measurement<Height>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let height = self.height.measure(&*records, cfg);
+
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_columns) {
+ let is_valid_pos = pos.0 < count_rows && pos.1 < count_columns;
+ if !is_valid_pos {
+ continue;
+ }
+
+ let text = records.get_text(pos);
+ let count_lines = count_lines(text);
+
+ if count_lines <= height {
+ continue;
+ }
+
+ let content = limit_lines(text, height);
+ records.set(pos, content);
+ }
+ }
+}
+
+impl<R, W> TableOption<R, CompleteDimensionVecRecords<'static>, ColoredConfig>
+ for CellHeightLimit<W>
+where
+ W: Measurement<Height>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ let height = self.height.measure(&*records, cfg);
+ TableHeightLimit::new(height).change(records, cfg, dims)
+ }
+}
+
+fn limit_lines(s: &str, n: usize) -> String {
+ let mut text = String::new();
+ for (i, line) in get_lines(s).take(n).enumerate() {
+ if i > 0 {
+ text.push('\n');
+ }
+
+ text.push_str(&line);
+ }
+
+ text
+}
diff --git a/vendor/tabled/src/settings/height/height_list.rs b/vendor/tabled/src/settings/height/height_list.rs
new file mode 100644
index 000000000..7861dd4d2
--- /dev/null
+++ b/vendor/tabled/src/settings/height/height_list.rs
@@ -0,0 +1,47 @@
+use std::iter::FromIterator;
+
+use crate::{
+ grid::dimension::CompleteDimensionVecRecords,
+ grid::records::{ExactRecords, Records},
+ settings::TableOption,
+};
+
+/// A structure used to set [`Table`] height via a list of rows heights.
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct HeightList {
+ list: Vec<usize>,
+}
+
+impl HeightList {
+ /// Creates a new object.
+ pub fn new(list: Vec<usize>) -> Self {
+ Self { list }
+ }
+}
+
+impl From<Vec<usize>> for HeightList {
+ fn from(list: Vec<usize>) -> Self {
+ Self::new(list)
+ }
+}
+
+impl FromIterator<usize> for HeightList {
+ fn from_iter<T: IntoIterator<Item = usize>>(iter: T) -> Self {
+ Self::new(iter.into_iter().collect())
+ }
+}
+
+impl<R, C> TableOption<R, CompleteDimensionVecRecords<'static>, C> for HeightList
+where
+ R: ExactRecords + Records,
+{
+ fn change(self, records: &mut R, _: &mut C, dims: &mut CompleteDimensionVecRecords<'static>) {
+ if self.list.len() < records.count_rows() {
+ return;
+ }
+
+ let _ = dims.set_heights(self.list);
+ }
+}
diff --git a/vendor/tabled/src/settings/height/mod.rs b/vendor/tabled/src/settings/height/mod.rs
new file mode 100644
index 000000000..ad9caff77
--- /dev/null
+++ b/vendor/tabled/src/settings/height/mod.rs
@@ -0,0 +1,228 @@
+//! The module contains [`Height`] structure which is responsible for a table and cell height.
+
+mod cell_height_increase;
+mod cell_height_limit;
+mod height_list;
+mod table_height_increase;
+mod table_height_limit;
+mod util;
+
+use crate::settings::measurement::Measurement;
+
+pub use cell_height_increase::CellHeightIncrease;
+pub use cell_height_limit::CellHeightLimit;
+pub use height_list::HeightList;
+pub use table_height_increase::TableHeightIncrease;
+pub use table_height_limit::TableHeightLimit;
+
+/// Height is a abstract factory for height settings.
+///
+/// # Example
+///
+/// ```
+/// use tabled::{Table, settings::{Height, Settings}};
+///
+/// let data = vec![
+/// ("Some data", "here", "and here"),
+/// ("Some data on a next", "line", "right here"),
+/// ];
+///
+/// let table = Table::new(data)
+/// .with(Settings::new(Height::limit(10), Height::increase(10)))
+/// .to_string();
+///
+/// assert_eq!(
+/// table,
+/// "+---------------------+------+------------+\n\
+/// | &str | &str | &str |\n\
+/// | | | |\n\
+/// +---------------------+------+------------+\n\
+/// | Some data | here | and here |\n\
+/// | | | |\n\
+/// +---------------------+------+------------+\n\
+/// | Some data on a next | line | right here |\n\
+/// | | | |\n\
+/// +---------------------+------+------------+",
+/// )
+/// ```
+#[derive(Debug)]
+pub struct Height;
+
+impl Height {
+ /// Create [`CellHeightIncrease`] to set a table/cell height.
+ ///
+ /// # Example
+ ///
+ /// ## Cell height
+ ///
+ /// ```
+ /// use tabled::{Table, settings::{Height, Modify, object::Columns}};
+ ///
+ /// let data = vec![
+ /// ("Some data", "here", "and here"),
+ /// ("Some data on a next", "line", "right here"),
+ /// ];
+ ///
+ /// let table = Table::new(data)
+ /// .with(Modify::new(Columns::first()).with(Height::increase(5)))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+---------------------+------+------------+\n\
+ /// | &str | &str | &str |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// +---------------------+------+------------+\n\
+ /// | Some data | here | and here |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// +---------------------+------+------------+\n\
+ /// | Some data on a next | line | right here |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// +---------------------+------+------------+"
+ /// )
+ /// ```
+ ///
+ /// ## Table height
+ ///
+ /// ```
+ /// use tabled::{Table, settings::Height};
+ ///
+ /// let data = vec![
+ /// ("Some data", "here", "and here"),
+ /// ("Some data on a next", "line", "right here"),
+ /// ];
+ ///
+ /// let table = Table::new(data)
+ /// .with(Height::increase(10))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+---------------------+------+------------+\n\
+ /// | &str | &str | &str |\n\
+ /// | | | |\n\
+ /// +---------------------+------+------------+\n\
+ /// | Some data | here | and here |\n\
+ /// | | | |\n\
+ /// +---------------------+------+------------+\n\
+ /// | Some data on a next | line | right here |\n\
+ /// | | | |\n\
+ /// +---------------------+------+------------+",
+ /// )
+ /// ```
+ pub fn increase<W: Measurement<Height>>(width: W) -> CellHeightIncrease<W> {
+ CellHeightIncrease::new(width)
+ }
+
+ /// Create [`CellHeightLimit`] to set a table/cell height.
+ ///
+ /// # Example
+ ///
+ /// ## Cell height
+ ///
+ /// ```
+ /// use tabled::{Table, settings::{Height, Modify, object::Columns}};
+ ///
+ /// let data = vec![
+ /// ("Some\ndata", "here", "and here"),
+ /// ("Some\ndata on a next", "line", "right here"),
+ /// ];
+ ///
+ /// let table = Table::new(data)
+ /// .with(Modify::new(Columns::first()).with(Height::limit(1)))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+------+------+------------+\n\
+ /// | &str | &str | &str |\n\
+ /// +------+------+------------+\n\
+ /// | Some | here | and here |\n\
+ /// +------+------+------------+\n\
+ /// | Some | line | right here |\n\
+ /// +------+------+------------+"
+ /// )
+ /// ```
+ ///
+ /// ## Table height
+ ///
+ /// ```
+ /// use tabled::{Table, settings::Height};
+ ///
+ /// let data = vec![
+ /// ("Some\ndata", "here", "and here"),
+ /// ("Some\ndata on a next", "line", "right here"),
+ /// ];
+ ///
+ /// let table = Table::new(&data)
+ /// .with(Height::limit(6))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+------+------+------------+\n\
+ /// +------+------+------------+\n\
+ /// | Some | here | and here |\n\
+ /// +------+------+------------+\n\
+ /// | Some | line | right here |\n\
+ /// +------+------+------------+",
+ /// );
+ ///
+ /// let table = Table::new(&data)
+ /// .with(Height::limit(1))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+--+--+--+\n\
+ /// +--+--+--+\n\
+ /// +--+--+--+\n\
+ /// +--+--+--+",
+ /// );
+ /// ```
+ pub fn limit<W: Measurement<Height>>(width: W) -> CellHeightLimit<W> {
+ CellHeightLimit::new(width)
+ }
+
+ /// Create [`HeightList`] to set a table height to a constant list of row heights.
+ ///
+ /// Notice if you provide a list with `.len()` less than `Table::count_rows` then it will have no affect.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{Table, settings::{Height, Modify, object::Columns}};
+ ///
+ /// let data = vec![
+ /// ("Some\ndata", "here", "and here"),
+ /// ("Some\ndata on a next", "line", "right here"),
+ /// ];
+ ///
+ /// let table = Table::new(data)
+ /// .with(Height::list([1, 0, 2]))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+----------------+------+------------+\n\
+ /// | &str | &str | &str |\n\
+ /// +----------------+------+------------+\n\
+ /// +----------------+------+------------+\n\
+ /// | Some | line | right here |\n\
+ /// | data on a next | | |\n\
+ /// +----------------+------+------------+",
+ /// )
+ /// ```
+ pub fn list<I: IntoIterator<Item = usize>>(rows: I) -> HeightList {
+ HeightList::new(rows.into_iter().collect())
+ }
+}
diff --git a/vendor/tabled/src/settings/height/table_height_increase.rs b/vendor/tabled/src/settings/height/table_height_increase.rs
new file mode 100644
index 000000000..f727bf041
--- /dev/null
+++ b/vendor/tabled/src/settings/height/table_height_increase.rs
@@ -0,0 +1,90 @@
+use crate::{
+ grid::{
+ config::ColoredConfig,
+ dimension::CompleteDimensionVecRecords,
+ records::{ExactRecords, PeekableRecords, Records},
+ },
+ settings::{
+ measurement::Measurement,
+ peaker::{Peaker, PriorityNone},
+ Height, TableOption,
+ },
+};
+
+use super::util::get_table_height;
+
+/// A modification of a table to increase the table height.
+#[derive(Debug, Clone)]
+pub struct TableHeightIncrease<W = usize, P = PriorityNone> {
+ height: W,
+ priority: P,
+}
+
+impl<W> TableHeightIncrease<W, PriorityNone> {
+ /// Creates a new object.
+ pub fn new(height: W) -> Self
+ where
+ W: Measurement<Height>,
+ {
+ Self {
+ height,
+ priority: PriorityNone::default(),
+ }
+ }
+
+ /// Sets a different priority logic.
+ pub fn priority<P>(self) -> TableHeightIncrease<W, P>
+ where
+ P: Peaker,
+ {
+ TableHeightIncrease {
+ priority: P::create(),
+ height: self.height,
+ }
+ }
+}
+
+impl<R, W, P> TableOption<R, CompleteDimensionVecRecords<'static>, ColoredConfig>
+ for TableHeightIncrease<W, P>
+where
+ W: Measurement<Height>,
+ P: Peaker + Clone,
+ R: Records + ExactRecords + PeekableRecords,
+ for<'a> &'a R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return;
+ }
+
+ let height = self.height.measure(&*records, cfg);
+ let (total, mut heights) = get_table_height(&*records, cfg);
+ if total >= height {
+ return;
+ }
+
+ get_increase_list(&mut heights, height, total, self.priority);
+
+ let _ = dims.set_heights(heights);
+ }
+}
+
+fn get_increase_list<P>(list: &mut [usize], total: usize, mut current: usize, mut peaker: P)
+where
+ P: Peaker,
+{
+ while current != total {
+ let col = match peaker.peak(&[], list) {
+ Some(col) => col,
+ None => break,
+ };
+
+ list[col] += 1;
+ current += 1;
+ }
+}
diff --git a/vendor/tabled/src/settings/height/table_height_limit.rs b/vendor/tabled/src/settings/height/table_height_limit.rs
new file mode 100644
index 000000000..2a3d19bb7
--- /dev/null
+++ b/vendor/tabled/src/settings/height/table_height_limit.rs
@@ -0,0 +1,123 @@
+use crate::{
+ grid::{
+ config::ColoredConfig,
+ dimension::CompleteDimensionVecRecords,
+ records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ util::string::{count_lines, get_lines},
+ },
+ settings::{
+ measurement::Measurement,
+ peaker::{Peaker, PriorityNone},
+ Height, TableOption,
+ },
+};
+
+use super::util::get_table_height;
+
+/// A modification of a table to decrease the table height.
+#[derive(Debug)]
+pub struct TableHeightLimit<W = usize, P = PriorityNone> {
+ height: W,
+ priority: P,
+}
+
+impl<W> TableHeightLimit<W, PriorityNone> {
+ /// Creates a new object.
+ pub fn new(height: W) -> Self
+ where
+ W: Measurement<Height>,
+ {
+ Self {
+ height,
+ priority: PriorityNone::default(),
+ }
+ }
+
+ /// Sets a different priority logic.
+ pub fn priority<P>(self) -> TableHeightLimit<W, P>
+ where
+ P: Peaker,
+ {
+ TableHeightLimit {
+ priority: P::create(),
+ height: self.height,
+ }
+ }
+}
+
+impl<R, W, P> TableOption<R, CompleteDimensionVecRecords<'static>, ColoredConfig>
+ for TableHeightLimit<W, P>
+where
+ W: Measurement<Height>,
+ P: Peaker + Clone,
+ R: ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ let count_rows = records.count_rows();
+ let count_cols = (&*records).count_columns();
+
+ if count_rows == 0 || count_cols == 0 {
+ return;
+ }
+
+ let height = self.height.measure(&*records, cfg);
+ let (total, mut heights) = get_table_height(&*records, cfg);
+ if total <= height {
+ return;
+ }
+
+ decrease_list(&mut heights, total, height, self.priority);
+
+ for (row, &height) in heights.iter().enumerate() {
+ for col in 0..count_cols {
+ let text = records.get_text((row, col));
+ let count_lines = count_lines(text);
+
+ if count_lines <= height {
+ continue;
+ }
+
+ let text = limit_lines(text, height);
+
+ records.set((row, col), text);
+ }
+ }
+
+ let _ = dims.set_heights(heights);
+ }
+}
+
+fn decrease_list<P>(list: &mut [usize], total: usize, mut value: usize, mut peaker: P)
+where
+ P: Peaker,
+{
+ while value != total {
+ let p = peaker.peak(&[], list);
+ let row = match p {
+ Some(row) => row,
+ None => break,
+ };
+
+ list[row] -= 1;
+ value += 1;
+ }
+}
+
+fn limit_lines(s: &str, n: usize) -> String {
+ let mut text = String::new();
+ for (i, line) in get_lines(s).take(n).enumerate() {
+ if i > 0 {
+ text.push('\n');
+ }
+
+ text.push_str(&line);
+ }
+
+ text
+}
diff --git a/vendor/tabled/src/settings/height/util.rs b/vendor/tabled/src/settings/height/util.rs
new file mode 100644
index 000000000..c6917d925
--- /dev/null
+++ b/vendor/tabled/src/settings/height/util.rs
@@ -0,0 +1,22 @@
+use crate::grid::{
+ config::SpannedConfig,
+ dimension::SpannedGridDimension,
+ records::{ExactRecords, Records},
+};
+
+pub(crate) fn get_table_height<R: Records + ExactRecords>(
+ records: R,
+ cfg: &SpannedConfig,
+) -> (usize, Vec<usize>) {
+ let count_horizontals = cfg.count_horizontal(records.count_rows());
+
+ let margin = cfg.get_margin();
+ let margin_size = margin.top.size + margin.bottom.size;
+
+ let list = SpannedGridDimension::height(records, cfg);
+ let total = list.iter().sum::<usize>();
+
+ let total = total + count_horizontals + margin_size;
+
+ (total, list)
+}
diff --git a/vendor/tabled/src/settings/highlight/mod.rs b/vendor/tabled/src/settings/highlight/mod.rs
new file mode 100644
index 000000000..57df867b3
--- /dev/null
+++ b/vendor/tabled/src/settings/highlight/mod.rs
@@ -0,0 +1,452 @@
+//! This module contains a [`Highlight`] primitive, which helps
+//! changing a [`Border`] style of any segment on a [`Table`].
+//!
+//! [`Table`]: crate::Table
+
+use std::collections::HashSet;
+
+use crate::{
+ grid::{
+ config::{Border as GridBorder, ColoredConfig, Entity, Position, SpannedConfig},
+ records::{ExactRecords, Records},
+ },
+ settings::{object::Object, style::BorderColor, Border, TableOption},
+};
+
+/// Highlight modifies a table style by changing a border of a target [`Table`] segment.
+///
+/// # Example
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{Highlight, Border, Style, object::Segment}
+/// };
+///
+/// let data = [
+/// ("ELF", "Extensible Linking Format", true),
+/// ("DWARF", "", true),
+/// ("PE", "Portable Executable", false),
+/// ];
+///
+/// let table = Table::new(data.iter().enumerate())
+/// .with(Style::markdown())
+/// .with(Highlight::new(Segment::all(), Border::default().top('^').bottom('v')))
+/// .to_string();
+///
+/// assert_eq!(
+/// table,
+/// concat!(
+/// " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ \n",
+/// "| usize | &str | &str | bool |\n",
+/// "|-------|-------|---------------------------|-------|\n",
+/// "| 0 | ELF | Extensible Linking Format | true |\n",
+/// "| 1 | DWARF | | true |\n",
+/// "| 2 | PE | Portable Executable | false |\n",
+/// " vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ",
+/// ),
+/// );
+/// ```
+///
+/// It's possible to use [`Highlight`] for many kinds of figures.
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{
+/// Highlight, Border, Style,
+/// object::{Segment, Object}
+/// }
+/// };
+///
+/// let data = [
+/// ("ELF", "Extensible Linking Format", true),
+/// ("DWARF", "", true),
+/// ("PE", "Portable Executable", false),
+/// ];
+///
+/// let table = Table::new(data.iter().enumerate())
+/// .with(Style::markdown())
+/// .with(Highlight::new(Segment::all().not((0,0).and((1, 0)).and((0, 1)).and((0, 3))), Border::filled('*')))
+/// .to_string();
+///
+/// println!("{}", table);
+///
+/// assert_eq!(
+/// table,
+/// concat!(
+/// " ***************************** \n",
+/// "| usize | &str * &str * bool |\n",
+/// "|-------*********---------------------------*********\n",
+/// "| 0 * ELF | Extensible Linking Format | true *\n",
+/// "********* *\n",
+/// "* 1 | DWARF | | true *\n",
+/// "* *\n",
+/// "* 2 | PE | Portable Executable | false *\n",
+/// "*****************************************************",
+/// ),
+/// );
+/// ```
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Highlight<O> {
+ target: O,
+ border: Border,
+}
+
+// todo: Add BorderColor.
+
+impl<O> Highlight<O> {
+ /// Build a new instance of [`Highlight`]
+ ///
+ /// BE AWARE: if target exceeds boundaries it may panic.
+ pub fn new(target: O, border: Border) -> Self {
+ Self { target, border }
+ }
+}
+
+impl<O> Highlight<O> {
+ /// Build a new instance of [`HighlightColored`]
+ pub fn colored(target: O, border: BorderColor) -> HighlightColored<O> {
+ HighlightColored { target, border }
+ }
+}
+
+impl<O, R, D> TableOption<R, D, ColoredConfig> for Highlight<O>
+where
+ O: Object<R>,
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ let cells = self.target.cells(records);
+ let segments = split_segments(cells, count_rows, count_cols);
+
+ for sector in segments {
+ set_border(cfg, &sector, self.border);
+ }
+ }
+}
+
+/// A [`Highlight`] object which works with a [`BorderColored`]
+///
+/// [`BorderColored`]: crate::settings::style::BorderColor
+#[derive(Debug)]
+pub struct HighlightColored<O> {
+ target: O,
+ border: BorderColor,
+}
+
+impl<O, R, D> TableOption<R, D, ColoredConfig> for HighlightColored<O>
+where
+ O: Object<R>,
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ let cells = self.target.cells(records);
+ let segments = split_segments(cells, count_rows, count_cols);
+
+ for sector in segments {
+ set_border_color(cfg, sector, &self.border);
+ }
+ }
+}
+
+fn set_border_color(
+ cfg: &mut SpannedConfig,
+ sector: HashSet<(usize, usize)>,
+ border: &BorderColor,
+) {
+ if sector.is_empty() {
+ return;
+ }
+ let color = border.clone().into();
+ for &(row, col) in &sector {
+ let border = build_cell_border(&sector, (row, col), &color);
+ cfg.set_border_color((row, col), border);
+ }
+}
+
+fn split_segments(
+ cells: impl Iterator<Item = Entity>,
+ count_rows: usize,
+ count_cols: usize,
+) -> Vec<HashSet<(usize, usize)>> {
+ let mut segments: Vec<HashSet<(usize, usize)>> = Vec::new();
+ for entity in cells {
+ for cell in entity.iter(count_rows, count_cols) {
+ let found_segment = segments
+ .iter_mut()
+ .find(|s| s.iter().any(|&c| is_cell_connected(cell, c)));
+
+ match found_segment {
+ Some(segment) => {
+ let _ = segment.insert(cell);
+ }
+ None => {
+ let mut segment = HashSet::new();
+ let _ = segment.insert(cell);
+ segments.push(segment);
+ }
+ }
+ }
+ }
+
+ let mut squashed_segments: Vec<HashSet<(usize, usize)>> = Vec::new();
+ while !segments.is_empty() {
+ let mut segment = segments.remove(0);
+
+ let mut i = 0;
+ while i < segments.len() {
+ if is_segment_connected(&segment, &segments[i]) {
+ segment.extend(&segments[i]);
+ let _ = segments.remove(i);
+ } else {
+ i += 1;
+ }
+ }
+
+ squashed_segments.push(segment);
+ }
+
+ squashed_segments
+}
+
+fn is_cell_connected((row1, col1): (usize, usize), (row2, col2): (usize, usize)) -> bool {
+ if col1 == col2 && row1 == row2 + 1 {
+ return true;
+ }
+
+ if col1 == col2 && (row2 > 0 && row1 == row2 - 1) {
+ return true;
+ }
+
+ if row1 == row2 && col1 == col2 + 1 {
+ return true;
+ }
+
+ if row1 == row2 && (col2 > 0 && col1 == col2 - 1) {
+ return true;
+ }
+
+ false
+}
+
+fn is_segment_connected(
+ segment1: &HashSet<(usize, usize)>,
+ segment2: &HashSet<(usize, usize)>,
+) -> bool {
+ for &cell1 in segment1.iter() {
+ for &cell2 in segment2.iter() {
+ if is_cell_connected(cell1, cell2) {
+ return true;
+ }
+ }
+ }
+
+ false
+}
+
+fn set_border(cfg: &mut SpannedConfig, sector: &HashSet<(usize, usize)>, border: Border) {
+ if sector.is_empty() {
+ return;
+ }
+
+ let border = border.into();
+ for &pos in sector {
+ let border = build_cell_border(sector, pos, &border);
+ cfg.set_border(pos, border);
+ }
+}
+
+fn build_cell_border<T>(
+ sector: &HashSet<(usize, usize)>,
+ (row, col): Position,
+ border: &GridBorder<T>,
+) -> GridBorder<T>
+where
+ T: Default + Clone,
+{
+ let cell_has_top_neighbor = cell_has_top_neighbor(sector, row, col);
+ let cell_has_bottom_neighbor = cell_has_bottom_neighbor(sector, row, col);
+ let cell_has_left_neighbor = cell_has_left_neighbor(sector, row, col);
+ let cell_has_right_neighbor = cell_has_right_neighbor(sector, row, col);
+
+ let this_has_left_top_neighbor = is_there_left_top_cell(sector, row, col);
+ let this_has_right_top_neighbor = is_there_right_top_cell(sector, row, col);
+ let this_has_left_bottom_neighbor = is_there_left_bottom_cell(sector, row, col);
+ let this_has_right_bottom_neighbor = is_there_right_bottom_cell(sector, row, col);
+
+ let mut cell_border = GridBorder::default();
+ if let Some(c) = border.top.clone() {
+ if !cell_has_top_neighbor {
+ cell_border.top = Some(c.clone());
+
+ if cell_has_right_neighbor && !this_has_right_top_neighbor {
+ cell_border.right_top_corner = Some(c);
+ }
+ }
+ }
+ if let Some(c) = border.bottom.clone() {
+ if !cell_has_bottom_neighbor {
+ cell_border.bottom = Some(c.clone());
+
+ if cell_has_right_neighbor && !this_has_right_bottom_neighbor {
+ cell_border.right_bottom_corner = Some(c);
+ }
+ }
+ }
+ if let Some(c) = border.left.clone() {
+ if !cell_has_left_neighbor {
+ cell_border.left = Some(c.clone());
+
+ if cell_has_bottom_neighbor && !this_has_left_bottom_neighbor {
+ cell_border.left_bottom_corner = Some(c);
+ }
+ }
+ }
+ if let Some(c) = border.right.clone() {
+ if !cell_has_right_neighbor {
+ cell_border.right = Some(c.clone());
+
+ if cell_has_bottom_neighbor && !this_has_right_bottom_neighbor {
+ cell_border.right_bottom_corner = Some(c);
+ }
+ }
+ }
+ if let Some(c) = border.left_top_corner.clone() {
+ if !cell_has_left_neighbor && !cell_has_top_neighbor {
+ cell_border.left_top_corner = Some(c);
+ }
+ }
+ if let Some(c) = border.left_bottom_corner.clone() {
+ if !cell_has_left_neighbor && !cell_has_bottom_neighbor {
+ cell_border.left_bottom_corner = Some(c);
+ }
+ }
+ if let Some(c) = border.right_top_corner.clone() {
+ if !cell_has_right_neighbor && !cell_has_top_neighbor {
+ cell_border.right_top_corner = Some(c);
+ }
+ }
+ if let Some(c) = border.right_bottom_corner.clone() {
+ if !cell_has_right_neighbor && !cell_has_bottom_neighbor {
+ cell_border.right_bottom_corner = Some(c);
+ }
+ }
+ {
+ if !cell_has_bottom_neighbor {
+ if !cell_has_left_neighbor && this_has_left_top_neighbor {
+ if let Some(c) = border.right_top_corner.clone() {
+ cell_border.left_top_corner = Some(c);
+ }
+ }
+
+ if cell_has_left_neighbor && this_has_left_bottom_neighbor {
+ if let Some(c) = border.left_top_corner.clone() {
+ cell_border.left_bottom_corner = Some(c);
+ }
+ }
+
+ if !cell_has_right_neighbor && this_has_right_top_neighbor {
+ if let Some(c) = border.left_top_corner.clone() {
+ cell_border.right_top_corner = Some(c);
+ }
+ }
+
+ if cell_has_right_neighbor && this_has_right_bottom_neighbor {
+ if let Some(c) = border.right_top_corner.clone() {
+ cell_border.right_bottom_corner = Some(c);
+ }
+ }
+ }
+
+ if !cell_has_top_neighbor {
+ if !cell_has_left_neighbor && this_has_left_bottom_neighbor {
+ if let Some(c) = border.right_bottom_corner.clone() {
+ cell_border.left_bottom_corner = Some(c);
+ }
+ }
+
+ if cell_has_left_neighbor && this_has_left_top_neighbor {
+ if let Some(c) = border.left_bottom_corner.clone() {
+ cell_border.left_top_corner = Some(c);
+ }
+ }
+
+ if !cell_has_right_neighbor && this_has_right_bottom_neighbor {
+ if let Some(c) = border.left_bottom_corner.clone() {
+ cell_border.right_bottom_corner = Some(c);
+ }
+ }
+
+ if cell_has_right_neighbor && this_has_right_top_neighbor {
+ if let Some(c) = border.right_bottom_corner.clone() {
+ cell_border.right_top_corner = Some(c);
+ }
+ }
+ }
+ }
+
+ cell_border
+}
+
+fn cell_has_top_neighbor(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ row > 0 && sector.contains(&(row - 1, col))
+}
+
+fn cell_has_bottom_neighbor(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ sector.contains(&(row + 1, col))
+}
+
+fn cell_has_left_neighbor(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ col > 0 && sector.contains(&(row, col - 1))
+}
+
+fn cell_has_right_neighbor(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ sector.contains(&(row, col + 1))
+}
+
+fn is_there_left_top_cell(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ row > 0 && col > 0 && sector.contains(&(row - 1, col - 1))
+}
+
+fn is_there_right_top_cell(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ row > 0 && sector.contains(&(row - 1, col + 1))
+}
+
+fn is_there_left_bottom_cell(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ col > 0 && sector.contains(&(row + 1, col - 1))
+}
+
+fn is_there_right_bottom_cell(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ sector.contains(&(row + 1, col + 1))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_is_connected() {
+ assert!(is_cell_connected((0, 0), (0, 1)));
+ assert!(is_cell_connected((0, 0), (1, 0)));
+ assert!(!is_cell_connected((0, 0), (1, 1)));
+
+ assert!(is_cell_connected((0, 1), (0, 0)));
+ assert!(is_cell_connected((1, 0), (0, 0)));
+ assert!(!is_cell_connected((1, 1), (0, 0)));
+
+ assert!(is_cell_connected((1, 1), (0, 1)));
+ assert!(is_cell_connected((1, 1), (1, 0)));
+ assert!(is_cell_connected((1, 1), (2, 1)));
+ assert!(is_cell_connected((1, 1), (1, 2)));
+ assert!(!is_cell_connected((1, 1), (1, 1)));
+ }
+}
diff --git a/vendor/tabled/src/settings/locator/mod.rs b/vendor/tabled/src/settings/locator/mod.rs
new file mode 100644
index 000000000..b48391bb7
--- /dev/null
+++ b/vendor/tabled/src/settings/locator/mod.rs
@@ -0,0 +1,202 @@
+//! The module contains a [`Locator`] trait and implementations for it.
+
+use core::ops::Bound;
+use std::{
+ iter::{self, Once},
+ ops::{Range, RangeBounds},
+};
+
+use crate::{
+ grid::config::Entity,
+ grid::records::{ExactRecords, PeekableRecords, Records},
+ settings::object::{
+ Column, Columns, FirstColumn, FirstRow, LastColumn, LastRow, Object, Row, Rows,
+ },
+};
+
+/// Locator is an interface which searches for a particular thing in the [`Records`],
+/// and returns coordinate of the foundings if any.
+pub trait Locator<Records> {
+ /// A coordinate of the finding.
+ type Coordinate;
+ /// An iterator of the coordinates.
+ /// If it's empty it's considered that nothing is found.
+ type IntoIter: IntoIterator<Item = Self::Coordinate>;
+
+ /// Search for the thing in [`Records`], returning a list of coordinates.
+ fn locate(&mut self, records: Records) -> Self::IntoIter;
+}
+
+impl<B, R> Locator<R> for Columns<B>
+where
+ B: RangeBounds<usize>,
+ R: Records,
+{
+ type Coordinate = usize;
+ type IntoIter = Range<usize>;
+
+ fn locate(&mut self, records: R) -> Self::IntoIter {
+ let range = self.get_range();
+ let max = records.count_columns();
+ let (from, to) = bounds_to_usize(range.start_bound(), range.end_bound(), max);
+
+ from..to
+ }
+}
+
+impl<R> Locator<R> for Column {
+ type Coordinate = usize;
+ type IntoIter = Once<usize>;
+
+ fn locate(&mut self, _: R) -> Self::IntoIter {
+ iter::once((*self).into())
+ }
+}
+
+impl<R> Locator<R> for FirstColumn {
+ type Coordinate = usize;
+ type IntoIter = Once<usize>;
+
+ fn locate(&mut self, _: R) -> Self::IntoIter {
+ iter::once(0)
+ }
+}
+
+impl<R> Locator<R> for LastColumn
+where
+ R: Records,
+{
+ type Coordinate = usize;
+ type IntoIter = Once<usize>;
+
+ fn locate(&mut self, records: R) -> Self::IntoIter {
+ if records.count_columns() > 0 {
+ iter::once(records.count_columns() - 1)
+ } else {
+ iter::once(0)
+ }
+ }
+}
+
+impl<B, R> Locator<R> for Rows<B>
+where
+ R: Records,
+ B: RangeBounds<usize>,
+{
+ type Coordinate = usize;
+ type IntoIter = Range<usize>;
+
+ fn locate(&mut self, records: R) -> Self::IntoIter {
+ let (from, to) = bounds_to_usize(
+ self.get_range().start_bound(),
+ self.get_range().end_bound(),
+ records.count_columns(),
+ );
+
+ from..to
+ }
+}
+
+impl<R> Locator<R> for Row {
+ type Coordinate = usize;
+ type IntoIter = Once<usize>;
+
+ fn locate(&mut self, _: R) -> Self::IntoIter {
+ iter::once((*self).into())
+ }
+}
+
+impl<R> Locator<R> for FirstRow {
+ type Coordinate = usize;
+ type IntoIter = Once<usize>;
+
+ fn locate(&mut self, _: R) -> Self::IntoIter {
+ iter::once(0)
+ }
+}
+
+impl<R> Locator<R> for LastRow
+where
+ R: ExactRecords,
+{
+ type Coordinate = usize;
+ type IntoIter = Once<usize>;
+
+ fn locate(&mut self, records: R) -> Self::IntoIter {
+ if records.count_rows() > 0 {
+ iter::once(records.count_rows() - 1)
+ } else {
+ iter::once(0)
+ }
+ }
+}
+
+/// The structure is an implementation of [`Locator`] to search for a column by it's name.
+/// A name is considered be a value in a first row.
+///
+/// So even if in reality there's no header, the first row will be considered to be one.
+#[derive(Debug, Clone, Copy)]
+pub struct ByColumnName<S>(S);
+
+impl<S> ByColumnName<S> {
+ /// Constructs a new object of the structure.
+ pub fn new(text: S) -> Self
+ where
+ S: AsRef<str>,
+ {
+ Self(text)
+ }
+}
+
+impl<R, S> Locator<R> for ByColumnName<S>
+where
+ S: AsRef<str>,
+ R: Records + ExactRecords + PeekableRecords,
+{
+ type Coordinate = usize;
+ type IntoIter = Vec<usize>;
+
+ fn locate(&mut self, records: R) -> Self::IntoIter {
+ // todo: can be optimized by creating Iterator
+ (0..records.count_columns())
+ .filter(|col| records.get_text((0, *col)) == self.0.as_ref())
+ .collect::<Vec<_>>()
+ }
+}
+
+impl<S, R> Object<R> for ByColumnName<S>
+where
+ S: AsRef<str>,
+ R: Records + PeekableRecords + ExactRecords,
+{
+ type Iter = std::vec::IntoIter<Entity>;
+
+ fn cells(&self, records: &R) -> Self::Iter {
+ // todo: can be optimized by creating Iterator
+ (0..records.count_columns())
+ .filter(|col| records.get_text((0, *col)) == self.0.as_ref())
+ .map(Entity::Column)
+ .collect::<Vec<_>>()
+ .into_iter()
+ }
+}
+
+fn bounds_to_usize(
+ left: Bound<&usize>,
+ right: Bound<&usize>,
+ count_elements: usize,
+) -> (usize, usize) {
+ match (left, right) {
+ (Bound::Included(x), Bound::Included(y)) => (*x, y + 1),
+ (Bound::Included(x), Bound::Excluded(y)) => (*x, *y),
+ (Bound::Included(x), Bound::Unbounded) => (*x, count_elements),
+ (Bound::Unbounded, Bound::Unbounded) => (0, count_elements),
+ (Bound::Unbounded, Bound::Included(y)) => (0, y + 1),
+ (Bound::Unbounded, Bound::Excluded(y)) => (0, *y),
+ (Bound::Excluded(_), Bound::Unbounded)
+ | (Bound::Excluded(_), Bound::Included(_))
+ | (Bound::Excluded(_), Bound::Excluded(_)) => {
+ unreachable!("A start bound can't be excluded")
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/margin/mod.rs b/vendor/tabled/src/settings/margin/mod.rs
new file mode 100644
index 000000000..b86a1d3e2
--- /dev/null
+++ b/vendor/tabled/src/settings/margin/mod.rs
@@ -0,0 +1,137 @@
+//! This module contains a Margin settings of a [`Table`].
+//!
+//! # Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{settings::{Margin, Style}, Table};
+//!
+//! let data = vec!["Hello", "World", "!"];
+//!
+//! let mut table = Table::new(data);
+//! table.with(Style::markdown()).with(Margin::new(3, 3, 1, 0));
+//!
+//! assert_eq!(
+//! table.to_string(),
+//! concat!(
+//! " \n",
+//! " | &str | \n",
+//! " |-------| \n",
+//! " | Hello | \n",
+//! " | World | \n",
+//! " | ! | ",
+//! )
+//! );
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use crate::{
+ grid::{
+ color::StaticColor,
+ config::{CompactConfig, CompactMultilineConfig},
+ config::{Indent, Sides},
+ },
+ settings::TableOption,
+};
+
+#[cfg(feature = "std")]
+use crate::grid::{color::AnsiColor, config::ColoredConfig};
+
+/// Margin is responsible for a left/right/top/bottom outer indent of a grid.
+///
+#[cfg_attr(feature = "std", doc = "```")]
+#[cfg_attr(not(feature = "std"), doc = "```ignore")]
+/// # use tabled::{settings::Margin, Table};
+/// # let data: Vec<&'static str> = Vec::new();
+/// let table = Table::new(&data)
+/// .with(Margin::new(1, 1, 1, 1).fill('>', '<', 'V', '^'));
+/// ```
+#[derive(Debug, Clone)]
+pub struct Margin<C = StaticColor> {
+ indent: Sides<Indent>,
+ colors: Option<Sides<C>>,
+}
+
+impl Margin {
+ /// Construct's an Margin object.
+ ///
+ /// It uses space(' ') as a default fill character.
+ /// To set a custom character you can use [`Margin::fill`] function.
+ pub const fn new(left: usize, right: usize, top: usize, bottom: usize) -> Self {
+ Self {
+ indent: Sides::new(
+ Indent::spaced(left),
+ Indent::spaced(right),
+ Indent::spaced(top),
+ Indent::spaced(bottom),
+ ),
+ colors: None,
+ }
+ }
+}
+
+impl<Color> Margin<Color> {
+ /// The function, sets a characters for the margin on an each side.
+ pub const fn fill(mut self, left: char, right: char, top: char, bottom: char) -> Self {
+ self.indent.left.fill = left;
+ self.indent.right.fill = right;
+ self.indent.top.fill = top;
+ self.indent.bottom.fill = bottom;
+ self
+ }
+
+ /// The function, sets a characters for the margin on an each side.
+ pub fn colorize<C>(self, left: C, right: C, top: C, bottom: C) -> Margin<C> {
+ Margin {
+ indent: self.indent,
+ colors: Some(Sides::new(left, right, top, bottom)),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, D, C> TableOption<R, D, ColoredConfig> for Margin<C>
+where
+ C: Into<AnsiColor<'static>> + Clone,
+{
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let indent = self.indent;
+ let margin = Sides::new(indent.left, indent.right, indent.top, indent.bottom);
+ cfg.set_margin(margin);
+
+ if let Some(colors) = &self.colors {
+ let margin = Sides::new(
+ Some(colors.left.clone().into()),
+ Some(colors.right.clone().into()),
+ Some(colors.top.clone().into()),
+ Some(colors.bottom.clone().into()),
+ );
+ cfg.set_margin_color(margin);
+ }
+ }
+}
+
+impl<R, D, C> TableOption<R, D, CompactConfig> for Margin<C>
+where
+ C: Into<StaticColor> + Clone,
+{
+ fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
+ *cfg = cfg.set_margin(self.indent);
+
+ if let Some(c) = self.colors {
+ // todo: make a new method (BECAUSE INTO doesn't work) try_into();
+ let colors = Sides::new(c.left.into(), c.right.into(), c.top.into(), c.bottom.into());
+ *cfg = cfg.set_margin_color(colors);
+ }
+ }
+}
+
+impl<R, D, C> TableOption<R, D, CompactMultilineConfig> for Margin<C>
+where
+ C: Into<StaticColor> + Clone,
+{
+ fn change(self, records: &mut R, cfg: &mut CompactMultilineConfig, dimension: &mut D) {
+ self.change(records, cfg.as_mut(), dimension)
+ }
+}
diff --git a/vendor/tabled/src/settings/measurement/mod.rs b/vendor/tabled/src/settings/measurement/mod.rs
new file mode 100644
index 000000000..f93cc6b4e
--- /dev/null
+++ b/vendor/tabled/src/settings/measurement/mod.rs
@@ -0,0 +1,161 @@
+//! The module contains [`Measurement`] trait and its implementations to be used in [`Height`] and [`Width`].;
+
+use crate::{
+ grid::config::SpannedConfig,
+ grid::dimension::SpannedGridDimension,
+ grid::records::{ExactRecords, PeekableRecords, Records},
+ grid::util::string::{self, string_width_multiline},
+ settings::{Height, Width},
+};
+
+/// A width value which can be obtained on behalf of [`Table`].
+///
+/// [`Table`]: crate::Table
+pub trait Measurement<Attribute> {
+ /// Returns a measurement value.
+ fn measure<R: Records + ExactRecords + PeekableRecords>(
+ &self,
+ records: R,
+ cfg: &SpannedConfig,
+ ) -> usize;
+}
+
+impl<T> Measurement<T> for usize {
+ fn measure<R>(&self, _: R, _: &SpannedConfig) -> usize {
+ *self
+ }
+}
+
+/// Max width value.
+#[derive(Debug)]
+pub struct Max;
+
+impl Measurement<Width> for Max {
+ fn measure<R: Records + ExactRecords + PeekableRecords>(
+ &self,
+ records: R,
+ _: &SpannedConfig,
+ ) -> usize {
+ grid_widths(&records)
+ .map(|r| r.max().unwrap_or(0))
+ .max()
+ .unwrap_or(0)
+ }
+}
+
+impl Measurement<Height> for Max {
+ fn measure<R: Records + ExactRecords + PeekableRecords>(
+ &self,
+ records: R,
+ _: &SpannedConfig,
+ ) -> usize {
+ records_heights(&records)
+ .map(|r| r.max().unwrap_or(0))
+ .max()
+ .unwrap_or(0)
+ }
+}
+
+/// Min width value.
+#[derive(Debug)]
+pub struct Min;
+
+impl Measurement<Width> for Min {
+ fn measure<R: Records + ExactRecords + PeekableRecords>(
+ &self,
+ records: R,
+ _: &SpannedConfig,
+ ) -> usize {
+ grid_widths(&records)
+ .map(|r| r.min().unwrap_or(0))
+ .max()
+ .unwrap_or(0)
+ }
+}
+
+impl Measurement<Height> for Min {
+ fn measure<R: Records + ExactRecords + PeekableRecords>(
+ &self,
+ records: R,
+ _: &SpannedConfig,
+ ) -> usize {
+ records_heights(&records)
+ .map(|r| r.max().unwrap_or(0))
+ .min()
+ .unwrap_or(0)
+ }
+}
+
+/// Percent from a total table width.
+#[derive(Debug)]
+pub struct Percent(pub usize);
+
+impl Measurement<Width> for Percent {
+ fn measure<R>(&self, records: R, cfg: &SpannedConfig) -> usize
+ where
+ R: Records,
+ {
+ let (_, total) = get_table_widths_with_total(records, cfg);
+ (total * self.0) / 100
+ }
+}
+
+impl Measurement<Height> for Percent {
+ fn measure<R>(&self, records: R, cfg: &SpannedConfig) -> usize
+ where
+ R: Records + ExactRecords,
+ {
+ let (_, total) = get_table_heights_width_total(records, cfg);
+ (total * self.0) / 100
+ }
+}
+
+fn grid_widths<R: Records + ExactRecords + PeekableRecords>(
+ records: &R,
+) -> impl Iterator<Item = impl Iterator<Item = usize> + '_> + '_ {
+ let (count_rows, count_cols) = (records.count_rows(), records.count_columns());
+ (0..count_rows).map(move |row| {
+ (0..count_cols).map(move |col| string_width_multiline(records.get_text((row, col))))
+ })
+}
+
+fn get_table_widths_with_total<R>(records: R, cfg: &SpannedConfig) -> (Vec<usize>, usize)
+where
+ R: Records,
+{
+ let widths = SpannedGridDimension::width(records, cfg);
+ let total_width = get_table_total_width(&widths, cfg);
+ (widths, total_width)
+}
+
+fn get_table_total_width(list: &[usize], cfg: &SpannedConfig) -> usize {
+ let total = list.iter().sum::<usize>();
+
+ total + cfg.count_vertical(list.len())
+}
+
+fn records_heights<R>(records: &R) -> impl Iterator<Item = impl Iterator<Item = usize> + '_> + '_
+where
+ R: Records + ExactRecords + PeekableRecords,
+{
+ (0..records.count_rows()).map(move |row| {
+ (0..records.count_columns())
+ .map(move |col| string::count_lines(records.get_text((row, col))))
+ })
+}
+
+fn get_table_heights_width_total<R>(records: R, cfg: &SpannedConfig) -> (Vec<usize>, usize)
+where
+ R: Records,
+{
+ let list = SpannedGridDimension::height(records, cfg);
+ let total = get_table_total_height(&list, cfg);
+ (list, total)
+}
+
+fn get_table_total_height(list: &[usize], cfg: &SpannedConfig) -> usize {
+ let total = list.iter().sum::<usize>();
+ let counth = cfg.count_horizontal(list.len());
+
+ total + counth
+}
diff --git a/vendor/tabled/src/settings/merge/mod.rs b/vendor/tabled/src/settings/merge/mod.rs
new file mode 100644
index 000000000..51fa2bbf6
--- /dev/null
+++ b/vendor/tabled/src/settings/merge/mod.rs
@@ -0,0 +1,196 @@
+//! The module contains a set of methods to merge cells together via [`Span`]s.
+//!
+//! [`Span`]: crate::settings::span::Span
+
+use crate::{
+ grid::config::ColoredConfig,
+ grid::records::{ExactRecords, PeekableRecords, Records},
+ settings::TableOption,
+};
+
+/// Merge to combine duplicates together, using [`Span`].
+///
+/// [`Span`]: crate::settings::span::Span
+#[derive(Debug)]
+pub struct Merge;
+
+impl Merge {
+ /// Vertical merge.
+ pub fn vertical() -> MergeDuplicatesVertical {
+ MergeDuplicatesVertical
+ }
+
+ /// Horizontal merge.
+ pub fn horizontal() -> MergeDuplicatesHorizontal {
+ MergeDuplicatesHorizontal
+ }
+}
+
+/// A modificator for [`Table`] which looks up for duplicates in columns and
+/// in case of duplicate merges the cells together using [`Span`].
+///
+/// [`Table`]: crate::Table
+/// [`Span`]: crate::settings::span::Span
+#[derive(Debug)]
+pub struct MergeDuplicatesVertical;
+
+impl<R, D> TableOption<R, D, ColoredConfig> for MergeDuplicatesVertical
+where
+ R: Records + PeekableRecords + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ if count_rows == 0 || count_cols == 0 {
+ return;
+ }
+
+ for column in 0..count_cols {
+ let mut repeat_length = 0;
+ let mut repeat_value = String::new();
+ let mut repeat_is_set = false;
+ let mut last_is_row_span = false;
+ for row in (0..count_rows).rev() {
+ if last_is_row_span {
+ last_is_row_span = false;
+ continue;
+ }
+
+ // we need to mitigate messing existing spans
+ let is_cell_visible = cfg.is_cell_visible((row, column));
+ let is_row_span_cell = cfg.get_column_span((row, column)).is_some();
+
+ if !repeat_is_set {
+ if !is_cell_visible {
+ continue;
+ }
+
+ if is_row_span_cell {
+ continue;
+ }
+
+ repeat_length = 1;
+ repeat_value = records.get_text((row, column)).to_owned();
+ repeat_is_set = true;
+ continue;
+ }
+
+ if is_row_span_cell {
+ repeat_is_set = false;
+ last_is_row_span = true;
+ continue;
+ }
+
+ if !is_cell_visible {
+ repeat_is_set = false;
+ continue;
+ }
+
+ let text = records.get_text((row, column));
+ let is_duplicate = text == repeat_value;
+
+ if is_duplicate {
+ repeat_length += 1;
+ continue;
+ }
+
+ if repeat_length > 1 {
+ cfg.set_row_span((row + 1, column), repeat_length);
+ }
+
+ repeat_length = 1;
+ repeat_value = records.get_text((row, column)).to_owned();
+ }
+
+ if repeat_length > 1 {
+ cfg.set_row_span((0, column), repeat_length);
+ }
+ }
+ }
+}
+
+/// A modificator for [`Table`] which looks up for duplicates in rows and
+/// in case of duplicate merges the cells together using [`Span`].
+///
+/// [`Table`]: crate::Table
+/// [`Span`]: crate::settings::span::Span
+#[derive(Debug)]
+pub struct MergeDuplicatesHorizontal;
+
+impl<R, D> TableOption<R, D, ColoredConfig> for MergeDuplicatesHorizontal
+where
+ R: Records + PeekableRecords + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ if count_rows == 0 || count_cols == 0 {
+ return;
+ }
+
+ for row in 0..count_rows {
+ let mut repeat_length = 0;
+ let mut repeat_value = String::new();
+ let mut repeat_is_set = false;
+ let mut last_is_col_span = false;
+
+ for column in (0..count_cols).rev() {
+ if last_is_col_span {
+ last_is_col_span = false;
+ continue;
+ }
+
+ // we need to mitigate messing existing spans
+ let is_cell_visible = cfg.is_cell_visible((row, column));
+ let is_col_span_cell = cfg.get_row_span((row, column)).is_some();
+
+ if !repeat_is_set {
+ if !is_cell_visible {
+ continue;
+ }
+
+ if is_col_span_cell {
+ continue;
+ }
+
+ repeat_length = 1;
+ repeat_value = records.get_text((row, column)).to_owned();
+ repeat_is_set = true;
+ continue;
+ }
+
+ if is_col_span_cell {
+ repeat_is_set = false;
+ last_is_col_span = true;
+ continue;
+ }
+
+ if !is_cell_visible {
+ repeat_is_set = false;
+ continue;
+ }
+
+ let text = records.get_text((row, column));
+ let is_duplicate = text == repeat_value;
+
+ if is_duplicate {
+ repeat_length += 1;
+ continue;
+ }
+
+ if repeat_length > 1 {
+ cfg.set_column_span((row, column + 1), repeat_length);
+ }
+
+ repeat_length = 1;
+ repeat_value = records.get_text((row, column)).to_owned();
+ }
+
+ if repeat_length > 1 {
+ cfg.set_column_span((row, 0), repeat_length);
+ }
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/mod.rs b/vendor/tabled/src/settings/mod.rs
new file mode 100644
index 000000000..a89037cbd
--- /dev/null
+++ b/vendor/tabled/src/settings/mod.rs
@@ -0,0 +1,135 @@
+//! Module contains various table configuration settings.
+//!
+//! There 2 types of settings;
+//!
+//! - [`CellOption`] which can modify only a cell.
+//! - [`TableOption`] which can modify table as a whole.
+//!
+//! [`CellOption`] works on behave of [`Modify`] which is actually a [`TableOption`].
+//!
+//! Notice that it's possble to combine settings together by the help of [`Settings`].
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::{Settings, Style, Padding}};
+//!
+//! let table_config = Settings::default()
+//! .with(Padding::new(2, 2, 1, 1))
+//! .with(Style::rounded());
+//!
+//! let data = [[2023;9]; 3];
+//!
+//! let table = Table::new(data).with(table_config).to_string();
+//!
+//! assert_eq!(
+//! table,
+//! "╭────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────╮\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! ├────────┼────────┼────────┼────────┼────────┼────────┼────────┼────────┼────────┤\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! ╰────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────╯"
+//! )
+//! ```
+
+mod cell_option;
+mod settings_list;
+mod table_option;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod object;
+
+#[cfg(feature = "std")]
+mod modify;
+
+mod alignment;
+mod extract;
+mod margin;
+mod padding;
+mod rotate;
+
+#[cfg(feature = "std")]
+mod color;
+#[cfg(feature = "std")]
+mod concat;
+#[cfg(feature = "std")]
+mod duplicate;
+
+pub mod style;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod disable;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod format;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod formatting;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod height;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod highlight;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod locator;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod measurement;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod merge;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod panel;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod peaker;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+mod shadow;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod span;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod split;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod themes;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod width;
+
+pub use cell_option::CellOption;
+pub use settings_list::{EmptySettings, Settings};
+pub use table_option::TableOption;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use modify::{Modify, ModifyList};
+
+pub use self::{
+ alignment::Alignment, extract::Extract, margin::Margin, padding::Padding, rotate::Rotate,
+ style::Style,
+};
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use self::{
+ color::Color, concat::Concat, disable::Disable, duplicate::Dup, format::Format, height::Height,
+ highlight::Highlight, merge::Merge, panel::Panel, shadow::Shadow, span::Span, style::Border,
+ width::Width,
+};
diff --git a/vendor/tabled/src/settings/modify.rs b/vendor/tabled/src/settings/modify.rs
new file mode 100644
index 000000000..c712a255e
--- /dev/null
+++ b/vendor/tabled/src/settings/modify.rs
@@ -0,0 +1,81 @@
+use crate::{
+ grid::records::{ExactRecords, Records},
+ settings::{object::Object, CellOption, Settings, TableOption},
+};
+
+/// Modify structure provide an abstraction, to be able to apply
+/// a set of [`CellOption`]s to the same object.
+///
+/// Be aware that the settings are applied all to a cell at a time.
+/// So sometimes you may need to make a several calls of [`Modify`] in order to achieve the desired affect.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Modify<O> {
+ obj: O,
+}
+
+impl<O> Modify<O> {
+ /// Creates a new [`Modify`] without any options.
+ pub const fn new(obj: O) -> Self {
+ Self { obj }
+ }
+
+ /// A function which combines together [`Modify::new`] and [`Modify::with`] calls.
+ pub const fn list<M>(obj: O, next: M) -> ModifyList<O, M> {
+ ModifyList {
+ obj,
+ modifiers: next,
+ }
+ }
+
+ /// It's a generic function which stores a [`CellOption`].
+ ///
+ /// IMPORTANT:
+ /// The function *doesn't* changes a [`Table`].
+ /// [`Table`] will be changed only after passing [`Modify`] object to [`Table::with`].
+ ///
+ /// [`Table`]: crate::Table
+ /// [`Table::with`]: crate::Table::with
+ pub fn with<M>(self, next: M) -> ModifyList<O, M> {
+ ModifyList {
+ obj: self.obj,
+ modifiers: next,
+ }
+ }
+}
+
+/// This is a container of [`CellOption`]s which are applied to a set [`Object`].
+#[derive(Debug)]
+pub struct ModifyList<O, S> {
+ obj: O,
+ modifiers: S,
+}
+
+impl<O, M1> ModifyList<O, M1> {
+ /// With a generic function which stores a [`CellOption`].
+ ///
+ /// IMPORTANT:
+ /// The function *doesn't* changes a [`Table`].
+ /// [`Table`] will be changed only after passing [`Modify`] object to [`Table::with`].
+ ///
+ /// [`Table`]: crate::Table
+ /// [`Table::with`]: crate::Table::with
+ pub fn with<M2>(self, next: M2) -> ModifyList<O, Settings<M1, M2>> {
+ ModifyList {
+ obj: self.obj,
+ modifiers: Settings::new(self.modifiers, next),
+ }
+ }
+}
+
+impl<O, M, R, D, C> TableOption<R, D, C> for ModifyList<O, M>
+where
+ O: Object<R>,
+ M: CellOption<R, C> + Clone,
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut C, _: &mut D) {
+ for entity in self.obj.cells(records) {
+ self.modifiers.clone().change(records, cfg, entity);
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/object/cell.rs b/vendor/tabled/src/settings/object/cell.rs
new file mode 100644
index 000000000..0b0463c9a
--- /dev/null
+++ b/vendor/tabled/src/settings/object/cell.rs
@@ -0,0 +1,70 @@
+use crate::{
+ grid::config::{Entity, Position},
+ settings::object::Object,
+};
+
+/// Cell denotes a particular cell on a [`Table`].
+///
+/// For example such table has 4 cells.
+/// Which indexes are (0, 0), (0, 1), (1, 0), (1, 1).
+///
+/// ```text
+/// ┌───┬───┐
+/// │ 0 │ 1 │
+/// ├───┼───┤
+/// │ 1 │ 2 │
+/// └───┴───┘
+/// ```
+///
+/// [`Table`]: crate::Table
+#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Cell(usize, usize);
+
+impl Cell {
+ /// Create new cell structure.
+ pub fn new(row: usize, col: usize) -> Self {
+ Self(row, col)
+ }
+}
+
+impl From<Position> for Cell {
+ fn from((row, col): Position) -> Self {
+ Self(row, col)
+ }
+}
+
+impl<I> Object<I> for Cell {
+ type Iter = EntityOnce;
+
+ fn cells(&self, _: &I) -> Self::Iter {
+ EntityOnce::new(Some(Entity::Cell(self.0, self.1)))
+ }
+}
+
+impl<I> Object<I> for Position {
+ type Iter = EntityOnce;
+
+ fn cells(&self, _: &I) -> Self::Iter {
+ EntityOnce::new(Some(Entity::Cell(self.0, self.1)))
+ }
+}
+
+/// An [`Iterator`] which returns an entity once.
+#[derive(Debug)]
+pub struct EntityOnce {
+ entity: Option<Entity>,
+}
+
+impl EntityOnce {
+ pub(crate) const fn new(entity: Option<Entity>) -> Self {
+ Self { entity }
+ }
+}
+
+impl Iterator for EntityOnce {
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.entity.take()
+ }
+}
diff --git a/vendor/tabled/src/settings/object/columns.rs b/vendor/tabled/src/settings/object/columns.rs
new file mode 100644
index 000000000..bcbe2acf5
--- /dev/null
+++ b/vendor/tabled/src/settings/object/columns.rs
@@ -0,0 +1,209 @@
+use std::ops::{Add, RangeBounds, Sub};
+
+use crate::{
+ grid::config::Entity,
+ grid::records::{ExactRecords, Records},
+ settings::object::{cell::EntityOnce, Object},
+};
+
+use super::util::bounds_to_usize;
+
+/// Column denotes a set of cells on given columns on a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Columns<R> {
+ range: R,
+}
+
+impl<R> Columns<R> {
+ /// Returns a new instance of [`Columns`] for a range of columns.
+ ///
+ /// If the boundaries are exceeded it may panic.
+ pub fn new(range: R) -> Self
+ where
+ R: RangeBounds<usize>,
+ {
+ Self { range }
+ }
+
+ pub(crate) fn get_range(&self) -> &R {
+ &self.range
+ }
+}
+
+impl Columns<()> {
+ /// Returns a new instance of [`Columns`] for a single column.
+ ///
+ /// If the boundaries are exceeded it may panic.
+ pub fn single(index: usize) -> Column {
+ Column(index)
+ }
+
+ /// Returns a new instance of [`Columns`] for a first column.
+ ///
+ /// If the boundaries are exceeded the object will produce no cells.
+ pub fn first() -> FirstColumn {
+ FirstColumn
+ }
+
+ /// Returns a new instance of [`Columns`] for a last column.
+ ///
+ /// If the boundaries are exceeded the object will produce no cells.
+ pub fn last() -> LastColumn {
+ LastColumn
+ }
+}
+
+impl<I, R> Object<I> for Columns<R>
+where
+ R: RangeBounds<usize>,
+ I: Records,
+{
+ type Iter = ColumnsIter;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let max = records.count_columns();
+ let start = self.range.start_bound();
+ let end = self.range.end_bound();
+ let (x, y) = bounds_to_usize(start, end, max);
+
+ ColumnsIter::new(x, y)
+ }
+}
+
+/// `FirstColumn` represents the first column on a grid.
+#[derive(Debug)]
+pub struct FirstColumn;
+
+impl<I> Object<I> for FirstColumn
+where
+ I: Records + ExactRecords,
+{
+ type Iter = EntityOnce;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return EntityOnce::new(None);
+ }
+
+ EntityOnce::new(Some(Entity::Column(0)))
+ }
+}
+
+impl Add<usize> for FirstColumn {
+ type Output = Column;
+
+ fn add(self, rhs: usize) -> Self::Output {
+ Column(rhs)
+ }
+}
+
+/// `LastColumn` represents the last column on a grid.
+#[derive(Debug)]
+pub struct LastColumn;
+
+impl<I> Object<I> for LastColumn
+where
+ I: Records + ExactRecords,
+{
+ type Iter = EntityOnce;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return EntityOnce::new(None);
+ }
+
+ let col = records.count_columns().saturating_sub(1);
+ EntityOnce::new(Some(Entity::Column(col)))
+ }
+}
+
+impl Sub<usize> for LastColumn {
+ type Output = LastColumnOffset;
+
+ fn sub(self, rhs: usize) -> Self::Output {
+ LastColumnOffset { offset: rhs }
+ }
+}
+
+/// Column represents a single column on a grid.
+#[derive(Debug, Clone, Copy)]
+pub struct Column(usize);
+
+impl<I> Object<I> for Column {
+ type Iter = EntityOnce;
+
+ fn cells(&self, _: &I) -> Self::Iter {
+ EntityOnce::new(Some(Entity::Column(self.0)))
+ }
+}
+
+impl From<usize> for Column {
+ fn from(i: usize) -> Self {
+ Self(i)
+ }
+}
+
+impl From<Column> for usize {
+ fn from(val: Column) -> Self {
+ val.0
+ }
+}
+
+/// `LastColumnOffset` represents a single column on a grid indexed via offset from the last column.
+#[derive(Debug)]
+pub struct LastColumnOffset {
+ offset: usize,
+}
+
+impl<I> Object<I> for LastColumnOffset
+where
+ I: Records + ExactRecords,
+{
+ type Iter = EntityOnce;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return EntityOnce::new(None);
+ }
+
+ let col = records.count_columns().saturating_sub(1);
+ if self.offset > col {
+ return EntityOnce::new(None);
+ }
+
+ let col = col - self.offset;
+ EntityOnce::new(Some(Entity::Column(col)))
+ }
+}
+
+/// An [`Iterator`] which goes goes over columns of a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct ColumnsIter {
+ start: usize,
+ end: usize,
+}
+
+impl ColumnsIter {
+ const fn new(start: usize, end: usize) -> Self {
+ Self { start, end }
+ }
+}
+
+impl Iterator for ColumnsIter {
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.start >= self.end {
+ return None;
+ }
+
+ let col = self.start;
+ self.start += 1;
+
+ Some(Entity::Column(col))
+ }
+}
diff --git a/vendor/tabled/src/settings/object/frame.rs b/vendor/tabled/src/settings/object/frame.rs
new file mode 100644
index 000000000..caeb10640
--- /dev/null
+++ b/vendor/tabled/src/settings/object/frame.rs
@@ -0,0 +1,69 @@
+use crate::{
+ grid::config::Entity,
+ grid::records::{ExactRecords, Records},
+ settings::object::Object,
+};
+
+/// Frame includes cells which are on the edges of each side.
+/// Therefore it's [`Object`] implementation returns a subset of cells which are present in frame.
+#[derive(Debug)]
+pub struct Frame;
+
+impl<I> Object<I> for Frame
+where
+ I: Records + ExactRecords,
+{
+ type Iter = FrameIter;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ FrameIter::new(records.count_rows(), records.count_columns())
+ }
+}
+
+/// An [`Iterator`] which goes goes over all cell on a frame of a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct FrameIter {
+ rows: usize,
+ cols: usize,
+ row: usize,
+ col: usize,
+}
+
+impl FrameIter {
+ const fn new(count_rows: usize, count_columns: usize) -> Self {
+ Self {
+ rows: count_rows,
+ cols: count_columns,
+ row: 0,
+ col: 0,
+ }
+ }
+}
+
+impl Iterator for FrameIter {
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.cols == 0 || self.rows == 0 {
+ return None;
+ }
+
+ if self.row == self.rows {
+ return None;
+ }
+
+ let row = self.row;
+ let col = self.col;
+
+ self.col += 1;
+
+ if self.col == self.cols {
+ self.row += 1;
+ self.col = 0;
+ }
+
+ Some(Entity::Cell(row, col))
+ }
+}
diff --git a/vendor/tabled/src/settings/object/mod.rs b/vendor/tabled/src/settings/object/mod.rs
new file mode 100644
index 000000000..46893c71d
--- /dev/null
+++ b/vendor/tabled/src/settings/object/mod.rs
@@ -0,0 +1,765 @@
+//! This module contains a list of primitives that implement a [`Object`] trait.
+//! They help to locate a necessary segment on a [`Table`].
+//!
+//! [`Table`]: crate::Table
+
+mod cell;
+mod columns;
+mod frame;
+mod rows;
+mod segment;
+pub(crate) mod util;
+
+use std::{collections::HashSet, marker::PhantomData};
+
+use self::segment::SectorCellsIter;
+
+use crate::{
+ grid::config::{Entity, EntityIterator},
+ grid::records::{ExactRecords, Records},
+};
+
+pub use cell::{Cell, EntityOnce};
+pub use columns::{Column, Columns, ColumnsIter, FirstColumn, LastColumn, LastColumnOffset};
+pub use frame::{Frame, FrameIter};
+pub use rows::{FirstRow, LastRow, LastRowOffset, Row, Rows, RowsIter};
+pub use segment::{SectorIter, Segment, SegmentAll};
+
+/// Object helps to locate a necessary part of a [`Table`].
+///
+/// [`Table`]: crate::Table
+pub trait Object<R> {
+ /// An [`Iterator`] which returns a list of cells.
+ type Iter: Iterator<Item = Entity>;
+
+ /// Cells returns a set of coordinates of cells.
+ fn cells(&self, records: &R) -> Self::Iter;
+
+ /// Combines cells.
+ /// It doesn't repeat cells.
+ fn and<O>(self, rhs: O) -> UnionCombination<Self, O, R>
+ where
+ Self: Sized,
+ {
+ UnionCombination::new(self, rhs)
+ }
+
+ /// Excludes rhs cells from this cells.
+ fn not<O>(self, rhs: O) -> DiffCombination<Self, O, R>
+ where
+ Self: Sized,
+ {
+ DiffCombination::new(self, rhs)
+ }
+
+ /// Returns cells which are present in both [`Object`]s only.
+ fn intersect<O>(self, rhs: O) -> IntersectionCombination<Self, O, R>
+ where
+ Self: Sized,
+ {
+ IntersectionCombination::new(self, rhs)
+ }
+
+ /// Returns cells which are not present in target [`Object`].
+ fn inverse(self) -> InversionCombination<Self, R>
+ where
+ Self: Sized,
+ {
+ InversionCombination::new(self)
+ }
+}
+
+/// Combination struct used for chaining [`Object`]'s.
+///
+/// Combines 2 sets of cells into one.
+///
+/// Duplicates are removed from the output set.
+#[derive(Debug)]
+pub struct UnionCombination<L, R, I> {
+ lhs: L,
+ rhs: R,
+ _records: PhantomData<I>,
+}
+
+impl<L, R, I> UnionCombination<L, R, I> {
+ fn new(lhs: L, rhs: R) -> Self {
+ Self {
+ lhs,
+ rhs,
+ _records: PhantomData,
+ }
+ }
+}
+
+impl<I, L, R> Object<I> for UnionCombination<L, R, I>
+where
+ L: Object<I>,
+ R: Object<I>,
+ I: Records + ExactRecords,
+{
+ type Iter = UnionIter<L::Iter, R::Iter>;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let lhs = self.lhs.cells(records);
+ let rhs = self.rhs.cells(records);
+
+ UnionIter::new(lhs, rhs, records.count_rows(), records.count_columns())
+ }
+}
+
+/// Difference struct used for chaining [`Object`]'s.
+///
+/// Returns cells from 1st set with removed ones from the 2nd set.
+#[derive(Debug)]
+pub struct DiffCombination<L, R, I> {
+ lhs: L,
+ rhs: R,
+ _records: PhantomData<I>,
+}
+
+impl<L, R, I> DiffCombination<L, R, I> {
+ fn new(lhs: L, rhs: R) -> Self {
+ Self {
+ lhs,
+ rhs,
+ _records: PhantomData,
+ }
+ }
+}
+
+impl<I, L, R> Object<I> for DiffCombination<L, R, I>
+where
+ L: Object<I>,
+ R: Object<I>,
+ I: Records + ExactRecords,
+{
+ type Iter = DiffIter<L::Iter>;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let lhs = self.lhs.cells(records);
+ let rhs = self.rhs.cells(records);
+
+ DiffIter::new(lhs, rhs, records.count_rows(), records.count_columns())
+ }
+}
+
+/// Intersection struct used for chaining [`Object`]'s.
+///
+/// Returns cells which are present in 2 sets.
+/// But not in one of them
+#[derive(Debug)]
+pub struct IntersectionCombination<L, R, I> {
+ lhs: L,
+ rhs: R,
+ _records: PhantomData<I>,
+}
+
+impl<L, R, I> IntersectionCombination<L, R, I> {
+ fn new(lhs: L, rhs: R) -> Self {
+ Self {
+ lhs,
+ rhs,
+ _records: PhantomData,
+ }
+ }
+}
+
+impl<I, L, R> Object<I> for IntersectionCombination<L, R, I>
+where
+ L: Object<I>,
+ R: Object<I>,
+ I: Records + ExactRecords,
+{
+ type Iter = IntersectIter<L::Iter>;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let lhs = self.lhs.cells(records);
+ let rhs = self.rhs.cells(records);
+
+ IntersectIter::new(lhs, rhs, records.count_rows(), records.count_columns())
+ }
+}
+
+/// Inversion struct used for chaining [`Object`]'s.
+///
+/// Returns cells which are present in 2 sets.
+/// But not in one of them
+#[derive(Debug)]
+pub struct InversionCombination<O, I> {
+ obj: O,
+ _records: PhantomData<I>,
+}
+
+impl<O, I> InversionCombination<O, I> {
+ fn new(obj: O) -> Self {
+ Self {
+ obj,
+ _records: PhantomData,
+ }
+ }
+}
+
+impl<I, O> Object<I> for InversionCombination<O, I>
+where
+ O: Object<I>,
+ I: Records + ExactRecords,
+{
+ type Iter = InversionIter;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let obj = self.obj.cells(records);
+
+ InversionIter::new(obj, records.count_rows(), records.count_columns())
+ }
+}
+
+/// An [`Iterator`] which goes over a combination [`Object::Iter`].
+#[derive(Debug)]
+pub struct UnionIter<L, R> {
+ lhs: Option<L>,
+ rhs: R,
+ seen: HashSet<(usize, usize)>,
+ current: Option<EntityIterator>,
+ count_rows: usize,
+ count_cols: usize,
+}
+
+impl<L, R> UnionIter<L, R>
+where
+ L: Iterator<Item = Entity>,
+ R: Iterator<Item = Entity>,
+{
+ fn new(lhs: L, rhs: R, count_rows: usize, count_cols: usize) -> Self {
+ let size = match lhs.size_hint() {
+ (s1, Some(s2)) if s1 == s2 => s1,
+ _ => 0,
+ };
+
+ Self {
+ lhs: Some(lhs),
+ rhs,
+ seen: HashSet::with_capacity(size),
+ current: None,
+ count_rows,
+ count_cols,
+ }
+ }
+}
+
+impl<L, R> Iterator for UnionIter<L, R>
+where
+ L: Iterator<Item = Entity>,
+ R: Iterator<Item = Entity>,
+{
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if let Some(iter) = self.current.as_mut() {
+ for p in iter.by_ref() {
+ if self.lhs.is_none() && self.seen.contains(&p) {
+ continue;
+ }
+
+ let _ = self.seen.insert(p);
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+
+ if let Some(lhs) = self.lhs.as_mut() {
+ for entity in lhs.by_ref() {
+ let mut iter = entity.iter(self.count_rows, self.count_cols);
+ if let Some(p) = iter.by_ref().next() {
+ let _ = self.seen.insert(p);
+ self.current = Some(iter);
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+
+ self.lhs = None;
+ }
+
+ for entity in self.rhs.by_ref() {
+ let mut iter = entity.iter(self.count_rows, self.count_cols);
+
+ for p in iter.by_ref() {
+ if !self.seen.contains(&p) {
+ let _ = self.seen.insert(p);
+ self.current = Some(iter);
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+ }
+
+ None
+ }
+}
+
+/// An [`Iterator`] which goes over only cells which are present in first [`Object::Iter`] but not second.
+#[derive(Debug)]
+pub struct DiffIter<L> {
+ lhs: L,
+ seen: HashSet<(usize, usize)>,
+ count_rows: usize,
+ count_cols: usize,
+ current: Option<EntityIterator>,
+}
+
+impl<L> DiffIter<L>
+where
+ L: Iterator<Item = Entity>,
+{
+ fn new<R>(lhs: L, rhs: R, count_rows: usize, count_cols: usize) -> Self
+ where
+ R: Iterator<Item = Entity>,
+ {
+ let size = match rhs.size_hint() {
+ (s1, Some(s2)) if s1 == s2 => s1,
+ _ => 0,
+ };
+
+ let mut seen = HashSet::with_capacity(size);
+ for entity in rhs {
+ seen.extend(entity.iter(count_rows, count_cols));
+ }
+
+ Self {
+ lhs,
+ seen,
+ count_rows,
+ count_cols,
+ current: None,
+ }
+ }
+}
+
+impl<L> Iterator for DiffIter<L>
+where
+ L: Iterator<Item = Entity>,
+{
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if let Some(iter) = self.current.as_mut() {
+ for p in iter.by_ref() {
+ if !self.seen.contains(&p) {
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+ }
+
+ for entity in self.lhs.by_ref() {
+ let mut iter = entity.iter(self.count_rows, self.count_cols);
+
+ for p in iter.by_ref() {
+ if !self.seen.contains(&p) {
+ self.current = Some(iter);
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+ }
+
+ None
+ }
+}
+
+/// An [`Iterator`] which goes goes over cells which are present in both [`Object::Iter`]ators.
+#[derive(Debug)]
+pub struct IntersectIter<L> {
+ lhs: L,
+ seen: HashSet<(usize, usize)>,
+ count_rows: usize,
+ count_cols: usize,
+ current: Option<EntityIterator>,
+}
+
+impl<L> IntersectIter<L>
+where
+ L: Iterator<Item = Entity>,
+{
+ fn new<R>(lhs: L, rhs: R, count_rows: usize, count_cols: usize) -> Self
+ where
+ R: Iterator<Item = Entity>,
+ {
+ let size = match rhs.size_hint() {
+ (s1, Some(s2)) if s1 == s2 => s1,
+ _ => 0,
+ };
+
+ let mut seen = HashSet::with_capacity(size);
+ for entity in rhs {
+ seen.extend(entity.iter(count_rows, count_cols));
+ }
+
+ Self {
+ lhs,
+ seen,
+ count_rows,
+ count_cols,
+ current: None,
+ }
+ }
+}
+
+impl<L> Iterator for IntersectIter<L>
+where
+ L: Iterator<Item = Entity>,
+{
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if let Some(iter) = self.current.as_mut() {
+ for p in iter.by_ref() {
+ if self.seen.contains(&p) {
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+ }
+
+ for entity in self.lhs.by_ref() {
+ let mut iter = entity.iter(self.count_rows, self.count_cols);
+
+ for p in iter.by_ref() {
+ if self.seen.contains(&p) {
+ self.current = Some(iter);
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+ }
+
+ None
+ }
+}
+
+/// An [`Iterator`] which goes goes over cells which are not present an [`Object::Iter`]ator.
+#[derive(Debug)]
+pub struct InversionIter {
+ all: SectorCellsIter,
+ seen: HashSet<(usize, usize)>,
+}
+
+impl InversionIter {
+ fn new<O>(obj: O, count_rows: usize, count_columns: usize) -> Self
+ where
+ O: Iterator<Item = Entity>,
+ {
+ let size = match obj.size_hint() {
+ (s1, Some(s2)) if s1 == s2 => s1,
+ _ => 0,
+ };
+
+ let mut seen = HashSet::with_capacity(size);
+ for entity in obj {
+ seen.extend(entity.iter(count_rows, count_columns));
+ }
+
+ let all = SectorCellsIter::new(0, count_rows, 0, count_columns);
+
+ Self { all, seen }
+ }
+}
+
+impl Iterator for InversionIter {
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ for p in self.all.by_ref() {
+ if !self.seen.contains(&p) {
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+
+ None
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::grid::records::vec_records::VecRecords;
+
+ use super::*;
+
+ #[test]
+ fn cell_test() {
+ assert_eq!(vec_cells((0, 0), 2, 3), [Entity::Cell(0, 0)]);
+ assert_eq!(vec_cells((1, 1), 2, 3), [Entity::Cell(1, 1)]);
+ assert_eq!(vec_cells((1, 1), 0, 0), [Entity::Cell(1, 1)]);
+ assert_eq!(vec_cells((1, 100), 2, 3), [Entity::Cell(1, 100)]);
+ assert_eq!(vec_cells((100, 1), 2, 3), [Entity::Cell(100, 1)]);
+ }
+
+ #[test]
+ fn columns_test() {
+ assert_eq!(
+ vec_cells(Columns::new(..), 2, 3),
+ [Entity::Column(0), Entity::Column(1), Entity::Column(2)]
+ );
+ assert_eq!(
+ vec_cells(Columns::new(1..), 2, 3),
+ [Entity::Column(1), Entity::Column(2)]
+ );
+ assert_eq!(vec_cells(Columns::new(2..), 2, 3), [Entity::Column(2)]);
+ assert_eq!(vec_cells(Columns::new(3..), 2, 3), []);
+ assert_eq!(vec_cells(Columns::new(3..), 0, 0), []);
+ assert_eq!(vec_cells(Columns::new(0..1), 2, 3), [Entity::Column(0)]);
+ assert_eq!(vec_cells(Columns::new(1..2), 2, 3), [Entity::Column(1)]);
+ assert_eq!(vec_cells(Columns::new(2..3), 2, 3), [Entity::Column(2)]);
+ assert_eq!(vec_cells(Columns::new(..), 0, 0), []);
+ assert_eq!(vec_cells(Columns::new(..), 2, 0), []);
+ assert_eq!(vec_cells(Columns::new(..), 0, 3), []);
+ }
+
+ #[test]
+ fn first_column_test() {
+ assert_eq!(vec_cells(Columns::first(), 5, 2), [Entity::Column(0)]);
+ assert_eq!(vec_cells(Columns::first(), 0, 0), []);
+ assert_eq!(vec_cells(Columns::first(), 10, 0), []);
+ assert_eq!(vec_cells(Columns::first(), 0, 10), []);
+ }
+
+ #[test]
+ fn last_column_test() {
+ assert_eq!(vec_cells(Columns::last(), 5, 2), [Entity::Column(1)]);
+ assert_eq!(vec_cells(Columns::last(), 5, 29), [Entity::Column(28)]);
+ assert_eq!(vec_cells(Columns::last(), 0, 0), []);
+ assert_eq!(vec_cells(Columns::last(), 10, 0), []);
+ assert_eq!(vec_cells(Columns::last(), 0, 10), []);
+ }
+
+ #[test]
+ fn last_column_sub_test() {
+ assert_eq!(vec_cells(Columns::last(), 5, 2), [Entity::Column(1)]);
+ assert_eq!(vec_cells(Columns::last() - 0, 5, 2), [Entity::Column(1)]);
+ assert_eq!(vec_cells(Columns::last() - 1, 5, 2), [Entity::Column(0)]);
+ assert_eq!(vec_cells(Columns::last() - 2, 5, 2), []);
+ assert_eq!(vec_cells(Columns::last() - 100, 5, 2), []);
+ }
+
+ #[test]
+ fn first_column_add_test() {
+ assert_eq!(vec_cells(Columns::first(), 5, 2), [Entity::Column(0)]);
+ assert_eq!(vec_cells(Columns::first() + 0, 5, 2), [Entity::Column(0)]);
+ assert_eq!(vec_cells(Columns::first() + 1, 5, 2), [Entity::Column(1)]);
+ assert_eq!(vec_cells(Columns::first() + 2, 5, 2), [Entity::Column(2)]);
+ assert_eq!(
+ vec_cells(Columns::first() + 100, 5, 2),
+ [Entity::Column(100)]
+ );
+ }
+
+ #[test]
+ fn rows_test() {
+ assert_eq!(
+ vec_cells(Rows::new(..), 2, 3),
+ [Entity::Row(0), Entity::Row(1)]
+ );
+ assert_eq!(vec_cells(Rows::new(1..), 2, 3), [Entity::Row(1)]);
+ assert_eq!(vec_cells(Rows::new(2..), 2, 3), []);
+ assert_eq!(vec_cells(Rows::new(2..), 0, 0), []);
+ assert_eq!(vec_cells(Rows::new(0..1), 2, 3), [Entity::Row(0)],);
+ assert_eq!(vec_cells(Rows::new(1..2), 2, 3), [Entity::Row(1)],);
+ assert_eq!(vec_cells(Rows::new(..), 0, 0), []);
+ assert_eq!(vec_cells(Rows::new(..), 0, 3), []);
+ assert_eq!(
+ vec_cells(Rows::new(..), 2, 0),
+ [Entity::Row(0), Entity::Row(1)]
+ );
+ }
+
+ #[test]
+ fn last_row_test() {
+ assert_eq!(vec_cells(Rows::last(), 5, 2), [Entity::Row(4)]);
+ assert_eq!(vec_cells(Rows::last(), 100, 2), [Entity::Row(99)]);
+ assert_eq!(vec_cells(Rows::last(), 0, 0), []);
+ assert_eq!(vec_cells(Rows::last(), 5, 0), []);
+ assert_eq!(vec_cells(Rows::last(), 0, 2), []);
+ }
+
+ #[test]
+ fn first_row_test() {
+ assert_eq!(vec_cells(Rows::first(), 5, 2), [Entity::Row(0)]);
+ assert_eq!(vec_cells(Rows::first(), 100, 2), [Entity::Row(0)]);
+ assert_eq!(vec_cells(Rows::first(), 0, 0), []);
+ assert_eq!(vec_cells(Rows::first(), 5, 0), []);
+ assert_eq!(vec_cells(Rows::first(), 0, 2), []);
+ }
+
+ #[test]
+ fn last_row_sub_test() {
+ assert_eq!(vec_cells(Rows::last(), 5, 2), [Entity::Row(4)]);
+ assert_eq!(vec_cells(Rows::last() - 0, 5, 2), [Entity::Row(4)]);
+ assert_eq!(vec_cells(Rows::last() - 1, 5, 2), [Entity::Row(3)]);
+ assert_eq!(vec_cells(Rows::last() - 2, 5, 2), [Entity::Row(2)]);
+ assert_eq!(vec_cells(Rows::last() - 3, 5, 2), [Entity::Row(1)]);
+ assert_eq!(vec_cells(Rows::last() - 4, 5, 2), [Entity::Row(0)]);
+ assert_eq!(vec_cells(Rows::last() - 5, 5, 2), []);
+ assert_eq!(vec_cells(Rows::last() - 100, 5, 2), []);
+ assert_eq!(vec_cells(Rows::last() - 1, 0, 0), []);
+ assert_eq!(vec_cells(Rows::last() - 1, 5, 0), []);
+ assert_eq!(vec_cells(Rows::last() - 1, 0, 2), []);
+ }
+
+ #[test]
+ fn first_row_add_test() {
+ assert_eq!(vec_cells(Rows::first(), 5, 2), [Entity::Row(0)]);
+ assert_eq!(vec_cells(Rows::first() + 0, 5, 2), [Entity::Row(0)]);
+ assert_eq!(vec_cells(Rows::first() + 1, 5, 2), [Entity::Row(1)]);
+ assert_eq!(vec_cells(Rows::first() + 2, 5, 2), [Entity::Row(2)]);
+ assert_eq!(vec_cells(Rows::first() + 3, 5, 2), [Entity::Row(3)]);
+ assert_eq!(vec_cells(Rows::first() + 4, 5, 2), [Entity::Row(4)]);
+ assert_eq!(vec_cells(Rows::first() + 5, 5, 2), [Entity::Row(5)]);
+ assert_eq!(vec_cells(Rows::first() + 100, 5, 2), [Entity::Row(100)]);
+ assert_eq!(vec_cells(Rows::first() + 1, 0, 0), [Entity::Row(1)]);
+ assert_eq!(vec_cells(Rows::first() + 1, 5, 0), [Entity::Row(1)]);
+ assert_eq!(vec_cells(Rows::first() + 1, 0, 2), [Entity::Row(1)]);
+ }
+
+ #[test]
+ fn frame_test() {
+ assert_eq!(
+ vec_cells(Frame, 2, 3),
+ [
+ Entity::Cell(0, 0),
+ Entity::Cell(0, 1),
+ Entity::Cell(0, 2),
+ Entity::Cell(1, 0),
+ Entity::Cell(1, 1),
+ Entity::Cell(1, 2)
+ ]
+ );
+ assert_eq!(vec_cells(Frame, 0, 0), []);
+ assert_eq!(vec_cells(Frame, 2, 0), []);
+ assert_eq!(vec_cells(Frame, 0, 2), []);
+ }
+
+ #[test]
+ fn segment_test() {
+ assert_eq!(
+ vec_cells(Segment::new(.., ..), 2, 3),
+ [
+ Entity::Cell(0, 0),
+ Entity::Cell(0, 1),
+ Entity::Cell(0, 2),
+ Entity::Cell(1, 0),
+ Entity::Cell(1, 1),
+ Entity::Cell(1, 2)
+ ]
+ );
+ assert_eq!(
+ vec_cells(Segment::new(1.., ..), 2, 3),
+ [Entity::Cell(1, 0), Entity::Cell(1, 1), Entity::Cell(1, 2)]
+ );
+ assert_eq!(vec_cells(Segment::new(2.., ..), 2, 3), []);
+
+ assert_eq!(
+ vec_cells(Segment::new(.., 1..), 2, 3),
+ [
+ Entity::Cell(0, 1),
+ Entity::Cell(0, 2),
+ Entity::Cell(1, 1),
+ Entity::Cell(1, 2)
+ ]
+ );
+ assert_eq!(
+ vec_cells(Segment::new(.., 2..), 2, 3),
+ [Entity::Cell(0, 2), Entity::Cell(1, 2)]
+ );
+ assert_eq!(vec_cells(Segment::new(.., 3..), 2, 3), []);
+
+ assert_eq!(
+ vec_cells(Segment::new(1.., 1..), 2, 3),
+ [Entity::Cell(1, 1), Entity::Cell(1, 2)]
+ );
+ assert_eq!(
+ vec_cells(Segment::new(1..2, 1..2), 2, 3),
+ [Entity::Cell(1, 1)]
+ );
+
+ assert_eq!(vec_cells(Segment::new(5.., 5..), 2, 3), []);
+ }
+
+ #[test]
+ fn object_and_test() {
+ assert_eq!(
+ vec_cells(Cell::new(0, 0).and(Cell::new(0, 0)), 2, 3),
+ [Entity::Cell(0, 0)]
+ );
+ assert_eq!(
+ vec_cells(Cell::new(0, 0).and(Cell::new(1, 2)), 2, 3),
+ [Entity::Cell(0, 0), Entity::Cell(1, 2)]
+ );
+ assert_eq!(vec_cells(Cell::new(0, 0).and(Cell::new(1, 2)), 0, 0), []);
+ }
+
+ #[test]
+ fn object_not_test() {
+ assert_eq!(vec_cells(Rows::first().not(Cell::new(0, 0)), 0, 0), []);
+ assert_eq!(vec_cells(Cell::new(0, 0).not(Cell::new(0, 0)), 2, 3), []);
+ assert_eq!(
+ vec_cells(Rows::first().not(Cell::new(0, 0)), 2, 3),
+ [Entity::Cell(0, 1), Entity::Cell(0, 2)]
+ );
+ assert_eq!(
+ vec_cells(Columns::single(1).not(Rows::single(1)), 3, 3),
+ [Entity::Cell(0, 1), Entity::Cell(2, 1)]
+ );
+ assert_eq!(
+ vec_cells(Rows::single(1).not(Columns::single(1)), 3, 3),
+ [Entity::Cell(1, 0), Entity::Cell(1, 2)]
+ );
+ }
+
+ #[test]
+ fn object_intersect_test() {
+ assert_eq!(
+ vec_cells(Rows::first().intersect(Cell::new(0, 0)), 0, 0),
+ []
+ );
+ assert_eq!(
+ vec_cells(Segment::all().intersect(Rows::single(1)), 2, 3),
+ [Entity::Cell(1, 0), Entity::Cell(1, 1), Entity::Cell(1, 2)]
+ );
+ assert_eq!(
+ vec_cells(Cell::new(0, 0).intersect(Cell::new(0, 0)), 2, 3),
+ [Entity::Cell(0, 0)]
+ );
+ assert_eq!(
+ vec_cells(Rows::first().intersect(Cell::new(0, 0)), 2, 3),
+ [Entity::Cell(0, 0)]
+ );
+ // maybe we somehow shall not limit the rows/columns by the max count?
+ assert_eq!(
+ vec_cells(Rows::single(1).intersect(Columns::single(1)), 2, 1),
+ []
+ );
+ }
+
+ #[test]
+ fn object_inverse_test() {
+ assert_eq!(vec_cells(Segment::all().inverse(), 2, 3), []);
+ assert_eq!(
+ vec_cells(Cell::new(0, 0).inverse(), 2, 3),
+ [
+ Entity::Cell(0, 1),
+ Entity::Cell(0, 2),
+ Entity::Cell(1, 0),
+ Entity::Cell(1, 1),
+ Entity::Cell(1, 2)
+ ]
+ );
+ assert_eq!(
+ vec_cells(Rows::first().inverse(), 2, 3),
+ [Entity::Cell(1, 0), Entity::Cell(1, 1), Entity::Cell(1, 2)]
+ );
+ assert_eq!(vec_cells(Rows::first().inverse(), 0, 0), []);
+ }
+
+ fn vec_cells<O: Object<VecRecords<String>>>(
+ o: O,
+ count_rows: usize,
+ count_cols: usize,
+ ) -> Vec<Entity> {
+ let data = vec![vec![String::default(); count_cols]; count_rows];
+ let records = VecRecords::new(data);
+ o.cells(&records).collect::<Vec<_>>()
+ }
+}
diff --git a/vendor/tabled/src/settings/object/rows.rs b/vendor/tabled/src/settings/object/rows.rs
new file mode 100644
index 000000000..b32e79ad5
--- /dev/null
+++ b/vendor/tabled/src/settings/object/rows.rs
@@ -0,0 +1,213 @@
+use std::ops::{Add, RangeBounds, Sub};
+
+use crate::{
+ grid::config::Entity,
+ grid::records::{ExactRecords, Records},
+ settings::object::{cell::EntityOnce, Object},
+};
+
+use super::util::bounds_to_usize;
+
+/// Row denotes a set of cells on given rows on a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Rows<R> {
+ range: R,
+}
+
+impl<R> Rows<R> {
+ /// Returns a new instance of [`Rows`] for a range of rows.
+ ///
+ /// If the boundaries are exceeded it may panic.
+ pub fn new(range: R) -> Self
+ where
+ R: RangeBounds<usize>,
+ {
+ Self { range }
+ }
+
+ pub(crate) const fn get_range(&self) -> &R {
+ &self.range
+ }
+}
+
+impl Rows<()> {
+ /// Returns a new instance of [`Rows`] with a single row.
+ ///
+ /// If the boundaries are exceeded it may panic.
+ pub const fn single(index: usize) -> Row {
+ Row { index }
+ }
+
+ /// Returns a first row [`Object`].
+ ///
+ /// If the table has 0 rows returns an empty set of cells.
+ pub const fn first() -> FirstRow {
+ FirstRow
+ }
+
+ /// Returns a last row [`Object`].
+ ///
+ /// If the table has 0 rows returns an empty set of cells.
+ pub const fn last() -> LastRow {
+ LastRow
+ }
+}
+
+impl<I, R> Object<I> for Rows<R>
+where
+ R: RangeBounds<usize>,
+ I: ExactRecords,
+{
+ type Iter = RowsIter;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let start = self.range.start_bound();
+ let end = self.range.end_bound();
+ let max = records.count_rows();
+ let (x, y) = bounds_to_usize(start, end, max);
+
+ RowsIter::new(x, y)
+ }
+}
+
+/// A row which is located by an offset from the first row.
+#[derive(Debug, Clone, Copy)]
+pub struct Row {
+ index: usize,
+}
+
+impl<I> Object<I> for Row {
+ type Iter = EntityOnce;
+
+ fn cells(&self, _: &I) -> Self::Iter {
+ EntityOnce::new(Some(Entity::Row(self.index)))
+ }
+}
+
+impl From<Row> for usize {
+ fn from(val: Row) -> Self {
+ val.index
+ }
+}
+
+/// This structure represents the first row of a [`Table`].
+/// It's often contains headers data.
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct FirstRow;
+
+impl<I> Object<I> for FirstRow
+where
+ I: Records + ExactRecords,
+{
+ type Iter = EntityOnce;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ if records.count_columns() == 0 || records.count_rows() == 0 {
+ return EntityOnce::new(None);
+ }
+
+ EntityOnce::new(Some(Entity::Row(0)))
+ }
+}
+
+impl Add<usize> for FirstRow {
+ type Output = Row;
+
+ fn add(self, rhs: usize) -> Self::Output {
+ Row { index: rhs }
+ }
+}
+
+/// This structure represents the last row of a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct LastRow;
+
+impl<I> Object<I> for LastRow
+where
+ I: Records + ExactRecords,
+{
+ type Iter = EntityOnce;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let count_rows = records.count_rows();
+ if records.count_columns() == 0 || count_rows == 0 {
+ return EntityOnce::new(None);
+ }
+
+ let row = if count_rows == 0 { 0 } else { count_rows - 1 };
+
+ EntityOnce::new(Some(Entity::Row(row)))
+ }
+}
+
+impl Sub<usize> for LastRow {
+ type Output = LastRowOffset;
+
+ fn sub(self, rhs: usize) -> Self::Output {
+ LastRowOffset { offset: rhs }
+ }
+}
+
+/// A row which is located by an offset from the last row.
+#[derive(Debug)]
+pub struct LastRowOffset {
+ offset: usize,
+}
+
+impl<I> Object<I> for LastRowOffset
+where
+ I: Records + ExactRecords,
+{
+ type Iter = EntityOnce;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let count_rows = records.count_rows();
+ if records.count_columns() == 0 || count_rows == 0 {
+ return EntityOnce::new(None);
+ }
+
+ let row = if count_rows == 0 { 0 } else { count_rows - 1 };
+ if self.offset > row {
+ return EntityOnce::new(None);
+ }
+
+ let row = row - self.offset;
+ EntityOnce::new(Some(Entity::Row(row)))
+ }
+}
+
+/// An [`Iterator`] which goes goes over all rows of a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct RowsIter {
+ start: usize,
+ end: usize,
+}
+
+impl RowsIter {
+ const fn new(start: usize, end: usize) -> Self {
+ Self { start, end }
+ }
+}
+
+impl Iterator for RowsIter {
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.start >= self.end {
+ return None;
+ }
+
+ let col = self.start;
+ self.start += 1;
+
+ Some(Entity::Row(col))
+ }
+}
diff --git a/vendor/tabled/src/settings/object/segment.rs b/vendor/tabled/src/settings/object/segment.rs
new file mode 100644
index 000000000..9b59ada47
--- /dev/null
+++ b/vendor/tabled/src/settings/object/segment.rs
@@ -0,0 +1,151 @@
+use std::ops::{RangeBounds, RangeFull};
+
+use crate::{
+ grid::config::Entity,
+ grid::records::{ExactRecords, Records},
+ settings::object::{cell::EntityOnce, Object},
+};
+
+use super::util::bounds_to_usize;
+
+/// This structure represents a sub table of [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Segment<C, R> {
+ columns: C,
+ rows: R,
+}
+
+impl Segment<RangeFull, RangeFull> {
+ /// Returns a table segment on which are present all cells.
+ pub fn all() -> SegmentAll {
+ SegmentAll
+ }
+}
+
+impl<C, R> Segment<C, R>
+where
+ C: RangeBounds<usize>,
+ R: RangeBounds<usize>,
+{
+ /// This function builds a [`Segment`].
+ pub fn new(rows: R, columns: C) -> Self {
+ Self { columns, rows }
+ }
+}
+
+impl<I, C, R> Object<I> for Segment<C, R>
+where
+ C: RangeBounds<usize>,
+ R: RangeBounds<usize>,
+ I: Records + ExactRecords,
+{
+ type Iter = SectorIter;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let start = self.rows.start_bound();
+ let end = self.rows.end_bound();
+ let max = records.count_rows();
+ let (rows_start, rows_end) = bounds_to_usize(start, end, max);
+
+ let start = self.columns.start_bound();
+ let end = self.columns.end_bound();
+ let max = records.count_columns();
+ let (cols_start, cols_end) = bounds_to_usize(start, end, max);
+
+ SectorIter::new(rows_start, rows_end, cols_start, cols_end)
+ }
+}
+
+/// This is a segment which contains all cells on the table.
+///
+/// Can be created from [`Segment::all`].
+#[derive(Debug)]
+pub struct SegmentAll;
+
+impl<I> Object<I> for SegmentAll {
+ type Iter = EntityOnce;
+
+ fn cells(&self, _: &I) -> Self::Iter {
+ EntityOnce::new(Some(Entity::Global))
+ }
+}
+
+/// An [`Iterator`] which goes goes over all cell in a sector in a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct SectorIter {
+ iter: SectorCellsIter,
+}
+
+impl SectorIter {
+ const fn new(rows_start: usize, rows_end: usize, cols_start: usize, cols_end: usize) -> Self {
+ Self {
+ iter: SectorCellsIter::new(rows_start, rows_end, cols_start, cols_end),
+ }
+ }
+}
+
+impl Iterator for SectorIter {
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let (row, col) = self.iter.next()?;
+ Some(Entity::Cell(row, col))
+ }
+}
+
+#[derive(Debug)]
+pub(crate) struct SectorCellsIter {
+ rows_end: usize,
+ cols_start: usize,
+ cols_end: usize,
+ row: usize,
+ col: usize,
+}
+
+impl SectorCellsIter {
+ /// Create an iterator from 1st row to last from 1st col to last.
+ pub(crate) const fn new(
+ rows_start: usize,
+ rows_end: usize,
+ cols_start: usize,
+ cols_end: usize,
+ ) -> Self {
+ Self {
+ rows_end,
+ cols_start,
+ cols_end,
+ row: rows_start,
+ col: cols_start,
+ }
+ }
+}
+
+impl Iterator for SectorCellsIter {
+ type Item = (usize, usize);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.row >= self.rows_end {
+ return None;
+ }
+
+ if self.col >= self.cols_end {
+ return None;
+ }
+
+ let row = self.row;
+ let col = self.col;
+
+ self.col += 1;
+
+ if self.col == self.cols_end {
+ self.row += 1;
+ self.col = self.cols_start;
+ }
+
+ Some((row, col))
+ }
+}
diff --git a/vendor/tabled/src/settings/object/util.rs b/vendor/tabled/src/settings/object/util.rs
new file mode 100644
index 000000000..94ca20e86
--- /dev/null
+++ b/vendor/tabled/src/settings/object/util.rs
@@ -0,0 +1,22 @@
+use std::ops::Bound;
+
+/// Converts a range bound to its indexes.
+pub(super) fn bounds_to_usize(
+ left: Bound<&usize>,
+ right: Bound<&usize>,
+ count_elements: usize,
+) -> (usize, usize) {
+ match (left, right) {
+ (Bound::Included(x), Bound::Included(y)) => (*x, y + 1),
+ (Bound::Included(x), Bound::Excluded(y)) => (*x, *y),
+ (Bound::Included(x), Bound::Unbounded) => (*x, count_elements),
+ (Bound::Unbounded, Bound::Unbounded) => (0, count_elements),
+ (Bound::Unbounded, Bound::Included(y)) => (0, y + 1),
+ (Bound::Unbounded, Bound::Excluded(y)) => (0, *y),
+ (Bound::Excluded(_), Bound::Unbounded)
+ | (Bound::Excluded(_), Bound::Included(_))
+ | (Bound::Excluded(_), Bound::Excluded(_)) => {
+ unreachable!("A start bound can't be excluded")
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/padding/mod.rs b/vendor/tabled/src/settings/padding/mod.rs
new file mode 100644
index 000000000..af343b3cb
--- /dev/null
+++ b/vendor/tabled/src/settings/padding/mod.rs
@@ -0,0 +1,164 @@
+//! This module contains a [`Padding`] setting of a cell on a [`Table`].
+//!
+//! # Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::{Padding, Style, Modify, object::Cell}};
+//!
+//! let table = Table::new("2022".chars())
+//! .with(Style::modern())
+//! .with(Modify::new((2, 0)).with(Padding::new(1, 1, 2, 2)))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "┌──────┐\n",
+//! "│ char │\n",
+//! "├──────┤\n",
+//! "│ 2 │\n",
+//! "├──────┤\n",
+//! "│ │\n",
+//! "│ │\n",
+//! "│ 0 │\n",
+//! "│ │\n",
+//! "│ │\n",
+//! "├──────┤\n",
+//! "│ 2 │\n",
+//! "├──────┤\n",
+//! "│ 2 │\n",
+//! "└──────┘",
+//! ),
+//! );
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use crate::{
+ grid::{
+ color::StaticColor,
+ config::{CompactConfig, CompactMultilineConfig},
+ config::{Indent, Sides},
+ },
+ settings::TableOption,
+};
+
+#[cfg(feature = "std")]
+use crate::grid::{color::AnsiColor, config::ColoredConfig, config::Entity};
+#[cfg(feature = "std")]
+use crate::settings::CellOption;
+
+/// Padding is responsible for a left/right/top/bottom inner indent of a particular cell.
+///
+#[cfg_attr(feature = "std", doc = "```")]
+#[cfg_attr(not(feature = "std"), doc = "```ignore")]
+/// # use tabled::{settings::{Style, Padding, object::Rows, Modify}, Table};
+/// # let data: Vec<&'static str> = Vec::new();
+/// let table = Table::new(&data).with(Modify::new(Rows::single(0)).with(Padding::new(0, 0, 1, 1).fill('>', '<', '^', 'V')));
+/// ```
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Padding<C = StaticColor> {
+ indent: Sides<Indent>,
+ colors: Option<Sides<C>>,
+}
+
+impl Padding {
+ /// Construct's an Padding object.
+ ///
+ /// It uses space(' ') as a default fill character.
+ /// To set a custom character you can use [`Padding::fill`] function.
+ pub const fn new(left: usize, right: usize, top: usize, bottom: usize) -> Self {
+ Self {
+ indent: Sides::new(
+ Indent::spaced(left),
+ Indent::spaced(right),
+ Indent::spaced(top),
+ Indent::spaced(bottom),
+ ),
+ colors: None,
+ }
+ }
+
+ /// Construct's an Padding object with all sides set to 0.
+ ///
+ /// It uses space(' ') as a default fill character.
+ /// To set a custom character you can use [`Padding::fill`] function.
+ pub const fn zero() -> Self {
+ Self::new(0, 0, 0, 0)
+ }
+}
+
+impl<Color> Padding<Color> {
+ /// The function, sets a characters for the padding on an each side.
+ pub const fn fill(mut self, left: char, right: char, top: char, bottom: char) -> Self {
+ self.indent.left.fill = left;
+ self.indent.right.fill = right;
+ self.indent.top.fill = top;
+ self.indent.bottom.fill = bottom;
+ self
+ }
+
+ /// The function, sets a characters for the padding on an each side.
+ pub fn colorize<C>(self, left: C, right: C, top: C, bottom: C) -> Padding<C> {
+ Padding {
+ indent: self.indent,
+ colors: Some(Sides::new(left, right, top, bottom)),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, C> CellOption<R, ColoredConfig> for Padding<C>
+where
+ C: Into<AnsiColor<'static>> + Clone,
+{
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let indent = self.indent;
+ let pad = Sides::new(indent.left, indent.right, indent.top, indent.bottom);
+ cfg.set_padding(entity, pad);
+
+ if let Some(colors) = &self.colors {
+ let pad = Sides::new(
+ Some(colors.left.clone().into()),
+ Some(colors.right.clone().into()),
+ Some(colors.top.clone().into()),
+ Some(colors.bottom.clone().into()),
+ );
+ cfg.set_padding_color(entity, pad);
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, D, C> TableOption<R, D, ColoredConfig> for Padding<C>
+where
+ C: Into<AnsiColor<'static>> + Clone,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ <Self as CellOption<R, ColoredConfig>>::change(self, records, cfg, Entity::Global)
+ }
+}
+
+impl<R, D, C> TableOption<R, D, CompactConfig> for Padding<C>
+where
+ C: Into<StaticColor> + Clone,
+{
+ fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
+ *cfg = cfg.set_padding(self.indent);
+
+ if let Some(c) = self.colors {
+ let colors = Sides::new(c.left.into(), c.right.into(), c.top.into(), c.bottom.into());
+ *cfg = cfg.set_padding_color(colors);
+ }
+ }
+}
+
+impl<R, D, C> TableOption<R, D, CompactMultilineConfig> for Padding<C>
+where
+ C: Into<StaticColor> + Clone,
+{
+ fn change(self, records: &mut R, cfg: &mut CompactMultilineConfig, dimension: &mut D) {
+ self.change(records, cfg.as_mut(), dimension)
+ }
+}
diff --git a/vendor/tabled/src/settings/panel/footer.rs b/vendor/tabled/src/settings/panel/footer.rs
new file mode 100644
index 000000000..8d16481cf
--- /dev/null
+++ b/vendor/tabled/src/settings/panel/footer.rs
@@ -0,0 +1,32 @@
+use crate::{
+ grid::config::ColoredConfig,
+ grid::records::{ExactRecords, Records, RecordsMut, Resizable},
+ settings::TableOption,
+};
+
+use super::Panel;
+
+/// Footer renders a [`Panel`] at the bottom.
+/// See [`Panel`].
+#[derive(Debug)]
+pub struct Footer<S>(S);
+
+impl<S> Footer<S> {
+ /// Creates a new object.
+ pub fn new(text: S) -> Self
+ where
+ S: AsRef<str>,
+ {
+ Self(text)
+ }
+}
+
+impl<S, R, D> TableOption<R, D, ColoredConfig> for Footer<S>
+where
+ S: AsRef<str>,
+ R: Records + ExactRecords + Resizable + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dimension: &mut D) {
+ Panel::horizontal(records.count_rows(), self.0.as_ref()).change(records, cfg, dimension);
+ }
+}
diff --git a/vendor/tabled/src/settings/panel/header.rs b/vendor/tabled/src/settings/panel/header.rs
new file mode 100644
index 000000000..e9d398cb5
--- /dev/null
+++ b/vendor/tabled/src/settings/panel/header.rs
@@ -0,0 +1,32 @@
+use crate::{
+ grid::config::ColoredConfig,
+ grid::records::{ExactRecords, Records, RecordsMut, Resizable},
+ settings::TableOption,
+};
+
+use super::Panel;
+
+/// Header inserts a [`Panel`] at the top.
+/// See [`Panel`].
+#[derive(Debug)]
+pub struct Header<S>(S);
+
+impl<S> Header<S> {
+ /// Creates a new object.
+ pub fn new(text: S) -> Self
+ where
+ S: AsRef<str>,
+ {
+ Self(text)
+ }
+}
+
+impl<S, R, D> TableOption<R, D, ColoredConfig> for Header<S>
+where
+ S: AsRef<str>,
+ R: Records + ExactRecords + Resizable + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dimension: &mut D) {
+ Panel::horizontal(0, self.0.as_ref()).change(records, cfg, dimension);
+ }
+}
diff --git a/vendor/tabled/src/settings/panel/horizontal_panel.rs b/vendor/tabled/src/settings/panel/horizontal_panel.rs
new file mode 100644
index 000000000..d5871d720
--- /dev/null
+++ b/vendor/tabled/src/settings/panel/horizontal_panel.rs
@@ -0,0 +1,80 @@
+use crate::{
+ grid::config::{ColoredConfig, SpannedConfig},
+ grid::records::{ExactRecords, Records, RecordsMut, Resizable},
+ settings::TableOption,
+};
+
+/// A horizontal/column span from 0 to a count rows.
+#[derive(Debug)]
+pub struct HorizontalPanel<S> {
+ text: S,
+ row: usize,
+}
+
+impl<S> HorizontalPanel<S> {
+ /// Creates a new horizontal panel.
+ pub fn new(row: usize, text: S) -> Self {
+ Self { row, text }
+ }
+}
+
+impl<S, R, D> TableOption<R, D, ColoredConfig> for HorizontalPanel<S>
+where
+ S: AsRef<str>,
+ R: Records + ExactRecords + Resizable + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ if self.row > count_rows {
+ return;
+ }
+
+ let is_intersect_vertical_span = (0..records.count_columns())
+ .any(|col| cfg.is_cell_covered_by_row_span((self.row, col)));
+ if is_intersect_vertical_span {
+ return;
+ }
+
+ move_rows_aside(records, self.row);
+ move_row_spans(cfg, self.row);
+
+ let text = self.text.as_ref().to_owned();
+ records.set((self.row, 0), text);
+
+ cfg.set_column_span((self.row, 0), count_cols);
+ }
+}
+
+fn move_rows_aside<R: ExactRecords + Resizable>(records: &mut R, row: usize) {
+ records.push_row();
+
+ let count_rows = records.count_rows();
+
+ let shift_count = count_rows - row;
+ for i in 1..shift_count {
+ let row = count_rows - i;
+ records.swap_row(row, row - 1);
+ }
+}
+
+fn move_row_spans(cfg: &mut SpannedConfig, target_row: usize) {
+ for ((row, col), span) in cfg.get_column_spans() {
+ if row < target_row {
+ continue;
+ }
+
+ cfg.set_column_span((row, col), 1);
+ cfg.set_column_span((row + 1, col), span);
+ }
+
+ for ((row, col), span) in cfg.get_row_spans() {
+ if row < target_row {
+ continue;
+ }
+
+ cfg.set_row_span((row, col), 1);
+ cfg.set_row_span((row + 1, col), span);
+ }
+}
diff --git a/vendor/tabled/src/settings/panel/mod.rs b/vendor/tabled/src/settings/panel/mod.rs
new file mode 100644
index 000000000..e4e819b6c
--- /dev/null
+++ b/vendor/tabled/src/settings/panel/mod.rs
@@ -0,0 +1,127 @@
+//! This module contains primitives to create a spread row.
+//! Ultimately it is a cell with a span set to a number of columns on the [`Table`].
+//!
+//! You can use a [`Span`] to set a custom span.
+//!
+//! # Example
+//!
+//! ```
+//! use tabled::{Table, settings::Panel};
+//!
+//! let data = [[1, 2, 3], [4, 5, 6]];
+//!
+//! let table = Table::new(data)
+//! .with(Panel::vertical(1, "S\np\nl\ni\nt"))
+//! .with(Panel::header("Numbers"))
+//! .to_string();
+//!
+//! println!("{}", table);
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "+---+---+---+---+\n",
+//! "| Numbers |\n",
+//! "+---+---+---+---+\n",
+//! "| 0 | S | 1 | 2 |\n",
+//! "+---+ p +---+---+\n",
+//! "| 1 | l | 2 | 3 |\n",
+//! "+---+ i +---+---+\n",
+//! "| 4 | t | 5 | 6 |\n",
+//! "+---+---+---+---+",
+//! )
+//! )
+//! ```
+//!
+//! [`Table`]: crate::Table
+//! [`Span`]: crate::settings::span::Span
+
+mod footer;
+mod header;
+mod horizontal_panel;
+mod vertical_panel;
+
+pub use footer::Footer;
+pub use header::Header;
+pub use horizontal_panel::HorizontalPanel;
+pub use vertical_panel::VerticalPanel;
+
+/// Panel allows to add a Row which has 1 continues Cell to a [`Table`].
+///
+/// See `examples/panel.rs`.
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Panel;
+
+impl Panel {
+ /// Creates an empty vertical row at given index.
+ ///
+ /// ```
+ /// use tabled::{settings::Panel, Table};
+ ///
+ /// let data = [[1, 2, 3], [4, 5, 6]];
+ ///
+ /// let table = Table::new(data)
+ /// .with(Panel::vertical(1, "Tabled Releases"))
+ /// .to_string();
+ ///
+ /// println!("{}", table);
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "+---+-----------------+---+---+\n",
+ /// "| 0 | Tabled Releases | 1 | 2 |\n",
+ /// "+---+ +---+---+\n",
+ /// "| 1 | | 2 | 3 |\n",
+ /// "+---+ +---+---+\n",
+ /// "| 4 | | 5 | 6 |\n",
+ /// "+---+-----------------+---+---+",
+ /// )
+ /// )
+ /// ```
+ pub fn vertical<S: AsRef<str>>(column: usize, text: S) -> VerticalPanel<S> {
+ VerticalPanel::new(column, text)
+ }
+
+ /// Creates an empty horizontal row at given index.
+ ///
+ /// ```
+ /// use tabled::{Table, settings::Panel};
+ ///
+ /// let data = [[1, 2, 3], [4, 5, 6]];
+ ///
+ /// let table = Table::new(data)
+ /// .with(Panel::vertical(1, ""))
+ /// .to_string();
+ ///
+ /// println!("{}", table);
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "+---+--+---+---+\n",
+ /// "| 0 | | 1 | 2 |\n",
+ /// "+---+ +---+---+\n",
+ /// "| 1 | | 2 | 3 |\n",
+ /// "+---+ +---+---+\n",
+ /// "| 4 | | 5 | 6 |\n",
+ /// "+---+--+---+---+",
+ /// )
+ /// )
+ /// ```
+ pub fn horizontal<S: AsRef<str>>(row: usize, text: S) -> HorizontalPanel<S> {
+ HorizontalPanel::new(row, text)
+ }
+
+ /// Creates an horizontal row at first row.
+ pub fn header<S: AsRef<str>>(text: S) -> Header<S> {
+ Header::new(text)
+ }
+
+ /// Creates an horizontal row at last row.
+ pub fn footer<S: AsRef<str>>(text: S) -> Footer<S> {
+ Footer::new(text)
+ }
+}
diff --git a/vendor/tabled/src/settings/panel/vertical_panel.rs b/vendor/tabled/src/settings/panel/vertical_panel.rs
new file mode 100644
index 000000000..ddc0a562b
--- /dev/null
+++ b/vendor/tabled/src/settings/panel/vertical_panel.rs
@@ -0,0 +1,83 @@
+use crate::{
+ grid::config::{ColoredConfig, SpannedConfig},
+ grid::records::{ExactRecords, Records, RecordsMut, Resizable},
+ settings::TableOption,
+};
+
+/// A vertical/row span from 0 to a count columns.
+#[derive(Debug)]
+pub struct VerticalPanel<S> {
+ text: S,
+ col: usize,
+}
+
+impl<S> VerticalPanel<S> {
+ /// Creates a new vertical panel.
+ pub fn new(col: usize, text: S) -> Self
+ where
+ S: AsRef<str>,
+ {
+ Self { text, col }
+ }
+}
+
+impl<S, R, D> TableOption<R, D, ColoredConfig> for VerticalPanel<S>
+where
+ S: AsRef<str>,
+ R: Records + ExactRecords + Resizable + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ if self.col > count_cols {
+ return;
+ }
+
+ let is_intersect_horizontal_span = (0..=records.count_rows())
+ .any(|row| cfg.is_cell_covered_by_column_span((row, self.col)));
+
+ if is_intersect_horizontal_span {
+ return;
+ }
+
+ move_columns_aside(records, self.col);
+ move_column_spans(cfg, self.col);
+
+ let text = self.text.as_ref().to_owned();
+ records.set((0, self.col), text);
+
+ cfg.set_row_span((0, self.col), count_rows);
+ }
+}
+
+fn move_columns_aside<R: Records + Resizable>(records: &mut R, column: usize) {
+ records.push_column();
+
+ let count_columns = records.count_columns();
+ let shift_count = count_columns - column;
+ for i in 1..shift_count {
+ let col = count_columns - i;
+ records.swap_column(col, col - 1);
+ }
+}
+
+fn move_column_spans(cfg: &mut SpannedConfig, target_column: usize) {
+ for ((row, col), span) in cfg.get_column_spans() {
+ if col < target_column {
+ continue;
+ }
+
+ cfg.set_column_span((row, col), 1);
+ cfg.set_column_span((row, col + 1), span);
+ }
+
+ for ((row, col), span) in cfg.get_row_spans() {
+ if col < target_column {
+ continue;
+ }
+
+ cfg.set_row_span((row, col), 1);
+ cfg.set_row_span((row, col + 1), span);
+ }
+}
diff --git a/vendor/tabled/src/settings/peaker/mod.rs b/vendor/tabled/src/settings/peaker/mod.rs
new file mode 100644
index 000000000..a49796b02
--- /dev/null
+++ b/vendor/tabled/src/settings/peaker/mod.rs
@@ -0,0 +1,94 @@
+//! The module contains [`Peaker`] trait and its implementations to be used in [`Height`] and [`Width`].
+//!
+//! [`Width`]: crate::settings::width::Width
+//! [`Height`]: crate::settings::height::Height
+
+/// A strategy of width function.
+/// It determines the order how the function is applied.
+pub trait Peaker {
+ /// Creates a new instance.
+ fn create() -> Self;
+ /// This function returns a column index which will be changed.
+ /// Or `None` if no changes are necessary.
+ fn peak(&mut self, min_widths: &[usize], widths: &[usize]) -> Option<usize>;
+}
+
+/// A Peaker which goes over column 1 by 1.
+#[derive(Debug, Default, Clone)]
+pub struct PriorityNone {
+ i: usize,
+}
+
+impl Peaker for PriorityNone {
+ fn create() -> Self {
+ Self { i: 0 }
+ }
+
+ fn peak(&mut self, _: &[usize], widths: &[usize]) -> Option<usize> {
+ let mut i = self.i;
+ let mut count_empty = 0;
+ while widths[i] == 0 {
+ i += 1;
+ if i >= widths.len() {
+ i = 0;
+ }
+
+ count_empty += 1;
+ if count_empty == widths.len() {
+ return None;
+ }
+ }
+
+ let col = i;
+
+ i += 1;
+ if i >= widths.len() {
+ i = 0;
+ }
+
+ self.i = i;
+
+ Some(col)
+ }
+}
+
+/// A Peaker which goes over the biggest column first.
+#[derive(Debug, Default, Clone)]
+pub struct PriorityMax;
+
+impl Peaker for PriorityMax {
+ fn create() -> Self {
+ Self
+ }
+
+ fn peak(&mut self, _: &[usize], widths: &[usize]) -> Option<usize> {
+ let col = (0..widths.len()).max_by_key(|&i| widths[i]).unwrap();
+ if widths[col] == 0 {
+ None
+ } else {
+ Some(col)
+ }
+ }
+}
+
+/// A Peaker which goes over the smallest column first.
+#[derive(Debug, Default, Clone)]
+pub struct PriorityMin;
+
+impl Peaker for PriorityMin {
+ fn create() -> Self {
+ Self
+ }
+
+ fn peak(&mut self, min_widths: &[usize], widths: &[usize]) -> Option<usize> {
+ let col = (0..widths.len())
+ .filter(|&i| min_widths.is_empty() || widths[i] > min_widths[i])
+ .min_by_key(|&i| widths[i])
+ .unwrap();
+ if widths[col] == 0 {
+ None
+ } else {
+ Some(col)
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/rotate/mod.rs b/vendor/tabled/src/settings/rotate/mod.rs
new file mode 100644
index 000000000..426417190
--- /dev/null
+++ b/vendor/tabled/src/settings/rotate/mod.rs
@@ -0,0 +1,155 @@
+//! This module contains a [`Rotate`] primitive which can be used in order to rotate [`Table`].
+//!
+//! It's also possible to transpose the table at the point of construction.
+//! See [`Builder::index`].
+//!
+//! # Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::Rotate};
+//!
+//! let data = [[1, 2, 3], [4, 5, 6]];
+//!
+//! let table = Table::new(data).with(Rotate::Left).to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "+---+---+---+\n",
+//! "| 2 | 3 | 6 |\n",
+//! "+---+---+---+\n",
+//! "| 1 | 2 | 5 |\n",
+//! "+---+---+---+\n",
+//! "| 0 | 1 | 4 |\n",
+//! "+---+---+---+",
+//! )
+//! );
+//! ```
+//!
+//! [`Table`]: crate::Table
+//! [`Builder::index`]: crate::builder::Builder::index
+
+// use core::cmp::max;
+use core::cmp::max;
+
+use crate::{
+ grid::records::{ExactRecords, Records, Resizable},
+ settings::TableOption,
+};
+
+/// Rotate can be used to rotate a table by 90 degrees.
+#[derive(Debug)]
+pub enum Rotate {
+ /// Rotate [`Table`] to the left.
+ ///
+ /// [`Table`]: crate::Table
+ Left,
+ /// Rotate [`Table`] to the right.
+ ///
+ /// [`Table`]: crate::Table
+ Right,
+ /// Rotate [`Table`] to the top.
+ ///
+ /// So the top becomes the bottom.
+ ///
+ /// [`Table`]: crate::Table
+ Top,
+ /// Rotate [`Table`] to the bottom.
+ ///
+ /// So the top becomes the bottom.
+ ///
+ /// [`Table`]: crate::Table
+ Bottom,
+}
+
+impl<R, D, C> TableOption<R, D, C> for Rotate
+where
+ R: Records + ExactRecords + Resizable,
+{
+ fn change(self, records: &mut R, _: &mut C, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ match self {
+ Self::Left => {
+ let size = max(count_rows, count_cols);
+
+ {
+ for _ in count_rows..size {
+ records.push_row();
+ }
+
+ for _ in count_cols..size {
+ records.push_column();
+ }
+ }
+
+ for col in 0..size {
+ for row in col..size {
+ records.swap((col, row), (row, col));
+ }
+ }
+
+ for row in 0..count_cols / 2 {
+ records.swap_row(row, count_cols - row - 1);
+ }
+
+ {
+ for (shift, row) in (count_rows..size).enumerate() {
+ let row = row - shift;
+ records.remove_column(row);
+ }
+
+ for (shift, col) in (count_cols..size).enumerate() {
+ let col = col - shift;
+ records.remove_row(col);
+ }
+ }
+ }
+ Self::Right => {
+ let size = max(count_rows, count_cols);
+
+ {
+ for _ in count_rows..size {
+ records.push_row();
+ }
+
+ for _ in count_cols..size {
+ records.push_column();
+ }
+ }
+
+ for col in 0..size {
+ for row in col..size {
+ records.swap((col, row), (row, col));
+ }
+ }
+
+ for col in 0..count_rows / 2 {
+ records.swap_column(col, count_rows - col - 1);
+ }
+
+ {
+ for (shift, row) in (count_rows..size).enumerate() {
+ let row = row - shift;
+ records.remove_column(row);
+ }
+
+ for (shift, col) in (count_cols..size).enumerate() {
+ let col = col - shift;
+ records.remove_row(col);
+ }
+ }
+ }
+ Self::Bottom | Self::Top => {
+ for row in 0..count_rows / 2 {
+ for col in 0..count_cols {
+ let last_row = count_rows - row - 1;
+ records.swap((last_row, col), (row, col));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/settings_list.rs b/vendor/tabled/src/settings/settings_list.rs
new file mode 100644
index 000000000..dc8aec179
--- /dev/null
+++ b/vendor/tabled/src/settings/settings_list.rs
@@ -0,0 +1,71 @@
+use crate::settings::TableOption;
+
+#[cfg(feature = "std")]
+use crate::grid::config::Entity;
+#[cfg(feature = "std")]
+use crate::settings::CellOption;
+
+/// Settings is a combinator of [`TableOption`]s.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Settings<A = EmptySettings, B = EmptySettings>(A, B);
+
+impl Default for Settings<EmptySettings, EmptySettings> {
+ fn default() -> Self {
+ Self(EmptySettings, EmptySettings)
+ }
+}
+
+impl Settings<(), ()> {
+ /// Creates an empty list.
+ pub const fn empty() -> Settings<EmptySettings, EmptySettings> {
+ Settings(EmptySettings, EmptySettings)
+ }
+}
+
+impl<A, B> Settings<A, B> {
+ /// Creates a new combinator.
+ pub const fn new(settings1: A, settings2: B) -> Settings<A, B> {
+ Settings(settings1, settings2)
+ }
+
+ /// Add an option to a combinator.
+ pub const fn with<C>(self, settings: C) -> Settings<Self, C> {
+ Settings(self, settings)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, C, A, B> CellOption<R, C> for Settings<A, B>
+where
+ A: CellOption<R, C>,
+ B: CellOption<R, C>,
+{
+ fn change(self, records: &mut R, cfg: &mut C, entity: Entity) {
+ self.0.change(records, cfg, entity);
+ self.1.change(records, cfg, entity);
+ }
+}
+
+impl<R, D, C, A, B> TableOption<R, D, C> for Settings<A, B>
+where
+ A: TableOption<R, D, C>,
+ B: TableOption<R, D, C>,
+{
+ fn change(self, records: &mut R, cfg: &mut C, dims: &mut D) {
+ self.0.change(records, cfg, dims);
+ self.1.change(records, cfg, dims);
+ }
+}
+
+/// A marker structure to be able to create an empty [`Settings`].
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct EmptySettings;
+
+#[cfg(feature = "std")]
+impl<R, C> CellOption<R, C> for EmptySettings {
+ fn change(self, _: &mut R, _: &mut C, _: Entity) {}
+}
+
+impl<R, D, C> TableOption<R, D, C> for EmptySettings {
+ fn change(self, _: &mut R, _: &mut C, _: &mut D) {}
+}
diff --git a/vendor/tabled/src/settings/shadow/mod.rs b/vendor/tabled/src/settings/shadow/mod.rs
new file mode 100644
index 000000000..6b8ff4861
--- /dev/null
+++ b/vendor/tabled/src/settings/shadow/mod.rs
@@ -0,0 +1,195 @@
+//! This module contains a [`Shadow`] option for a [`Table`].
+//!
+//! # Example
+//!
+//! ```
+//! use tabled::{Table, settings::{Shadow, Style}};
+//!
+//! let data = vec!["Hello", "World", "!"];
+//!
+//! let table = Table::new(data)
+//! .with(Style::markdown())
+//! .with(Shadow::new(1))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "| &str | \n",
+//! "|-------|▒\n",
+//! "| Hello |▒\n",
+//! "| World |▒\n",
+//! "| ! |▒\n",
+//! " ▒▒▒▒▒▒▒▒▒",
+//! )
+//! );
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use crate::{
+ grid::color::AnsiColor,
+ grid::config::{ColoredConfig, Indent, Offset, Sides},
+ settings::{color::Color, TableOption},
+};
+
+/// The structure represents a shadow of a table.
+///
+/// NOTICE: It uses [`Margin`] therefore it often can't be combined.
+///
+/// [`Margin`]: crate::settings::Margin
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Shadow {
+ c: char,
+ size: usize,
+ size_offset: usize,
+ direction: Sides<bool>,
+ color: Option<Color>,
+}
+
+impl Shadow {
+ /// A default fill character to be used.
+ pub const DEFAULT_FILL: char = '▒';
+
+ /// Construct's an [`Shadow`] object with default fill [`Shadow::DEFAULT_FILL`].
+ ///
+ /// It uses space(' ') as a default fill character.
+ /// To set a custom character you can use [`Self::set_fill`] function.
+ pub fn new(size: usize) -> Self {
+ Self {
+ c: Self::DEFAULT_FILL,
+ size,
+ size_offset: 1,
+ direction: Sides::new(false, true, false, true),
+ color: None,
+ }
+ }
+
+ /// The function, sets a characters for the [`Shadow`] to be used.
+ pub fn set_fill(mut self, c: char) -> Self {
+ self.c = c;
+ self
+ }
+
+ /// Set an offset value (default is '1').
+ pub fn set_offset(mut self, size: usize) -> Self {
+ self.size_offset = size;
+ self
+ }
+
+ /// Switch shadow to top.
+ pub fn set_top(mut self) -> Self {
+ self.direction.top = true;
+ self.direction.bottom = false;
+ self
+ }
+
+ /// Switch shadow to bottom.
+ pub fn set_bottom(mut self) -> Self {
+ self.direction.bottom = true;
+ self.direction.top = false;
+ self
+ }
+
+ /// Switch shadow to left.
+ pub fn set_left(mut self) -> Self {
+ self.direction.left = true;
+ self.direction.right = false;
+ self
+ }
+
+ /// Switch shadow to right.
+ pub fn set_right(mut self) -> Self {
+ self.direction.right = true;
+ self.direction.left = false;
+ self
+ }
+
+ /// Sets a color for a shadow.
+ pub fn set_color(mut self, color: Color) -> Self {
+ self.color = Some(color);
+ self
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for Shadow {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ set_margin(cfg, self.size, self.c, &self.direction);
+ set_margin_offset(cfg, self.size_offset, &self.direction);
+
+ if let Some(color) = &self.color {
+ set_margin_color(cfg, color.clone().into(), &self.direction);
+ }
+ }
+}
+
+fn set_margin(cfg: &mut ColoredConfig, size: usize, c: char, direction: &Sides<bool>) {
+ let mut margin: Sides<Indent> = Sides::default();
+ if direction.top {
+ margin.top.size = size;
+ margin.top.fill = c;
+ }
+
+ if direction.bottom {
+ margin.bottom.size = size;
+ margin.bottom.fill = c;
+ }
+
+ if direction.left {
+ margin.left.size = size;
+ margin.left.fill = c;
+ }
+
+ if direction.right {
+ margin.right.size = size;
+ margin.right.fill = c;
+ }
+
+ cfg.set_margin(margin);
+}
+
+fn set_margin_offset(cfg: &mut ColoredConfig, size: usize, direction: &Sides<bool>) {
+ let mut margin = Sides::filled(Offset::Begin(0));
+ if direction.right && direction.bottom {
+ margin.bottom = Offset::Begin(size);
+ margin.right = Offset::Begin(size);
+ }
+
+ if direction.right && direction.top {
+ margin.top = Offset::Begin(size);
+ margin.right = Offset::End(size);
+ }
+
+ if direction.left && direction.bottom {
+ margin.bottom = Offset::End(size);
+ margin.left = Offset::Begin(size);
+ }
+
+ if direction.left && direction.top {
+ margin.top = Offset::End(size);
+ margin.left = Offset::End(size);
+ }
+
+ cfg.set_margin_offset(margin);
+}
+
+fn set_margin_color(cfg: &mut ColoredConfig, color: AnsiColor<'static>, direction: &Sides<bool>) {
+ let mut margin: Sides<Option<AnsiColor<'static>>> = Sides::default();
+ if direction.right {
+ margin.right = Some(color.clone());
+ }
+
+ if direction.top {
+ margin.top = Some(color.clone());
+ }
+
+ if direction.left {
+ margin.left = Some(color.clone());
+ }
+
+ if direction.bottom {
+ margin.bottom = Some(color.clone());
+ }
+
+ cfg.set_margin_color(margin);
+}
diff --git a/vendor/tabled/src/settings/span/column.rs b/vendor/tabled/src/settings/span/column.rs
new file mode 100644
index 000000000..50af64c23
--- /dev/null
+++ b/vendor/tabled/src/settings/span/column.rs
@@ -0,0 +1,125 @@
+use crate::{
+ grid::{
+ config::{ColoredConfig, Entity, Position, SpannedConfig},
+ records::{ExactRecords, Records},
+ },
+ settings::CellOption,
+};
+
+/// Columns (Vertical) span.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct ColumnSpan {
+ size: usize,
+}
+
+impl ColumnSpan {
+ /// Creates a new column (vertical) span.
+ pub fn new(size: usize) -> Self {
+ Self { size }
+ }
+
+ /// Creates a new column (vertical) span with a maximux value possible.
+ pub fn max() -> Self {
+ Self::new(usize::MAX)
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for ColumnSpan
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ set_col_spans(cfg, self.size, entity, (count_rows, count_cols));
+ remove_false_spans(cfg);
+ }
+}
+
+fn set_col_spans(cfg: &mut SpannedConfig, span: usize, entity: Entity, shape: (usize, usize)) {
+ for pos in entity.iter(shape.0, shape.1) {
+ if !is_valid_pos(pos, shape) {
+ continue;
+ }
+
+ let mut span = span;
+ if !is_column_span_valid(pos.1, span, shape.1) {
+ span = shape.1 - pos.1;
+ }
+
+ if span_has_intersections(cfg, pos, span) {
+ continue;
+ }
+
+ set_span_column(cfg, pos, span);
+ }
+}
+
+fn set_span_column(cfg: &mut SpannedConfig, pos: (usize, usize), span: usize) {
+ if span == 0 {
+ let (row, col) = pos;
+ if col == 0 {
+ return;
+ }
+
+ if let Some(closecol) = closest_visible(cfg, (row, col - 1)) {
+ let span = col + 1 - closecol;
+ cfg.set_column_span((row, closecol), span);
+ }
+ }
+
+ cfg.set_column_span(pos, span);
+}
+
+fn closest_visible(cfg: &SpannedConfig, mut pos: Position) -> Option<usize> {
+ loop {
+ if cfg.is_cell_visible(pos) {
+ return Some(pos.1);
+ }
+
+ if pos.1 == 0 {
+ return None;
+ }
+
+ pos.1 -= 1;
+ }
+}
+
+fn is_column_span_valid(col: usize, span: usize, count_cols: usize) -> bool {
+ span + col <= count_cols
+}
+
+fn is_valid_pos((row, col): Position, (count_rows, count_cols): (usize, usize)) -> bool {
+ row < count_rows && col < count_cols
+}
+
+fn span_has_intersections(cfg: &SpannedConfig, (row, col): Position, span: usize) -> bool {
+ for col in col..col + span {
+ if !cfg.is_cell_visible((row, col)) {
+ return true;
+ }
+ }
+
+ false
+}
+
+fn remove_false_spans(cfg: &mut SpannedConfig) {
+ for (pos, _) in cfg.get_column_spans() {
+ if cfg.is_cell_visible(pos) {
+ continue;
+ }
+
+ cfg.set_row_span(pos, 1);
+ cfg.set_column_span(pos, 1);
+ }
+
+ for (pos, _) in cfg.get_row_spans() {
+ if cfg.is_cell_visible(pos) {
+ continue;
+ }
+
+ cfg.set_row_span(pos, 1);
+ cfg.set_column_span(pos, 1);
+ }
+}
diff --git a/vendor/tabled/src/settings/span/mod.rs b/vendor/tabled/src/settings/span/mod.rs
new file mode 100644
index 000000000..1139a2b99
--- /dev/null
+++ b/vendor/tabled/src/settings/span/mod.rs
@@ -0,0 +1,68 @@
+//! This module contains a [`Span`] settings, it helps to
+//! make a cell take more space then it generally takes.
+//!
+//! # Example
+//!
+//! ```
+//! use tabled::{settings::{Span, Modify}, Table};
+//!
+//! let data = [[1, 2, 3], [4, 5, 6]];
+//!
+//! let table = Table::new(data)
+//! .with(Modify::new((2, 0)).with(Span::column(2)))
+//! .with(Modify::new((0, 1)).with(Span::column(2)))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "+---+---+---+\n",
+//! "| 0 | 1 |\n",
+//! "+---+---+---+\n",
+//! "| 1 | 2 | 3 |\n",
+//! "+---+---+---+\n",
+//! "| 4 | 6 |\n",
+//! "+---+---+---+",
+//! )
+//! )
+//! ```
+
+mod column;
+mod row;
+
+pub use column::ColumnSpan;
+pub use row::RowSpan;
+
+/// Span represent a horizontal/column span setting for any cell on a [`Table`].
+///
+/// It will be ignored if:
+/// - cell position is out of scope
+/// - size is bigger then the total number of columns.
+/// - size is bigger then the total number of rows.
+///
+/// ```rust,no_run
+/// # use tabled::{Table, settings::{Style, Span, Modify, object::Columns}};
+/// # let data: Vec<&'static str> = Vec::new();
+/// let table = Table::new(&data)
+/// .with(Modify::new(Columns::single(0)).with(Span::column(2)));
+/// ```
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Span;
+
+impl Span {
+ /// New constructs a horizontal/column [`Span`].
+ ///
+ /// If size is bigger then the total number of columns it will be ignored.
+ pub fn column(size: usize) -> ColumnSpan {
+ ColumnSpan::new(size)
+ }
+
+ /// New constructs a vertical/row [`Span`].
+ ///
+ /// If size is bigger then the total number of rows it will be ignored.
+ pub fn row(size: usize) -> RowSpan {
+ RowSpan::new(size)
+ }
+}
diff --git a/vendor/tabled/src/settings/span/row.rs b/vendor/tabled/src/settings/span/row.rs
new file mode 100644
index 000000000..68673f7a2
--- /dev/null
+++ b/vendor/tabled/src/settings/span/row.rs
@@ -0,0 +1,126 @@
+use crate::{
+ grid::{
+ config::{ColoredConfig, Entity, Position, SpannedConfig},
+ records::{ExactRecords, Records},
+ },
+ settings::CellOption,
+};
+
+/// Row (vertical) span.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct RowSpan {
+ size: usize,
+}
+
+impl RowSpan {
+ /// Creates a new row (vertical) span.
+ pub const fn new(size: usize) -> Self {
+ Self { size }
+ }
+
+ /// Creates a new row (vertical) span with a maximux value possible.
+ pub const fn max() -> Self {
+ Self::new(usize::MAX)
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for RowSpan
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ set_row_spans(cfg, self.size, entity, (count_rows, count_cols));
+ remove_false_spans(cfg);
+ }
+}
+
+fn set_row_spans(cfg: &mut SpannedConfig, span: usize, entity: Entity, shape: (usize, usize)) {
+ for pos in entity.iter(shape.0, shape.1) {
+ if !is_valid_pos(pos, shape) {
+ continue;
+ }
+
+ let mut span = span;
+ if !is_row_span_valid(pos.0, span, shape.0) {
+ span = shape.0 - pos.0;
+ }
+
+ if span_has_intersections(cfg, pos, span) {
+ continue;
+ }
+
+ set_span_row(cfg, pos, span);
+ }
+}
+
+fn set_span_row(cfg: &mut SpannedConfig, pos: (usize, usize), span: usize) {
+ if span == 0 {
+ let (row, col) = pos;
+ if row == 0 {
+ return;
+ }
+
+ if let Some(closerow) = closest_visible_row(cfg, (row - 1, col)) {
+ let span = row + 1 - closerow;
+ cfg.set_row_span((closerow, col), span);
+ }
+ }
+
+ cfg.set_row_span(pos, span);
+}
+
+fn closest_visible_row(cfg: &SpannedConfig, mut pos: Position) -> Option<usize> {
+ loop {
+ if cfg.is_cell_visible(pos) {
+ return Some(pos.0);
+ }
+
+ if pos.0 == 0 {
+ // can happen if we have a above horizontal spanned cell
+ return None;
+ }
+
+ pos.0 -= 1;
+ }
+}
+
+fn is_row_span_valid(row: usize, span: usize, count_rows: usize) -> bool {
+ span + row <= count_rows
+}
+
+fn is_valid_pos((row, col): Position, (count_rows, count_cols): (usize, usize)) -> bool {
+ row < count_rows && col < count_cols
+}
+
+fn span_has_intersections(cfg: &SpannedConfig, (row, col): Position, span: usize) -> bool {
+ for row in row..row + span {
+ if !cfg.is_cell_visible((row, col)) {
+ return true;
+ }
+ }
+
+ false
+}
+
+fn remove_false_spans(cfg: &mut SpannedConfig) {
+ for (pos, _) in cfg.get_column_spans() {
+ if cfg.is_cell_visible(pos) {
+ continue;
+ }
+
+ cfg.set_row_span(pos, 1);
+ cfg.set_column_span(pos, 1);
+ }
+
+ for (pos, _) in cfg.get_row_spans() {
+ if cfg.is_cell_visible(pos) {
+ continue;
+ }
+
+ cfg.set_row_span(pos, 1);
+ cfg.set_column_span(pos, 1);
+ }
+}
diff --git a/vendor/tabled/src/settings/split/mod.rs b/vendor/tabled/src/settings/split/mod.rs
new file mode 100644
index 000000000..32f820aef
--- /dev/null
+++ b/vendor/tabled/src/settings/split/mod.rs
@@ -0,0 +1,420 @@
+//! This module contains a [`Split`] setting which is used to
+//! format the cells of a [`Table`] by a provided index, direction, behavior, and display preference.
+//!
+//! [`Table`]: crate::Table
+
+use core::ops::Range;
+
+use papergrid::{config::Position, records::PeekableRecords};
+
+use crate::grid::records::{ExactRecords, Records, Resizable};
+
+use super::TableOption;
+
+#[derive(Debug, Clone, Copy)]
+enum Direction {
+ Column,
+ Row,
+}
+
+#[derive(Debug, Clone, Copy)]
+enum Behavior {
+ Concat,
+ Zip,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+enum Display {
+ Clean,
+ Retain,
+}
+
+/// Returns a new [`Table`] formatted with several optional parameters.
+///
+/// The required index parameter determines how many columns/rows a table will be redistributed into.
+///
+/// - index
+/// - direction
+/// - behavior
+/// - display
+///
+/// # Example
+///
+/// ```rust,no_run
+/// use std::iter::FromIterator;
+/// use tabled::{
+/// settings::split::Split,
+/// Table,
+/// };
+///
+/// let mut table = Table::from_iter(['a'..='z']);
+/// let table = table.with(Split::column(4)).to_string();
+///
+/// assert_eq!(table, "+---+---+---+---+\n\
+/// | a | b | c | d |\n\
+/// +---+---+---+---+\n\
+/// | e | f | g | h |\n\
+/// +---+---+---+---+\n\
+/// | i | j | k | l |\n\
+/// +---+---+---+---+\n\
+/// | m | n | o | p |\n\
+/// +---+---+---+---+\n\
+/// | q | r | s | t |\n\
+/// +---+---+---+---+\n\
+/// | u | v | w | x |\n\
+/// +---+---+---+---+\n\
+/// | y | z | | |\n\
+/// +---+---+---+---+")
+/// ```
+///
+/// [`Table`]: crate::Table
+#[derive(Debug, Clone, Copy)]
+pub struct Split {
+ direction: Direction,
+ behavior: Behavior,
+ display: Display,
+ index: usize,
+}
+
+impl Split {
+ /// Returns a new [`Table`] split on the column at the provided index.
+ ///
+ /// The column found at that index becomes the new right-most column in the returned table.
+ /// Columns found beyond the index are redistributed into the table based on other defined
+ /// parameters.
+ ///
+ /// ```rust,no_run
+ /// # use tabled::settings::split::Split;
+ /// Split::column(4);
+ /// ```
+ ///
+ /// [`Table`]: crate::Table
+ pub fn column(index: usize) -> Self {
+ Split {
+ direction: Direction::Column,
+ behavior: Behavior::Zip,
+ display: Display::Clean,
+ index,
+ }
+ }
+
+ /// Returns a new [`Table`] split on the row at the provided index.
+ ///
+ /// The row found at that index becomes the new bottom row in the returned table.
+ /// Rows found beyond the index are redistributed into the table based on other defined
+ /// parameters.
+ ///
+ /// ```rust,no_run
+ /// # use tabled::settings::split::Split;
+ /// Split::row(4);
+ /// ```
+ ///
+ /// [`Table`]: crate::Table
+ pub fn row(index: usize) -> Self {
+ Split {
+ direction: Direction::Row,
+ behavior: Behavior::Zip,
+ display: Display::Clean,
+ index,
+ }
+ }
+
+ /// Returns a split [`Table`] with the redistributed cells pushed to the back of the new shape.
+ ///
+ /// ```text
+ /// +---+---+
+ /// | a | b |
+ /// +---+---+
+ /// +---+---+---+---+---+ | f | g |
+ /// | a | b | c | d | e | Split::column(2).concat() +---+---+
+ /// +---+---+---+---+---+ => | c | d |
+ /// | f | g | h | i | j | +---+---+
+ /// +---+---+---+---+---+ | h | i |
+ /// +---+---+
+ /// | e | |
+ /// +---+---+
+ /// | j | |
+ /// +---+---+
+ /// ```
+ ///
+ /// [`Table`]: crate::Table
+ pub fn concat(self) -> Self {
+ Self {
+ behavior: Behavior::Concat,
+ ..self
+ }
+ }
+
+ /// Returns a split [`Table`] with the redistributed cells inserted behind
+ /// the first correlating column/row one after another.
+ ///
+ /// ```text
+ /// +---+---+
+ /// | a | b |
+ /// +---+---+
+ /// +---+---+---+---+---+ | c | d |
+ /// | a | b | c | d | e | Split::column(2).zip() +---+---+
+ /// +---+---+---+---+---+ => | e | |
+ /// | f | g | h | i | j | +---+---+
+ /// +---+---+---+---+---+ | f | g |
+ /// +---+---+
+ /// | h | i |
+ /// +---+---+
+ /// | j | |
+ /// +---+---+
+ /// ```
+ ///
+ /// [`Table`]: crate::Table
+ pub fn zip(self) -> Self {
+ Self {
+ behavior: Behavior::Zip,
+ ..self
+ }
+ }
+
+ /// Returns a split [`Table`] with the empty columns/rows filtered out.
+ ///
+ /// ```text
+ ///
+ ///
+ /// +---+---+---+
+ /// +---+---+---+---+---+ | a | b | c |
+ /// | a | b | c | d | e | Split::column(3).clean() +---+---+---+
+ /// +---+---+---+---+---+ => | d | e | |
+ /// | f | g | h | | | +---+---+---+
+ /// +---+---+---+---+---+ | f | g | h |
+ /// ^ ^ +---+---+---+
+ /// these cells are filtered
+ /// from the resulting table
+ /// ```
+ ///
+ /// ## Notes
+ ///
+ /// This is apart of the default configuration for Split.
+ ///
+ /// See [`retain`] for an alternative display option.
+ ///
+ /// [`Table`]: crate::Table
+ /// [`retain`]: crate::settings::split::Split::retain
+ pub fn clean(self) -> Self {
+ Self {
+ display: Display::Clean,
+ ..self
+ }
+ }
+
+ /// Returns a split [`Table`] with all cells retained.
+ ///
+ /// ```text
+ /// +---+---+---+
+ /// | a | b | c |
+ /// +---+---+---+
+ /// +---+---+---+---+---+ | d | e | |
+ /// | a | b | c | d | e | Split::column(3).retain() +---+---+---+
+ /// +---+---+---+---+---+ => | f | g | h |
+ /// | f | g | h | | | +---+---+---+
+ /// +---+---+---+---+---+ |-----------> | | | |
+ /// ^ ^ | +---+---+---+
+ /// |___|_____cells are kept!
+ /// ```
+ ///
+ /// ## Notes
+ ///
+ /// See [`clean`] for an alternative display option.
+ ///
+ /// [`Table`]: crate::Table
+ /// [`clean`]: crate::settings::split::Split::clean
+ pub fn retain(self) -> Self {
+ Self {
+ display: Display::Retain,
+ ..self
+ }
+ }
+}
+
+impl<R, D, Cfg> TableOption<R, D, Cfg> for Split
+where
+ R: Records + ExactRecords + Resizable + PeekableRecords,
+{
+ fn change(self, records: &mut R, _: &mut Cfg, _: &mut D) {
+ // variables
+ let Split {
+ direction,
+ behavior,
+ display,
+ index: section_length,
+ } = self;
+ let mut filtered_sections = 0;
+
+ // early return check
+ if records.count_columns() == 0 || records.count_rows() == 0 || section_length == 0 {
+ return;
+ }
+
+ // computed variables
+ let (primary_length, secondary_length) = compute_length_arrangement(records, direction);
+ let sections_per_direction = ceil_div(primary_length, section_length);
+ let (outer_range, inner_range) =
+ compute_range_order(secondary_length, sections_per_direction, behavior);
+
+ // work
+ for outer_index in outer_range {
+ let from_secondary_index = outer_index * sections_per_direction - filtered_sections;
+ for inner_index in inner_range.clone() {
+ let (section_index, from_secondary_index, to_secondary_index) =
+ compute_range_variables(
+ outer_index,
+ inner_index,
+ secondary_length,
+ from_secondary_index,
+ sections_per_direction,
+ filtered_sections,
+ behavior,
+ );
+
+ match (direction, behavior) {
+ (Direction::Column, Behavior::Concat) => records.push_row(),
+ (Direction::Column, Behavior::Zip) => records.insert_row(to_secondary_index),
+ (Direction::Row, Behavior::Concat) => records.push_column(),
+ (Direction::Row, Behavior::Zip) => records.insert_column(to_secondary_index),
+ }
+
+ let section_is_empty = copy_section(
+ records,
+ section_length,
+ section_index,
+ primary_length,
+ from_secondary_index,
+ to_secondary_index,
+ direction,
+ );
+
+ if section_is_empty && display == Display::Clean {
+ delete(records, to_secondary_index, direction);
+ filtered_sections += 1;
+ }
+ }
+ }
+
+ cleanup(records, section_length, primary_length, direction);
+ }
+}
+
+/// Determine which direction should be considered the primary, and which the secondary based on direction
+fn compute_length_arrangement<R>(records: &mut R, direction: Direction) -> (usize, usize)
+where
+ R: Records + ExactRecords,
+{
+ match direction {
+ Direction::Column => (records.count_columns(), records.count_rows()),
+ Direction::Row => (records.count_rows(), records.count_columns()),
+ }
+}
+
+/// reduce the table size to the length of the index in the specified direction
+fn cleanup<R>(records: &mut R, section_length: usize, primary_length: usize, direction: Direction)
+where
+ R: Resizable,
+{
+ for segment in (section_length..primary_length).rev() {
+ match direction {
+ Direction::Column => records.remove_column(segment),
+ Direction::Row => records.remove_row(segment),
+ }
+ }
+}
+
+/// Delete target index row or column
+fn delete<R>(records: &mut R, target_index: usize, direction: Direction)
+where
+ R: Resizable,
+{
+ match direction {
+ Direction::Column => records.remove_row(target_index),
+ Direction::Row => records.remove_column(target_index),
+ }
+}
+
+/// copy cells to new location
+///
+/// returns if the copied section was entirely blank
+fn copy_section<R>(
+ records: &mut R,
+ section_length: usize,
+ section_index: usize,
+ primary_length: usize,
+ from_secondary_index: usize,
+ to_secondary_index: usize,
+ direction: Direction,
+) -> bool
+where
+ R: ExactRecords + Resizable + PeekableRecords,
+{
+ let mut section_is_empty = true;
+ for to_primary_index in 0..section_length {
+ let from_primary_index = to_primary_index + section_index * section_length;
+
+ if from_primary_index < primary_length {
+ let from_position =
+ format_position(direction, from_primary_index, from_secondary_index);
+ if records.get_text(from_position) != "" {
+ section_is_empty = false;
+ }
+ records.swap(
+ from_position,
+ format_position(direction, to_primary_index, to_secondary_index),
+ );
+ }
+ }
+ section_is_empty
+}
+
+/// determine section over direction or vice versa based on behavior
+fn compute_range_order(
+ direction_length: usize,
+ sections_per_direction: usize,
+ behavior: Behavior,
+) -> (Range<usize>, Range<usize>) {
+ match behavior {
+ Behavior::Concat => (1..sections_per_direction, 0..direction_length),
+ Behavior::Zip => (0..direction_length, 1..sections_per_direction),
+ }
+}
+
+/// helper function for shimming both behaviors to work within a single nested loop
+fn compute_range_variables(
+ outer_index: usize,
+ inner_index: usize,
+ direction_length: usize,
+ from_secondary_index: usize,
+ sections_per_direction: usize,
+ filtered_sections: usize,
+ behavior: Behavior,
+) -> (usize, usize, usize) {
+ match behavior {
+ Behavior::Concat => (
+ outer_index,
+ inner_index,
+ inner_index + outer_index * direction_length - filtered_sections,
+ ),
+ Behavior::Zip => (
+ inner_index,
+ from_secondary_index,
+ outer_index * sections_per_direction + inner_index - filtered_sections,
+ ),
+ }
+}
+
+/// utility for arguments of a position easily
+fn format_position(direction: Direction, primary_index: usize, secondary_index: usize) -> Position {
+ match direction {
+ Direction::Column => (secondary_index, primary_index),
+ Direction::Row => (primary_index, secondary_index),
+ }
+}
+
+/// ceil division utility because the std lib ceil_div isn't stable yet
+fn ceil_div(x: usize, y: usize) -> usize {
+ debug_assert!(x != 0);
+ 1 + ((x - 1) / y)
+}
diff --git a/vendor/tabled/src/settings/style/border.rs b/vendor/tabled/src/settings/style/border.rs
new file mode 100644
index 000000000..a8cbefb5d
--- /dev/null
+++ b/vendor/tabled/src/settings/style/border.rs
@@ -0,0 +1,159 @@
+use crate::{
+ grid::{
+ config::{Border as GBorder, ColoredConfig, Entity},
+ records::{ExactRecords, Records},
+ },
+ settings::CellOption,
+};
+
+/// Border represents a border of a Cell.
+///
+/// ```text
+/// top border
+/// |
+/// V
+/// corner top left ------> +_______+ <---- corner top left
+/// | |
+/// left border ----------> | cell | <---- right border
+/// | |
+/// corner bottom right --> +_______+ <---- corner bottom right
+/// ^
+/// |
+/// bottom border
+/// ```
+///
+/// ```rust,no_run
+/// # use tabled::{Table, settings::{Modify, style::{Style, Border}, object::Rows}};
+/// # let data: Vec<&'static str> = Vec::new();
+/// let table = Table::new(&data)
+/// .with(Style::ascii())
+/// .with(Modify::new(Rows::single(0)).with(Border::default().top('x')));
+/// ```
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Border(GBorder<char>);
+
+impl Border {
+ /// This function constructs a cell borders with all sides set.
+ #[allow(clippy::too_many_arguments)]
+ pub const fn full(
+ top: char,
+ bottom: char,
+ left: char,
+ right: char,
+ top_left: char,
+ top_right: char,
+ bottom_left: char,
+ bottom_right: char,
+ ) -> Self {
+ Self(GBorder::full(
+ top,
+ bottom,
+ left,
+ right,
+ top_left,
+ top_right,
+ bottom_left,
+ bottom_right,
+ ))
+ }
+
+ /// This function constructs a cell borders with all sides's char set to a given character.
+ /// It behaves like [`Border::full`] with the same character set to each side.
+ pub const fn filled(c: char) -> Self {
+ Self::full(c, c, c, c, c, c, c, c)
+ }
+
+ /// Using this function you deconstruct the existing borders.
+ pub const fn empty() -> EmptyBorder {
+ EmptyBorder
+ }
+
+ /// Set a top border character.
+ pub const fn top(mut self, c: char) -> Self {
+ self.0.top = Some(c);
+ self
+ }
+
+ /// Set a bottom border character.
+ pub const fn bottom(mut self, c: char) -> Self {
+ self.0.bottom = Some(c);
+ self
+ }
+
+ /// Set a left border character.
+ pub const fn left(mut self, c: char) -> Self {
+ self.0.left = Some(c);
+ self
+ }
+
+ /// Set a right border character.
+ pub const fn right(mut self, c: char) -> Self {
+ self.0.right = Some(c);
+ self
+ }
+
+ /// Set a top left intersection character.
+ pub const fn corner_top_left(mut self, c: char) -> Self {
+ self.0.left_top_corner = Some(c);
+ self
+ }
+
+ /// Set a top right intersection character.
+ pub const fn corner_top_right(mut self, c: char) -> Self {
+ self.0.right_top_corner = Some(c);
+ self
+ }
+
+ /// Set a bottom left intersection character.
+ pub const fn corner_bottom_left(mut self, c: char) -> Self {
+ self.0.left_bottom_corner = Some(c);
+ self
+ }
+
+ /// Set a bottom right intersection character.
+ pub const fn corner_bottom_right(mut self, c: char) -> Self {
+ self.0.right_bottom_corner = Some(c);
+ self
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for Border
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let shape = (records.count_rows(), records.count_columns());
+
+ for pos in entity.iter(shape.0, shape.1) {
+ cfg.set_border(pos, self.0);
+ }
+ }
+}
+
+impl From<GBorder<char>> for Border {
+ fn from(b: GBorder<char>) -> Border {
+ Border(b)
+ }
+}
+
+impl From<Border> for GBorder<char> {
+ fn from(value: Border) -> Self {
+ value.0
+ }
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct EmptyBorder;
+
+impl<R> CellOption<R, ColoredConfig> for EmptyBorder
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let shape = (records.count_rows(), records.count_columns());
+
+ for pos in entity.iter(shape.0, shape.1) {
+ cfg.remove_border(pos, shape);
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/style/border_char.rs b/vendor/tabled/src/settings/style/border_char.rs
new file mode 100644
index 000000000..b1e04c579
--- /dev/null
+++ b/vendor/tabled/src/settings/style/border_char.rs
@@ -0,0 +1,99 @@
+use crate::{
+ grid::config::{ColoredConfig, Entity, Position, SpannedConfig},
+ grid::records::{ExactRecords, Records},
+ settings::CellOption,
+};
+
+use super::Offset;
+
+/// [`BorderChar`] sets a char to a specific location on a horizontal line.
+///
+/// # Example
+///
+/// ```rust
+/// use tabled::{Table, settings::{style::{Style, BorderChar, Offset}, Modify, object::{Object, Rows, Columns}}};
+///
+/// let mut table = Table::new(["Hello World"]);
+/// table
+/// .with(Style::markdown())
+/// .with(Modify::new(Rows::single(1))
+/// .with(BorderChar::horizontal(':', Offset::Begin(0)))
+/// .with(BorderChar::horizontal(':', Offset::End(0)))
+/// )
+/// .with(Modify::new((1, 0).and((1, 1))).with(BorderChar::vertical('#', Offset::Begin(0))));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// concat!(
+/// "| &str |\n",
+/// "|:-----------:|\n",
+/// "# Hello World #",
+/// ),
+/// );
+/// ```
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct BorderChar {
+ c: char,
+ offset: Offset,
+ horizontal: bool,
+}
+
+impl BorderChar {
+ /// Creates a [`BorderChar`] which overrides horizontal line.
+ pub fn horizontal(c: char, offset: Offset) -> Self {
+ Self {
+ c,
+ offset,
+ horizontal: true,
+ }
+ }
+
+ /// Creates a [`BorderChar`] which overrides vertical line.
+ pub fn vertical(c: char, offset: Offset) -> Self {
+ Self {
+ c,
+ offset,
+ horizontal: false,
+ }
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for BorderChar
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let cells = entity.iter(records.count_rows(), records.count_columns());
+
+ match self.horizontal {
+ true => add_char_horizontal(cfg, self.c, self.offset, cells),
+ false => add_char_vertical(cfg, self.c, self.offset, cells),
+ }
+ }
+}
+
+fn add_char_vertical<I: Iterator<Item = Position>>(
+ cfg: &mut SpannedConfig,
+ c: char,
+ offset: Offset,
+ cells: I,
+) {
+ let offset = offset.into();
+
+ for pos in cells {
+ cfg.set_vertical_char(pos, c, offset);
+ }
+}
+
+fn add_char_horizontal<I: Iterator<Item = Position>>(
+ cfg: &mut SpannedConfig,
+ c: char,
+ offset: Offset,
+ cells: I,
+) {
+ let offset = offset.into();
+
+ for pos in cells {
+ cfg.set_horizontal_char(pos, c, offset);
+ }
+}
diff --git a/vendor/tabled/src/settings/style/border_color.rs b/vendor/tabled/src/settings/style/border_color.rs
new file mode 100644
index 000000000..6b3fa2b1a
--- /dev/null
+++ b/vendor/tabled/src/settings/style/border_color.rs
@@ -0,0 +1,155 @@
+//! This module contains a configuration of a Border to set its color via [`BorderColor`].
+
+use crate::{
+ grid::{
+ color::AnsiColor,
+ config::{Border, ColoredConfig, Entity},
+ records::{ExactRecords, Records},
+ },
+ settings::{color::Color, CellOption, TableOption},
+};
+
+/// BorderColored represents a colored border of a Cell.
+///
+/// ```rust,no_run
+/// # use tabled::{settings::{style::BorderColor, Style, Color, object::Rows, Modify}, Table};
+/// #
+/// # let data: Vec<&'static str> = Vec::new();
+/// #
+/// let table = Table::new(&data)
+/// .with(Style::ascii())
+/// .with(Modify::new(Rows::single(0)).with(BorderColor::default().top(Color::FG_RED)));
+/// ```
+#[derive(Debug, Clone, Default, Eq, PartialEq)]
+pub struct BorderColor(Border<AnsiColor<'static>>);
+
+impl BorderColor {
+ /// This function constructs a cell borders with all sides set.
+ #[allow(clippy::too_many_arguments)]
+ pub fn full(
+ top: Color,
+ bottom: Color,
+ left: Color,
+ right: Color,
+ top_left: Color,
+ top_right: Color,
+ bottom_left: Color,
+ bottom_right: Color,
+ ) -> Self {
+ Self(Border::full(
+ top.into(),
+ bottom.into(),
+ left.into(),
+ right.into(),
+ top_left.into(),
+ top_right.into(),
+ bottom_left.into(),
+ bottom_right.into(),
+ ))
+ }
+
+ /// This function constructs a cell borders with all sides's char set to a given character.
+ /// It behaves like [`Border::full`] with the same character set to each side.
+ pub fn filled(c: Color) -> Self {
+ let c: AnsiColor<'_> = c.into();
+
+ Self(Border {
+ top: Some(c.clone()),
+ bottom: Some(c.clone()),
+ left: Some(c.clone()),
+ right: Some(c.clone()),
+ left_bottom_corner: Some(c.clone()),
+ left_top_corner: Some(c.clone()),
+ right_bottom_corner: Some(c.clone()),
+ right_top_corner: Some(c),
+ })
+ }
+
+ /// Set a top border character.
+ pub fn top(mut self, c: Color) -> Self {
+ self.0.top = Some(c.into());
+ self
+ }
+
+ /// Set a bottom border character.
+ pub fn bottom(mut self, c: Color) -> Self {
+ self.0.bottom = Some(c.into());
+ self
+ }
+
+ /// Set a left border character.
+ pub fn left(mut self, c: Color) -> Self {
+ self.0.left = Some(c.into());
+ self
+ }
+
+ /// Set a right border character.
+ pub fn right(mut self, c: Color) -> Self {
+ self.0.right = Some(c.into());
+ self
+ }
+
+ /// Set a top left intersection character.
+ pub fn corner_top_left(mut self, c: Color) -> Self {
+ self.0.left_top_corner = Some(c.into());
+ self
+ }
+
+ /// Set a top right intersection character.
+ pub fn corner_top_right(mut self, c: Color) -> Self {
+ self.0.right_top_corner = Some(c.into());
+ self
+ }
+
+ /// Set a bottom left intersection character.
+ pub fn corner_bottom_left(mut self, c: Color) -> Self {
+ self.0.left_bottom_corner = Some(c.into());
+ self
+ }
+
+ /// Set a bottom right intersection character.
+ pub fn corner_bottom_right(mut self, c: Color) -> Self {
+ self.0.right_bottom_corner = Some(c.into());
+ self
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for BorderColor
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ let border_color = &self.0;
+
+ for pos in entity.iter(count_rows, count_columns) {
+ cfg.set_border_color(pos, border_color.clone());
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderColor
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ let border_color = &self.0;
+
+ for row in 0..count_rows {
+ for col in 0..count_columns {
+ cfg.set_border_color((row, col), border_color.clone());
+ }
+ }
+ }
+}
+
+impl From<BorderColor> for Border<AnsiColor<'static>> {
+ fn from(val: BorderColor) -> Self {
+ val.0
+ }
+}
diff --git a/vendor/tabled/src/settings/style/border_text.rs b/vendor/tabled/src/settings/style/border_text.rs
new file mode 100644
index 000000000..163a19821
--- /dev/null
+++ b/vendor/tabled/src/settings/style/border_text.rs
@@ -0,0 +1,253 @@
+use crate::{
+ grid::{
+ color::AnsiColor,
+ config::{self, ColoredConfig, SpannedConfig},
+ dimension::{Dimension, Estimate},
+ records::{ExactRecords, Records},
+ },
+ settings::{
+ object::{FirstRow, LastRow},
+ Color, TableOption,
+ },
+};
+
+use super::Offset;
+
+/// [`BorderText`] writes a custom text on a border.
+///
+/// # Example
+///
+/// ```rust
+/// use tabled::{Table, settings::style::BorderText, settings::object::Rows};
+///
+/// let mut table = Table::new(["Hello World"]);
+/// table
+/// .with(BorderText::new("+-.table").horizontal(Rows::first()));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+-.table------+\n\
+/// | &str |\n\
+/// +-------------+\n\
+/// | Hello World |\n\
+/// +-------------+"
+/// );
+/// ```
+#[derive(Debug)]
+pub struct BorderText<L> {
+ text: String,
+ offset: Offset,
+ color: Option<AnsiColor<'static>>,
+ line: L,
+}
+
+impl BorderText<()> {
+ /// Creates a [`BorderText`] instance.
+ ///
+ /// Lines are numbered from 0 to the `count_rows` included
+ /// (`line >= 0 && line <= count_rows`).
+ pub fn new<S: Into<String>>(text: S) -> Self {
+ BorderText {
+ text: text.into(),
+ line: (),
+ offset: Offset::Begin(0),
+ color: None,
+ }
+ }
+}
+
+impl<Line> BorderText<Line> {
+ /// Set a line on which we will set the text.
+ pub fn horizontal<L>(self, line: L) -> BorderText<L> {
+ BorderText {
+ line,
+ text: self.text,
+ offset: self.offset,
+ color: self.color,
+ }
+ }
+
+ /// Set an offset from which the text will be started.
+ pub fn offset(self, offset: Offset) -> Self {
+ BorderText {
+ offset,
+ text: self.text,
+ line: self.line,
+ color: self.color,
+ }
+ }
+
+ /// Set a color of the text.
+ pub fn color(self, color: Color) -> Self {
+ BorderText {
+ color: Some(color.into()),
+ text: self.text,
+ line: self.line,
+ offset: self.offset,
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderText<usize>
+where
+ R: Records + ExactRecords,
+ for<'a> &'a R: Records,
+ for<'a> D: Estimate<&'a R, ColoredConfig>,
+ D: Dimension,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dims: &mut D) {
+ dims.estimate(records, cfg);
+ let shape = (records.count_rows(), records.count_columns());
+ let line = self.line;
+ set_horizontal_chars(cfg, dims, self.offset, line, &self.text, &self.color, shape);
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderText<FirstRow>
+where
+ R: Records + ExactRecords,
+ for<'a> &'a R: Records,
+ for<'a> D: Estimate<&'a R, ColoredConfig>,
+ D: Dimension,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dims: &mut D) {
+ dims.estimate(records, cfg);
+ let shape = (records.count_rows(), records.count_columns());
+ let line = 0;
+ set_horizontal_chars(cfg, dims, self.offset, line, &self.text, &self.color, shape);
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderText<LastRow>
+where
+ R: Records + ExactRecords,
+ for<'a> &'a R: Records,
+ for<'a> D: Estimate<&'a R, ColoredConfig>,
+ D: Dimension,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dims: &mut D) {
+ dims.estimate(records, cfg);
+ let shape = (records.count_rows(), records.count_columns());
+ let line = records.count_rows();
+ set_horizontal_chars(cfg, dims, self.offset, line, &self.text, &self.color, shape);
+ }
+}
+
+fn set_horizontal_chars<D: Dimension>(
+ cfg: &mut SpannedConfig,
+ dims: &D,
+ offset: Offset,
+ line: usize,
+ text: &str,
+ color: &Option<AnsiColor<'static>>,
+ shape: (usize, usize),
+) {
+ let (_, count_columns) = shape;
+ let pos = get_start_pos(cfg, dims, offset, count_columns);
+ let pos = match pos {
+ Some(pos) => pos,
+ None => return,
+ };
+
+ let mut chars = text.chars();
+ let mut i = cfg.has_vertical(0, count_columns) as usize;
+ if i == 1 && pos == 0 {
+ let c = match chars.next() {
+ Some(c) => c,
+ None => return,
+ };
+
+ let mut b = cfg.get_border((line, 0), shape);
+ b.left_top_corner = b.left_top_corner.map(|_| c);
+ cfg.set_border((line, 0), b);
+
+ if let Some(color) = color.as_ref() {
+ let mut b = cfg.get_border_color((line, 0), shape).cloned();
+ b.left_top_corner = Some(color.clone());
+ cfg.set_border_color((line, 0), b);
+ }
+ }
+
+ for col in 0..count_columns {
+ let w = dims.get_width(col);
+ if i + w > pos {
+ for off in 0..w {
+ if i + off < pos {
+ continue;
+ }
+
+ let c = match chars.next() {
+ Some(c) => c,
+ None => return,
+ };
+
+ cfg.set_horizontal_char((line, col), c, config::Offset::Begin(off));
+ if let Some(color) = color.as_ref() {
+ cfg.set_horizontal_color(
+ (line, col),
+ color.clone(),
+ config::Offset::Begin(off),
+ );
+ }
+ }
+ }
+
+ i += w;
+
+ if cfg.has_vertical(col + 1, count_columns) {
+ i += 1;
+
+ if i > pos {
+ let c = match chars.next() {
+ Some(c) => c,
+ None => return,
+ };
+
+ let mut b = cfg.get_border((line, col), shape);
+ b.right_top_corner = b.right_top_corner.map(|_| c);
+ cfg.set_border((line, col), b);
+
+ if let Some(color) = color.as_ref() {
+ let mut b = cfg.get_border_color((line, col), shape).cloned();
+ b.right_top_corner = Some(color.clone());
+ cfg.set_border_color((line, col), b);
+ }
+ }
+ }
+ }
+}
+
+fn get_start_pos<D: Dimension>(
+ cfg: &SpannedConfig,
+ dims: &D,
+ offset: Offset,
+ count_columns: usize,
+) -> Option<usize> {
+ let totalw = total_width(cfg, dims, count_columns);
+ match offset {
+ Offset::Begin(i) => {
+ if i > totalw {
+ None
+ } else {
+ Some(i)
+ }
+ }
+ Offset::End(i) => {
+ if i > totalw {
+ None
+ } else {
+ Some(totalw - i)
+ }
+ }
+ }
+}
+
+fn total_width<D: Dimension>(cfg: &SpannedConfig, dims: &D, count_columns: usize) -> usize {
+ let mut totalw = cfg.has_vertical(0, count_columns) as usize;
+ for col in 0..count_columns {
+ totalw += dims.get_width(col);
+ totalw += cfg.has_vertical(col + 1, count_columns) as usize;
+ }
+
+ totalw
+}
diff --git a/vendor/tabled/src/settings/style/builder.rs b/vendor/tabled/src/settings/style/builder.rs
new file mode 100644
index 000000000..d55c400cc
--- /dev/null
+++ b/vendor/tabled/src/settings/style/builder.rs
@@ -0,0 +1,1208 @@
+//! This module contains a compile time style builder [`Style`].
+
+use core::marker::PhantomData;
+
+use crate::{
+ grid::config::{Borders, CompactConfig, CompactMultilineConfig},
+ settings::TableOption,
+};
+
+#[cfg(feature = "std")]
+use crate::grid::config::ColoredConfig;
+
+use super::{HorizontalLine, Line, VerticalLine};
+
+/// Style is represents a theme of a [`Table`].
+///
+/// ```text
+/// corner top left top intersection corner top right
+/// . | .
+/// . V .
+/// ╭───┬───┬───┬───┬───┬───┬────┬────┬────╮
+/// │ i │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │
+/// ├───┼───┼───┼───┼───┼───┼────┼────┼────┤ <- this horizontal line is custom 'horizontals'
+/// │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ other lines horizontal lines are not set they called 'horizontal'
+/// │ 1 │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │
+/// │ 2 │ 0 │ 2 │ 4 │ 6 │ 8 │ 10 │ 12 │ 14 │
+/// ╰───┴───┴───┴───┴───┴───┴────┴────┴────╯
+/// . ^ ^ .
+/// . | | .
+/// corner bottom left | bottom intersection corner bottom right
+/// |
+/// |
+/// all this vertical lines are called 'vertical'
+/// ```
+///
+///
+/// ```text
+/// ┌───┬───┬───┬───┬───┐
+/// │ 0 │ 1 │ 2 │ 3 │ 4 │
+/// intersection left ->├───X───X───X───X───┤ <- all this horizontal lines are called 'horizontal'
+/// │ 1 │ 2 │ 3 │ 4 │ 5 │
+/// ├───X───X───X───X───┤ <- intersection right
+/// │ 2 │ 3 │ 4 │ 5 │ 6 │
+/// └───┴───┴───┴───┴───┘
+///
+/// All 'X' positions are called 'intersection'.
+/// It's a place where 'vertical' and 'horizontal' lines intersect.
+/// ```
+///
+/// It tries to limit an controlling a valid state of it.
+/// For example, it won't allow to call method [`Style::corner_top_left`] unless [`Style::left`] and [`Style::top`] is set.
+///
+/// You can turn [`Style`] into [`RawStyle`] to have more control using [`Into`] implementation.
+///
+/// # Example
+///
+#[cfg_attr(feature = "std", doc = "```")]
+#[cfg_attr(not(feature = "std"), doc = "```ignore")]
+/// use tabled::{Table, settings::Style};
+///
+/// let style = Style::ascii()
+/// .bottom('*')
+/// .intersection(' ');
+///
+/// let data = vec!["Hello", "2021"];
+/// let table = Table::new(&data).with(style).to_string();
+///
+/// println!("{}", table);
+/// ```
+///
+/// [`Table`]: crate::Table
+/// [`RawStyle`]: crate::settings::style::RawStyle
+/// [`Style::corner_top_left`]: Style::corner_top_left
+/// [`Style::left`]: Style.left
+/// [`Style::top`]: Style.function.top
+#[derive(Debug, Clone)]
+pub struct Style<T, B, L, R, H, V, HLines = HLineArray<0>, VLines = VLineArray<0>> {
+ borders: Borders<char>,
+ horizontals: HLines,
+ verticals: VLines,
+ _top: PhantomData<T>,
+ _bottom: PhantomData<B>,
+ _left: PhantomData<L>,
+ _right: PhantomData<R>,
+ _horizontal: PhantomData<H>,
+ _vertical: PhantomData<V>,
+}
+
+type HLineArray<const N: usize> = [HorizontalLine; N];
+
+type VLineArray<const N: usize> = [VerticalLine; N];
+
+/// A marker struct which is used in [`Style`].
+#[derive(Debug, Clone)]
+pub struct On;
+
+impl Style<(), (), (), (), (), (), (), ()> {
+ /// This style is a style with no styling options on,
+ ///
+ /// ```text
+ /// id destribution link
+ /// 0 Fedora https://getfedora.org/
+ /// 2 OpenSUSE https://www.opensuse.org/
+ /// 3 Endeavouros https://endeavouros.com/
+ /// ```
+ ///
+ /// Note: The cells in the example have 1-left and 1-right indent.
+ ///
+ /// This style can be used as a base style to build a custom one.
+ ///
+ /// ```rust,no_run
+ /// # use tabled::settings::Style;
+ /// let style = Style::empty()
+ /// .top('*')
+ /// .bottom('*')
+ /// .vertical('#')
+ /// .intersection_top('*');
+ /// ```
+ pub const fn empty() -> Style<(), (), (), (), (), ()> {
+ Style::new(
+ create_borders(
+ Line::empty(),
+ Line::empty(),
+ Line::empty(),
+ None,
+ None,
+ None,
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This style is analog of `empty` but with a vertical space(' ') line.
+ ///
+ /// ```text
+ /// id destribution link
+ /// 0 Fedora https://getfedora.org/
+ /// 2 OpenSUSE https://www.opensuse.org/
+ /// 3 Endeavouros https://endeavouros.com/
+ /// ```
+ pub const fn blank() -> Style<(), (), (), (), (), On> {
+ Style::new(
+ create_borders(
+ Line::empty(),
+ Line::empty(),
+ Line::empty(),
+ None,
+ None,
+ Some(' '),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This is a style which relays only on ASCII charset.
+ ///
+ /// It has horizontal and vertical lines.
+ ///
+ /// ```text
+ /// +----+--------------+---------------------------+
+ /// | id | destribution | link |
+ /// +----+--------------+---------------------------+
+ /// | 0 | Fedora | https://getfedora.org/ |
+ /// +----+--------------+---------------------------+
+ /// | 2 | OpenSUSE | https://www.opensuse.org/ |
+ /// +----+--------------+---------------------------+
+ /// | 3 | Endeavouros | https://endeavouros.com/ |
+ /// +----+--------------+---------------------------+
+ /// ```
+ pub const fn ascii() -> Style<On, On, On, On, On, On> {
+ Style::new(
+ create_borders(
+ Line::full('-', '+', '+', '+'),
+ Line::full('-', '+', '+', '+'),
+ Line::full('-', '+', '+', '+'),
+ Some('|'),
+ Some('|'),
+ Some('|'),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// `psql` style looks like a table style `PostgreSQL` uses.
+ ///
+ /// It has only 1 horizontal line which splits header.
+ /// And no left and right vertical lines.
+ ///
+ /// ```text
+ /// id | destribution | link
+ /// ----+--------------+---------------------------
+ /// 0 | Fedora | https://getfedora.org/
+ /// 2 | OpenSUSE | https://www.opensuse.org/
+ /// 3 | Endeavouros | https://endeavouros.com/
+ /// ```
+ pub const fn psql() -> Style<(), (), (), (), (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::empty(),
+ Line::empty(),
+ Line::empty(),
+ None,
+ None,
+ Some('|'),
+ ),
+ [HorizontalLine::new(1, Line::empty())
+ .main(Some('-'))
+ .intersection(Some('+'))],
+ [],
+ )
+ }
+
+ /// `markdown` style mimics a `Markdown` table style.
+ ///
+ /// ```text
+ /// | id | destribution | link |
+ /// |----|--------------|---------------------------|
+ /// | 0 | Fedora | https://getfedora.org/ |
+ /// | 2 | OpenSUSE | https://www.opensuse.org/ |
+ /// | 3 | Endeavouros | https://endeavouros.com/ |
+ /// ```
+ pub const fn markdown() -> Style<(), (), On, On, (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::empty(),
+ Line::empty(),
+ Line::empty(),
+ Some('|'),
+ Some('|'),
+ Some('|'),
+ ),
+ [HorizontalLine::new(1, Line::full('-', '|', '|', '|'))],
+ [],
+ )
+ }
+
+ /// This style is analog of [`Style::ascii`] which uses UTF-8 charset.
+ ///
+ /// It has vertical and horizontal split lines.
+ ///
+ /// ```text
+ /// ┌────┬──────────────┬───────────────────────────┐
+ /// │ id │ destribution │ link │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 0 │ Fedora │ https://getfedora.org/ │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 2 │ OpenSUSE │ https://www.opensuse.org/ │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 3 │ Endeavouros │ https://endeavouros.com/ │
+ /// └────┴──────────────┴───────────────────────────┘
+ /// ```
+ pub const fn modern() -> Style<On, On, On, On, On, On> {
+ Style::new(
+ create_borders(
+ Line::full('─', '┬', '┌', '┐'),
+ Line::full('─', '┴', '└', '┘'),
+ Line::full('─', '┼', '├', '┤'),
+ Some('│'),
+ Some('│'),
+ Some('│'),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This style looks like a [`Style::modern`] but without horozizontal lines except a header.
+ ///
+ /// Beware: It uses UTF-8 characters.
+ ///
+ /// ```text
+ /// ┌────┬──────────────┬───────────────────────────┐
+ /// │ id │ destribution │ link │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 0 │ Fedora │ https://getfedora.org/ │
+ /// │ 2 │ OpenSUSE │ https://www.opensuse.org/ │
+ /// │ 3 │ Endeavouros │ https://endeavouros.com/ │
+ /// └────┴──────────────┴───────────────────────────┘
+ /// ```
+ pub const fn sharp() -> Style<On, On, On, On, (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::full('─', '┬', '┌', '┐'),
+ Line::full('─', '┴', '└', '┘'),
+ Line::empty(),
+ Some('│'),
+ Some('│'),
+ Some('│'),
+ ),
+ [HorizontalLine::new(1, Line::full('─', '┼', '├', '┤'))],
+ [],
+ )
+ }
+
+ /// This style looks like a [`Style::sharp`] but with rounded corners.
+ ///
+ /// Beware: It uses UTF-8 characters.
+ ///
+ /// ```text
+ /// ╭────┬──────────────┬───────────────────────────╮
+ /// │ id │ destribution │ link │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 0 │ Fedora │ https://getfedora.org/ │
+ /// │ 2 │ OpenSUSE │ https://www.opensuse.org/ │
+ /// │ 3 │ Endeavouros │ https://endeavouros.com/ │
+ /// ╰────┴──────────────┴───────────────────────────╯
+ /// ```
+ pub const fn rounded() -> Style<On, On, On, On, (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::full('─', '┬', '╭', '╮'),
+ Line::full('─', '┴', '╰', '╯'),
+ Line::empty(),
+ Some('│'),
+ Some('│'),
+ Some('│'),
+ ),
+ [HorizontalLine::new(1, Line::full('─', '┼', '├', '┤'))],
+ [],
+ )
+ }
+
+ /// This style uses a chars which resembles '2 lines'.
+ ///
+ /// Beware: It uses UTF8 characters.
+ ///
+ /// ```text
+ /// ╔════╦══════════════╦═══════════════════════════╗
+ /// ║ id ║ destribution ║ link ║
+ /// ╠════╬══════════════╬═══════════════════════════╣
+ /// ║ 0 ║ Fedora ║ https://getfedora.org/ ║
+ /// ╠════╬══════════════╬═══════════════════════════╣
+ /// ║ 2 ║ OpenSUSE ║ https://www.opensuse.org/ ║
+ /// ╠════╬══════════════╬═══════════════════════════╣
+ /// ║ 3 ║ Endeavouros ║ https://endeavouros.com/ ║
+ /// ╚════╩══════════════╩═══════════════════════════╝
+ /// ```
+ pub const fn extended() -> Style<On, On, On, On, On, On> {
+ Style::new(
+ create_borders(
+ Line::full('═', '╦', '╔', '╗'),
+ Line::full('═', '╩', '╚', '╝'),
+ Line::full('═', '╬', '╠', '╣'),
+ Some('║'),
+ Some('║'),
+ Some('║'),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This is a style uses only '.' and ':' chars.
+ /// It has a vertical and horizontal split lines.
+ ///
+ /// ```text
+ /// .................................................
+ /// : id : destribution : link :
+ /// :....:..............:...........................:
+ /// : 0 : Fedora : https://getfedora.org/ :
+ /// :....:..............:...........................:
+ /// : 2 : OpenSUSE : https://www.opensuse.org/ :
+ /// :....:..............:...........................:
+ /// : 3 : Endeavouros : https://endeavouros.com/ :
+ /// :....:..............:...........................:
+ /// ```
+ pub const fn dots() -> Style<On, On, On, On, On, On> {
+ Style::new(
+ create_borders(
+ Line::full('.', '.', '.', '.'),
+ Line::full('.', ':', ':', ':'),
+ Line::full('.', ':', ':', ':'),
+ Some(':'),
+ Some(':'),
+ Some(':'),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This style is one of table views in `ReStructuredText`.
+ ///
+ /// ```text
+ /// ==== ============== ===========================
+ /// id destribution link
+ /// ==== ============== ===========================
+ /// 0 Fedora https://getfedora.org/
+ /// 2 OpenSUSE https://www.opensuse.org/
+ /// 3 Endeavouros https://endeavouros.com/
+ /// ==== ============== ===========================
+ /// ```
+ pub const fn re_structured_text() -> Style<On, On, (), (), (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::new(Some('='), Some(' '), None, None),
+ Line::new(Some('='), Some(' '), None, None),
+ Line::empty(),
+ None,
+ None,
+ Some(' '),
+ ),
+ [HorizontalLine::new(
+ 1,
+ Line::new(Some('='), Some(' '), None, None),
+ )],
+ [],
+ )
+ }
+
+ /// This is a theme analog of [`Style::rounded`], but in using ascii charset and
+ /// with no horizontal lines.
+ ///
+ /// ```text
+ /// .-----------------------------------------------.
+ /// | id | destribution | link |
+ /// | 0 | Fedora | https://getfedora.org/ |
+ /// | 2 | OpenSUSE | https://www.opensuse.org/ |
+ /// | 3 | Endeavouros | https://endeavouros.com/ |
+ /// '-----------------------------------------------'
+ /// ```
+ pub const fn ascii_rounded() -> Style<On, On, On, On, (), On> {
+ Style::new(
+ create_borders(
+ Line::full('-', '-', '.', '.'),
+ Line::full('-', '-', '\'', '\''),
+ Line::empty(),
+ Some('|'),
+ Some('|'),
+ Some('|'),
+ ),
+ [],
+ [],
+ )
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines> Style<T, B, L, R, H, V, HLines, VLines> {
+ /// Frame function returns a frame as a border.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{Table, settings::{Style, Highlight, object::Rows}};
+ ///
+ /// let data = [["10:52:19", "Hello"], ["10:52:20", "World"]];
+ /// let table = Table::new(data)
+ /// .with(Highlight::new(Rows::first(), Style::modern().get_frame()))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "┌──────────────────┐\n",
+ /// "│ 0 | 1 │\n",
+ /// "└──────────────────┘\n",
+ /// "| 10:52:19 | Hello |\n",
+ /// "+----------+-------+\n",
+ /// "| 10:52:20 | World |\n",
+ /// "+----------+-------+",
+ /// )
+ /// );
+ /// ```
+ #[cfg(feature = "std")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+ pub const fn get_frame(&self) -> super::Border {
+ let mut border = super::Border::filled(' ');
+
+ if let Some(c) = self.borders.top {
+ border = border.top(c);
+ }
+
+ if let Some(c) = self.borders.bottom {
+ border = border.bottom(c);
+ }
+
+ if let Some(c) = self.borders.left {
+ border = border.left(c);
+ }
+
+ if let Some(c) = self.borders.right {
+ border = border.right(c);
+ }
+
+ if let Some(c) = self.borders.top_left {
+ border = border.corner_top_left(c);
+ }
+
+ if let Some(c) = self.borders.bottom_left {
+ border = border.corner_bottom_left(c);
+ }
+
+ if let Some(c) = self.borders.top_right {
+ border = border.corner_top_right(c);
+ }
+
+ if let Some(c) = self.borders.bottom_right {
+ border = border.corner_bottom_right(c);
+ }
+
+ border
+ }
+
+ /// Get a [`Style`]'s default horizontal line.
+ ///
+ /// It doesn't return an overloaded line via [`Style::horizontals`].
+ ///
+ /// # Example
+ ///
+ #[cfg_attr(feature = "std", doc = "```")]
+ #[cfg_attr(not(feature = "std"), doc = "```ignore")]
+ /// use tabled::{settings::style::{Style, HorizontalLine, Line}, Table};
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", "World", i)))
+ /// .with(Style::ascii().remove_horizontal().horizontals([HorizontalLine::new(1, Style::modern().get_horizontal())]))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "+-------+-------+-----+\n",
+ /// "| &str | &str | i32 |\n",
+ /// "├───────┼───────┼─────┤\n",
+ /// "| Hello | World | 0 |\n",
+ /// "| Hello | World | 1 |\n",
+ /// "| Hello | World | 2 |\n",
+ /// "+-------+-------+-----+",
+ /// )
+ /// )
+ /// ```
+ pub const fn get_horizontal(&self) -> Line {
+ Line::new(
+ self.borders.horizontal,
+ self.borders.intersection,
+ self.borders.left_intersection,
+ self.borders.right_intersection,
+ )
+ }
+
+ /// Get a [`Style`]'s default horizontal line.
+ ///
+ /// It doesn't return an overloaded line via [`Style::verticals`].
+ ///
+ /// # Example
+ ///
+ #[cfg_attr(feature = "std", doc = "```")]
+ #[cfg_attr(not(feature = "std"), doc = "```ignore")]
+ /// use tabled::{settings::style::{Style, VerticalLine, Line}, Table};
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", "World", i)))
+ /// .with(Style::ascii().remove_horizontal().verticals([VerticalLine::new(1, Style::modern().get_vertical())]))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "+-------┬-------+-----+\n",
+ /// "| &str │ &str | i32 |\n",
+ /// "| Hello │ World | 0 |\n",
+ /// "| Hello │ World | 1 |\n",
+ /// "| Hello │ World | 2 |\n",
+ /// "+-------┴-------+-----+",
+ /// )
+ /// )
+ /// ```
+ pub const fn get_vertical(&self) -> Line {
+ Line::new(
+ self.borders.vertical,
+ self.borders.intersection,
+ self.borders.top_intersection,
+ self.borders.bottom_intersection,
+ )
+ }
+
+ /// Sets a top border.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn top(mut self, c: char) -> Style<On, B, L, R, H, V, HLines, VLines>
+ where
+ for<'a> &'a mut VLines: IntoIterator<Item = &'a mut VerticalLine>,
+ {
+ self.borders.top = Some(c);
+
+ if self.borders.has_left() {
+ self.borders.top_left = Some(c);
+ }
+
+ if self.borders.has_right() {
+ self.borders.top_right = Some(c);
+ }
+
+ if self.borders.has_vertical() {
+ self.borders.top_intersection = Some(c);
+ }
+
+ for vl in &mut self.verticals {
+ vl.line.connector1 = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a bottom border.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn bottom(mut self, c: char) -> Style<T, On, L, R, H, V, HLines, VLines>
+ where
+ for<'a> &'a mut VLines: IntoIterator<Item = &'a mut VerticalLine>,
+ {
+ self.borders.bottom = Some(c);
+
+ if self.borders.has_left() {
+ self.borders.bottom_left = Some(c);
+ }
+
+ if self.borders.has_right() {
+ self.borders.bottom_right = Some(c);
+ }
+
+ if self.borders.has_vertical() {
+ self.borders.bottom_intersection = Some(c);
+ }
+
+ for vl in &mut self.verticals {
+ vl.line.connector2 = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a left border.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn left(mut self, c: char) -> Style<T, B, On, R, H, V, HLines, VLines>
+ where
+ for<'a> &'a mut HLines: IntoIterator<Item = &'a mut HorizontalLine>,
+ {
+ self.borders.left = Some(c);
+
+ if self.borders.has_top() {
+ self.borders.top_left = Some(c);
+ }
+
+ if self.borders.has_bottom() {
+ self.borders.bottom_left = Some(c);
+ }
+
+ if self.borders.has_horizontal() {
+ self.borders.left_intersection = Some(c);
+ }
+
+ for hl in &mut self.horizontals {
+ hl.line.connector1 = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a right border.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn right(mut self, c: char) -> Style<T, B, L, On, H, V, HLines, VLines>
+ where
+ for<'a> &'a mut HLines: IntoIterator<Item = &'a mut HorizontalLine>,
+ {
+ self.borders.right = Some(c);
+
+ if self.borders.has_top() {
+ self.borders.top_right = Some(c);
+ }
+
+ if self.borders.has_bottom() {
+ self.borders.bottom_right = Some(c);
+ }
+
+ if self.borders.has_horizontal() {
+ self.borders.right_intersection = Some(c);
+ }
+
+ for hl in &mut self.horizontals {
+ hl.line.connector2 = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a horizontal split line.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn horizontal(mut self, c: char) -> Style<T, B, L, R, On, V, HLines, VLines>
+ where
+ for<'a> &'a mut VLines: IntoIterator<Item = &'a mut VerticalLine>,
+ {
+ self.borders.horizontal = Some(c);
+
+ if self.borders.has_vertical() {
+ self.borders.intersection = Some(c);
+ }
+
+ if self.borders.has_left() {
+ self.borders.left_intersection = Some(c);
+ }
+
+ if self.borders.has_right() {
+ self.borders.right_intersection = Some(c);
+ }
+
+ for vl in &mut self.verticals {
+ vl.line.intersection = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a vertical split line.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn vertical(mut self, c: char) -> Style<T, B, L, R, H, On, HLines, VLines>
+ where
+ for<'a> &'a mut HLines: IntoIterator<Item = &'a mut HorizontalLine>,
+ {
+ self.borders.vertical = Some(c);
+
+ if self.borders.has_horizontal() {
+ self.borders.intersection = Some(c);
+ }
+
+ if self.borders.has_top() {
+ self.borders.top_intersection = Some(c);
+ }
+
+ if self.borders.has_bottom() {
+ self.borders.bottom_intersection = Some(c);
+ }
+
+ for hl in &mut self.horizontals {
+ hl.line.intersection = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Set border horizontal lines.
+ ///
+ /// # Example
+ ///
+ #[cfg_attr(feature = "derive", doc = "```")]
+ #[cfg_attr(not(feature = "derive"), doc = "```ignore")]
+ /// use tabled::{settings::style::{Style, HorizontalLine, Line}, Table};
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", i)))
+ /// .with(Style::rounded().horizontals((1..4).map(|i| HorizontalLine::new(i, Line::filled('#')))))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "╭───────┬─────╮\n",
+ /// "│ &str │ i32 │\n",
+ /// "###############\n",
+ /// "│ Hello │ 0 │\n",
+ /// "###############\n",
+ /// "│ Hello │ 1 │\n",
+ /// "###############\n",
+ /// "│ Hello │ 2 │\n",
+ /// "╰───────┴─────╯",
+ /// )
+ /// )
+ /// ```
+ pub fn horizontals<NewLines>(self, lines: NewLines) -> Style<T, B, L, R, H, V, NewLines, VLines>
+ where
+ NewLines: IntoIterator<Item = HorizontalLine> + Clone,
+ {
+ Style::new(self.borders, lines, self.verticals)
+ }
+
+ /// Set border vertical lines.
+ ///
+ /// # Example
+ ///
+ #[cfg_attr(feature = "derive", doc = "```")]
+ #[cfg_attr(not(feature = "derive"), doc = "```ignore")]
+ /// use tabled::{Table, settings::style::{Style, VerticalLine, Line}};
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", i)))
+ /// .with(Style::rounded().verticals((0..3).map(|i| VerticalLine::new(i, Line::filled('#')))))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "#───────#─────#\n",
+ /// "# &str # i32 #\n",
+ /// "├───────┼─────┤\n",
+ /// "# Hello # 0 #\n",
+ /// "# Hello # 1 #\n",
+ /// "# Hello # 2 #\n",
+ /// "#───────#─────#",
+ /// )
+ /// )
+ /// ```
+ pub fn verticals<NewLines>(self, lines: NewLines) -> Style<T, B, L, R, H, V, HLines, NewLines>
+ where
+ NewLines: IntoIterator<Item = VerticalLine> + Clone,
+ {
+ Style::new(self.borders, self.horizontals, lines)
+ }
+
+ /// Removes all horizontal lines set by [`Style::horizontals`]
+ pub fn remove_horizontals(self) -> Style<T, B, L, R, H, V, HLineArray<0>, VLines> {
+ Style::new(self.borders, [], self.verticals)
+ }
+
+ /// Removes all verticals lines set by [`Style::verticals`]
+ pub fn remove_verticals(self) -> Style<T, B, L, R, H, V, HLines, VLineArray<0>> {
+ Style::new(self.borders, self.horizontals, [])
+ }
+}
+
+impl<B, R, H, V, HLines, VLines> Style<On, B, On, R, H, V, HLines, VLines> {
+ /// Sets a top left corner.
+ pub fn corner_top_left(mut self, c: char) -> Self {
+ self.borders.top_left = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<B, L, H, V, HLines, VLines> Style<On, B, L, On, H, V, HLines, VLines> {
+ /// Sets a top right corner.
+ pub fn corner_top_right(mut self, c: char) -> Self {
+ self.borders.top_right = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, L, H, V, HLines, VLines> Style<T, On, L, On, H, V, HLines, VLines> {
+ /// Sets a bottom right corner.
+ pub fn corner_bottom_right(mut self, c: char) -> Self {
+ self.borders.bottom_right = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, R, H, V, HLines, VLines> Style<T, On, On, R, H, V, HLines, VLines> {
+ /// Sets a bottom left corner.
+ pub fn corner_bottom_left(mut self, c: char) -> Self {
+ self.borders.bottom_left = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, B, R, V, HLines, VLines> Style<T, B, On, R, On, V, HLines, VLines> {
+ /// Sets a left intersection char.
+ pub fn intersection_left(mut self, c: char) -> Self {
+ self.borders.left_intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, B, L, V, HLines, VLines> Style<T, B, L, On, On, V, HLines, VLines> {
+ /// Sets a right intersection char.
+ pub fn intersection_right(mut self, c: char) -> Self {
+ self.borders.right_intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<B, L, R, H, HLines, VLines> Style<On, B, L, R, H, On, HLines, VLines> {
+ /// Sets a top intersection char.
+ pub fn intersection_top(mut self, c: char) -> Self {
+ self.borders.top_intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, L, R, H, HLines, VLines> Style<T, On, L, R, H, On, HLines, VLines> {
+ /// Sets a bottom intersection char.
+ pub fn intersection_bottom(mut self, c: char) -> Self {
+ self.borders.bottom_intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, B, L, R, HLines, VLines> Style<T, B, L, R, On, On, HLines, VLines> {
+ /// Sets an inner intersection char.
+ /// A char between horizontal and vertical split lines.
+ pub fn intersection(mut self, c: char) -> Self {
+ self.borders.intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<B, L, R, H, V, HLines, VLines> Style<On, B, L, R, H, V, HLines, VLines> {
+ /// Removes top border.
+ pub fn remove_top(
+ mut self,
+ ) -> Style<(), B, L, R, H, V, HLines, VerticalLineIter<VLines::IntoIter>>
+ where
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+ {
+ self.borders.top = None;
+ self.borders.top_intersection = None;
+ self.borders.top_left = None;
+ self.borders.top_right = None;
+
+ let iter = VerticalLineIter::new(self.verticals.into_iter(), false, true, false);
+ Style::new(self.borders, self.horizontals, iter)
+ }
+}
+
+impl<T, L, R, H, V, HLines, VLines> Style<T, On, L, R, H, V, HLines, VLines> {
+ /// Removes bottom border.
+ pub fn remove_bottom(
+ mut self,
+ ) -> Style<T, (), L, R, H, V, HLines, VerticalLineIter<VLines::IntoIter>>
+ where
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+ {
+ self.borders.bottom = None;
+ self.borders.bottom_intersection = None;
+ self.borders.bottom_left = None;
+ self.borders.bottom_right = None;
+
+ let iter = VerticalLineIter::new(self.verticals.into_iter(), false, false, true);
+ Style::new(self.borders, self.horizontals, iter)
+ }
+}
+
+impl<T, B, R, H, V, HLines, VLines> Style<T, B, On, R, H, V, HLines, VLines> {
+ /// Removes left border.
+ pub fn remove_left(
+ mut self,
+ ) -> Style<T, B, (), R, H, V, HorizontalLineIter<HLines::IntoIter>, VLines>
+ where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ {
+ self.borders.left = None;
+ self.borders.left_intersection = None;
+ self.borders.top_left = None;
+ self.borders.bottom_left = None;
+
+ let iter = HorizontalLineIter::new(self.horizontals.into_iter(), false, true, false);
+ Style::new(self.borders, iter, self.verticals)
+ }
+}
+
+impl<T, B, L, H, V, HLines, VLines> Style<T, B, L, On, H, V, HLines, VLines> {
+ /// Removes right border.
+ pub fn remove_right(
+ mut self,
+ ) -> Style<T, B, L, (), H, V, HorizontalLineIter<HLines::IntoIter>, VLines>
+ where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ {
+ self.borders.right = None;
+ self.borders.right_intersection = None;
+ self.borders.top_right = None;
+ self.borders.bottom_right = None;
+
+ let iter = HorizontalLineIter::new(self.horizontals.into_iter(), false, false, true);
+ Style::new(self.borders, iter, self.verticals)
+ }
+}
+
+impl<T, B, L, R, V, HLines, VLines> Style<T, B, L, R, On, V, HLines, VLines> {
+ /// Removes horizontal split lines.
+ ///
+ /// Not including custom split lines.
+ pub fn remove_horizontal(
+ mut self,
+ ) -> Style<T, B, L, R, (), V, HLines, VerticalLineIter<VLines::IntoIter>>
+ where
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+ {
+ self.borders.horizontal = None;
+ self.borders.left_intersection = None;
+ self.borders.right_intersection = None;
+ self.borders.intersection = None;
+
+ let iter = VerticalLineIter::new(self.verticals.into_iter(), true, false, false);
+ Style::new(self.borders, self.horizontals, iter)
+ }
+}
+
+impl<T, B, L, R, H, HLines, VLines> Style<T, B, L, R, H, On, HLines, VLines> {
+ /// Removes vertical split lines.
+ pub fn remove_vertical(
+ mut self,
+ ) -> Style<T, B, L, R, H, (), HorizontalLineIter<HLines::IntoIter>, VLines>
+ where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ {
+ self.borders.vertical = None;
+ self.borders.top_intersection = None;
+ self.borders.bottom_intersection = None;
+ self.borders.intersection = None;
+
+ let iter = HorizontalLineIter::new(self.horizontals.into_iter(), true, false, false);
+ Style::new(self.borders, iter, self.verticals)
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines> Style<T, B, L, R, H, V, HLines, VLines> {
+ const fn new(borders: Borders<char>, horizontals: HLines, verticals: VLines) -> Self {
+ Self {
+ borders,
+ horizontals,
+ verticals,
+ _top: PhantomData,
+ _bottom: PhantomData,
+ _left: PhantomData,
+ _right: PhantomData,
+ _horizontal: PhantomData,
+ _vertical: PhantomData,
+ }
+ }
+
+ /// Return borders of a table.
+ pub const fn get_borders(&self) -> &Borders<char> {
+ &self.borders
+ }
+
+ /// Return custom horizontals which were set.
+ pub const fn get_horizontals(&self) -> &HLines {
+ &self.horizontals
+ }
+
+ /// Return custom verticals which were set.
+ pub const fn get_verticals(&self) -> &VLines {
+ &self.verticals
+ }
+}
+
+#[cfg(feature = "std")]
+impl<T, B, L, R, H, V, HLines, VLines, I, D> TableOption<I, D, ColoredConfig>
+ for Style<T, B, L, R, H, V, HLines, VLines>
+where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+{
+ fn change(self, records: &mut I, cfg: &mut ColoredConfig, dimension: &mut D) {
+ cfg.clear_theme();
+
+ cfg.set_borders(self.borders);
+
+ for hl in self.horizontals {
+ hl.change(records, cfg, dimension);
+ }
+
+ for vl in self.verticals {
+ vl.change(records, cfg, dimension);
+ }
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines, I, D> TableOption<I, D, CompactConfig>
+ for Style<T, B, L, R, H, V, HLines, VLines>
+where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+{
+ fn change(self, records: &mut I, cfg: &mut CompactConfig, dimension: &mut D) {
+ *cfg = cfg.set_borders(self.borders);
+
+ let first_line = self.horizontals.into_iter().next();
+ if let Some(line) = first_line {
+ line.change(records, cfg, dimension);
+ }
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines, I, D> TableOption<I, D, CompactMultilineConfig>
+ for Style<T, B, L, R, H, V, HLines, VLines>
+where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+{
+ fn change(self, records: &mut I, cfg: &mut CompactMultilineConfig, dimension: &mut D) {
+ self.change(records, cfg.as_mut(), dimension)
+ }
+}
+
+/// An iterator which limits [`Line`] influence on iterations over lines for in [`Style`].
+#[derive(Debug, Clone)]
+pub struct HorizontalLineIter<I> {
+ iter: I,
+ intersection: bool,
+ left: bool,
+ right: bool,
+}
+
+impl<I> HorizontalLineIter<I> {
+ fn new(iter: I, intersection: bool, left: bool, right: bool) -> Self {
+ Self {
+ iter,
+ intersection,
+ left,
+ right,
+ }
+ }
+}
+
+impl<I> Iterator for HorizontalLineIter<I>
+where
+ I: Iterator<Item = HorizontalLine>,
+{
+ type Item = HorizontalLine;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let mut hl = self.iter.next()?;
+
+ if self.intersection {
+ hl.line.intersection = None;
+ }
+
+ if self.left {
+ hl.line.connector1 = None;
+ }
+
+ if self.right {
+ hl.line.connector2 = None;
+ }
+
+ Some(hl)
+ }
+}
+
+/// An iterator which limits [`Line`] influence on iterations over lines for in [`Style`].
+#[derive(Debug, Clone)]
+pub struct VerticalLineIter<I> {
+ iter: I,
+ intersection: bool,
+ top: bool,
+ bottom: bool,
+}
+
+impl<I> VerticalLineIter<I> {
+ fn new(iter: I, intersection: bool, top: bool, bottom: bool) -> Self {
+ Self {
+ iter,
+ intersection,
+ top,
+ bottom,
+ }
+ }
+}
+
+impl<I> Iterator for VerticalLineIter<I>
+where
+ I: Iterator<Item = VerticalLine>,
+{
+ type Item = VerticalLine;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let mut hl = self.iter.next()?;
+
+ if self.intersection {
+ hl.line.intersection = None;
+ }
+
+ if self.top {
+ hl.line.connector1 = None;
+ }
+
+ if self.bottom {
+ hl.line.connector2 = None;
+ }
+
+ Some(hl)
+ }
+}
+
+const fn create_borders(
+ top: Line,
+ bottom: Line,
+ horizontal: Line,
+ left: Option<char>,
+ right: Option<char>,
+ vertical: Option<char>,
+) -> Borders<char> {
+ Borders {
+ top: top.main,
+ bottom: bottom.main,
+ top_left: top.connector1,
+ top_right: top.connector2,
+ bottom_left: bottom.connector1,
+ bottom_right: bottom.connector2,
+ top_intersection: top.intersection,
+ bottom_intersection: bottom.intersection,
+ left_intersection: horizontal.connector1,
+ right_intersection: horizontal.connector2,
+ horizontal: horizontal.main,
+ intersection: horizontal.intersection,
+ left,
+ right,
+ vertical,
+ }
+}
diff --git a/vendor/tabled/src/settings/style/horizontal_line.rs b/vendor/tabled/src/settings/style/horizontal_line.rs
new file mode 100644
index 000000000..bbcdc3fb4
--- /dev/null
+++ b/vendor/tabled/src/settings/style/horizontal_line.rs
@@ -0,0 +1,69 @@
+use crate::{
+ grid::config::{CompactConfig, CompactMultilineConfig},
+ settings::TableOption,
+};
+
+#[cfg(feature = "std")]
+use crate::grid::config::{ColoredConfig, HorizontalLine as GridLine};
+
+use super::Line;
+
+/// A horizontal split line which can be used to set a border.
+#[cfg_attr(not(feature = "std"), allow(dead_code))]
+#[derive(Debug, Clone)]
+pub struct HorizontalLine {
+ pub(super) index: usize,
+ pub(super) line: Line,
+}
+
+impl HorizontalLine {
+ /// Creates a new horizontal split line.
+ pub const fn new(index: usize, line: Line) -> Self {
+ Self { index, line }
+ }
+
+ /// Sets a horizontal character.
+ pub const fn main(mut self, c: Option<char>) -> Self {
+ self.line.main = c;
+ self
+ }
+
+ /// Sets a vertical intersection character.
+ pub const fn intersection(mut self, c: Option<char>) -> Self {
+ self.line.intersection = c;
+ self
+ }
+
+ /// Sets a left character.
+ pub const fn left(mut self, c: Option<char>) -> Self {
+ self.line.connector1 = c;
+ self
+ }
+
+ /// Sets a right character.
+ pub const fn right(mut self, c: Option<char>) -> Self {
+ self.line.connector2 = c;
+ self
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, D> TableOption<R, D, ColoredConfig> for HorizontalLine {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ cfg.insert_horizontal_line(self.index, GridLine::from(self.line))
+ }
+}
+
+impl<R, D> TableOption<R, D, CompactConfig> for HorizontalLine {
+ fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
+ if self.index == 1 {
+ *cfg = cfg.set_first_horizontal_line(papergrid::config::Line::from(self.line));
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, CompactMultilineConfig> for HorizontalLine {
+ fn change(self, records: &mut R, cfg: &mut CompactMultilineConfig, dimension: &mut D) {
+ self.change(records, cfg.as_mut(), dimension)
+ }
+}
diff --git a/vendor/tabled/src/settings/style/line.rs b/vendor/tabled/src/settings/style/line.rs
new file mode 100644
index 000000000..10b3895c4
--- /dev/null
+++ b/vendor/tabled/src/settings/style/line.rs
@@ -0,0 +1,91 @@
+#[cfg(feature = "std")]
+use crate::grid::config::{HorizontalLine, VerticalLine};
+
+/// The structure represent a vertical or horizontal line.
+#[derive(Debug, Default, Clone, Copy)]
+pub struct Line {
+ pub(crate) main: Option<char>,
+ pub(crate) intersection: Option<char>,
+ pub(crate) connector1: Option<char>,
+ pub(crate) connector2: Option<char>,
+}
+
+impl Line {
+ /// Creates a new [`Line`] object.
+ pub const fn new(
+ main: Option<char>,
+ intersection: Option<char>,
+ connector1: Option<char>,
+ connector2: Option<char>,
+ ) -> Self {
+ Self {
+ main,
+ intersection,
+ connector1,
+ connector2,
+ }
+ }
+
+ /// Creates a new [`Line`] object with all chars set.
+ pub const fn full(main: char, intersection: char, connector1: char, connector2: char) -> Self {
+ Self::new(
+ Some(main),
+ Some(intersection),
+ Some(connector1),
+ Some(connector2),
+ )
+ }
+
+ /// Creates a new [`Line`] object with all chars set to the provided one.
+ pub const fn filled(c: char) -> Self {
+ Self::full(c, c, c, c)
+ }
+
+ /// Creates a new [`Line`] object with all chars not set.
+ pub const fn empty() -> Self {
+ Self::new(None, None, None, None)
+ }
+
+ /// Checks if the line has nothing set.
+ pub const fn is_empty(&self) -> bool {
+ self.main.is_none()
+ && self.intersection.is_none()
+ && self.connector1.is_none()
+ && self.connector2.is_none()
+ }
+}
+
+#[cfg(feature = "std")]
+impl From<Line> for HorizontalLine {
+ fn from(l: Line) -> Self {
+ Self {
+ main: l.main,
+ intersection: l.intersection,
+ left: l.connector1,
+ right: l.connector2,
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl From<Line> for VerticalLine {
+ fn from(l: Line) -> Self {
+ Self {
+ main: l.main,
+ intersection: l.intersection,
+ top: l.connector1,
+ bottom: l.connector2,
+ }
+ }
+}
+
+impl From<Line> for papergrid::config::Line<char> {
+ fn from(l: Line) -> Self {
+ Self {
+ main: l.main.unwrap_or(' '),
+ intersection: l.intersection,
+ connect1: l.connector1,
+ connect2: l.connector2,
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/style/mod.rs b/vendor/tabled/src/settings/style/mod.rs
new file mode 100644
index 000000000..b8d7f688d
--- /dev/null
+++ b/vendor/tabled/src/settings/style/mod.rs
@@ -0,0 +1,127 @@
+//! This module contains a list of primitives which can be applied to change [`Table`] style.
+//!
+//! ## [`Style`]
+//!
+//! It is responsible for a table border style.
+//! An individual cell border can be set by [`Border`].
+//!
+//! ### Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::Style};
+//!
+//! let data = vec!["Hello", "2022"];
+//! let mut table = Table::new(&data);
+//! table.with(Style::psql());
+//!
+//! assert_eq!(
+//! table.to_string(),
+//! concat!(
+//! " &str \n",
+//! "-------\n",
+//! " Hello \n",
+//! " 2022 ",
+//! )
+//! )
+//! ```
+//!
+//! ## [`BorderText`]
+//!
+//! It's used to override a border with a custom text.
+//!
+//! ### Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::style::{BorderText, Style}};
+//!
+//! let data = vec!["Hello", "2022"];
+//! let table = Table::new(&data)
+//! .with(Style::psql())
+//! .with(BorderText::new("Santa").horizontal(1))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! " &str \n",
+//! "Santa--\n",
+//! " Hello \n",
+//! " 2022 ",
+//! )
+//! )
+//! ```
+//!
+//! ## [`Border`]
+//!
+//! [`Border`] can be used to modify cell's borders.
+//!
+//! It's possible to set a collored border when `color` feature is on.
+//!
+//! ### Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::{Modify, Style}};
+//!
+//! let data = vec!["Hello", "2022"];
+//! let table = Table::new(&data)
+//! .with(Style::psql())
+//! .with(Modify::new((0, 0)).with(Style::modern().get_frame()))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "┌───────┐\n",
+//! "│ &str │\n",
+//! "└───────┘\n",
+//! " Hello \n",
+//! " 2022 ",
+//! )
+//! )
+//! ```
+//!
+//! ## [`RawStyle`]
+//!
+//! A different representation of [`Style`].
+//! With no checks in place.
+//!
+//! It also contains a list of types to support colors.
+//!
+//! [`Table`]: crate::Table
+//! [`BorderText`]: crate::settings::style::BorderText
+//! [`RawStyle`]: crate::settings::style::RawStyle
+
+#[cfg(feature = "std")]
+mod border;
+#[cfg(feature = "std")]
+mod border_char;
+#[cfg(feature = "std")]
+mod border_color;
+#[cfg(feature = "std")]
+mod border_text;
+#[cfg(feature = "std")]
+mod offset;
+#[cfg(feature = "std")]
+mod raw_style;
+#[cfg(feature = "std")]
+mod span_border_correction;
+
+mod builder;
+mod horizontal_line;
+mod line;
+mod vertical_line;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use self::{
+ border::Border, border_char::BorderChar, border_color::BorderColor, border_text::BorderText,
+ offset::Offset, raw_style::RawStyle, span_border_correction::BorderSpanCorrection,
+};
+
+pub use builder::{HorizontalLineIter, On, Style, VerticalLineIter};
+pub use horizontal_line::HorizontalLine;
+pub use line::Line;
+pub use vertical_line::VerticalLine;
diff --git a/vendor/tabled/src/settings/style/offset.rs b/vendor/tabled/src/settings/style/offset.rs
new file mode 100644
index 000000000..3f8a4f85e
--- /dev/null
+++ b/vendor/tabled/src/settings/style/offset.rs
@@ -0,0 +1,19 @@
+use crate::grid::config;
+
+/// The structure represents an offset in a text.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum Offset {
+ /// An offset from the start.
+ Begin(usize),
+ /// An offset from the end.
+ End(usize),
+}
+
+impl From<Offset> for config::Offset {
+ fn from(o: Offset) -> Self {
+ match o {
+ Offset::Begin(i) => config::Offset::Begin(i),
+ Offset::End(i) => config::Offset::End(i),
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/style/raw_style.rs b/vendor/tabled/src/settings/style/raw_style.rs
new file mode 100644
index 000000000..eedb48e79
--- /dev/null
+++ b/vendor/tabled/src/settings/style/raw_style.rs
@@ -0,0 +1,438 @@
+//! This module contains [`RawStyle`] structure, which is analogues to [`Style`] but not generic,
+//! so sometimes it can be used more conviently.
+
+use std::collections::HashMap;
+
+use crate::{
+ grid::{color::AnsiColor, config, config::Borders, config::ColoredConfig, records::Records},
+ settings::{Color, TableOption},
+};
+
+use super::{Border, HorizontalLine, Line, Style, VerticalLine};
+
+/// A raw style data, which can be produced safely from [`Style`].
+///
+/// It can be useful in order to not have a generics and be able to use it as a variable more conveniently.
+#[derive(Default, Debug, Clone)]
+pub struct RawStyle {
+ borders: Borders<char>,
+ colors: Borders<AnsiColor<'static>>,
+ horizontals: HashMap<usize, Line>,
+ verticals: HashMap<usize, Line>,
+}
+
+impl RawStyle {
+ /// Set a top border character.
+ pub fn set_top(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.top = s;
+ self
+ }
+
+ /// Set a top border color.
+ pub fn set_color_top(&mut self, color: Color) -> &mut Self {
+ self.colors.top = Some(color.into());
+ self
+ }
+
+ /// Set a bottom border character.
+ pub fn set_bottom(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.bottom = s;
+ self
+ }
+
+ /// Set a bottom border color.
+ pub fn set_color_bottom(&mut self, color: Color) -> &mut Self {
+ self.colors.bottom = Some(color.into());
+ self
+ }
+
+ /// Set a left border character.
+ pub fn set_left(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.left = s;
+ self
+ }
+
+ /// Set a left border color.
+ pub fn set_color_left(&mut self, color: Color) -> &mut Self {
+ self.colors.left = Some(color.into());
+ self
+ }
+
+ /// Set a right border character.
+ pub fn set_right(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.right = s;
+ self
+ }
+
+ /// Set a right border color.
+ pub fn set_color_right(&mut self, color: Color) -> &mut Self {
+ self.colors.right = Some(color.into());
+ self
+ }
+
+ /// Set a top intersection character.
+ pub fn set_intersection_top(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.top_intersection = s;
+ self
+ }
+
+ /// Set a top intersection color.
+ pub fn set_color_intersection_top(&mut self, color: Color) -> &mut Self {
+ self.colors.top_intersection = Some(color.into());
+ self
+ }
+
+ /// Set a bottom intersection character.
+ pub fn set_intersection_bottom(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.bottom_intersection = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_intersection_bottom(&mut self, color: Color) -> &mut Self {
+ self.colors.bottom_intersection = Some(color.into());
+ self
+ }
+
+ /// Set a left split character.
+ pub fn set_intersection_left(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.left_intersection = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_intersection_left(&mut self, color: Color) -> &mut Self {
+ self.colors.left_intersection = Some(color.into());
+ self
+ }
+
+ /// Set a right split character.
+ pub fn set_intersection_right(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.right_intersection = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_intersection_right(&mut self, color: Color) -> &mut Self {
+ self.colors.right_intersection = Some(color.into());
+ self
+ }
+
+ /// Set an internal character.
+ pub fn set_intersection(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.intersection = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_intersection(&mut self, color: Color) -> &mut Self {
+ self.colors.intersection = Some(color.into());
+ self
+ }
+
+ /// Set a vertical character.
+ pub fn set_vertical(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.vertical = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_vertical(&mut self, color: Color) -> &mut Self {
+ self.colors.vertical = Some(color.into());
+ self
+ }
+
+ /// Set a horizontal character.
+ pub fn set_horizontal(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.horizontal = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_horizontal(&mut self, color: Color) -> &mut Self {
+ self.colors.horizontal = Some(color.into());
+ self
+ }
+
+ /// Set a character for a top left corner.
+ pub fn set_corner_top_left(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.top_left = s;
+ self
+ }
+ /// Set a bottom intersection color.
+ pub fn set_color_corner_top_left(&mut self, color: Color) -> &mut Self {
+ self.colors.top_left = Some(color.into());
+ self
+ }
+
+ /// Set a character for a top right corner.
+ pub fn set_corner_top_right(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.top_right = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_corner_top_right(&mut self, color: Color) -> &mut Self {
+ self.colors.top_right = Some(color.into());
+ self
+ }
+
+ /// Set a character for a bottom left corner.
+ pub fn set_corner_bottom_left(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.bottom_left = s;
+ self
+ }
+ /// Set a bottom intersection color.
+ pub fn set_color_corner_bottom_left(&mut self, color: Color) -> &mut Self {
+ self.colors.bottom_left = Some(color.into());
+ self
+ }
+
+ /// Set a character for a bottom right corner.
+ pub fn set_corner_bottom_right(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.bottom_right = s;
+ self
+ }
+ /// Set a bottom intersection color.
+ pub fn set_color_corner_bottom_right(&mut self, color: Color) -> &mut Self {
+ self.colors.bottom_right = Some(color.into());
+ self
+ }
+
+ /// Set horizontal border lines.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use std::collections::HashMap;
+ /// use tabled::{Table, settings::style::{Style, Line, RawStyle}};
+ ///
+ /// let mut style = RawStyle::from(Style::re_structured_text());
+ ///
+ /// let mut lines = HashMap::new();
+ /// lines.insert(1, Style::extended().get_horizontal());
+ /// style.set_horizontals(lines);
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", i)))
+ /// .with(style)
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// " ======= ===== \n",
+ /// " &str i32 \n",
+ /// "╠═══════╬═════╣\n",
+ /// " Hello 0 \n",
+ /// " Hello 1 \n",
+ /// " Hello 2 \n",
+ /// " ======= ===== ",
+ /// ),
+ /// )
+ /// ```
+ pub fn set_horizontals(&mut self, lines: HashMap<usize, Line>) -> &mut Self {
+ self.horizontals = lines;
+ self
+ }
+
+ /// Insert a horizontal line to a specific row location.
+ pub fn insert_horizontal(&mut self, row: usize, line: Line) -> &mut Self {
+ let _ = self.horizontals.insert(row, line);
+ self
+ }
+
+ /// Insert a horizontal line to a specific row location.
+ pub fn get_horizontal(&self, row: usize) -> Option<Line> {
+ self.horizontals.get(&row).cloned()
+ }
+
+ /// Set vertical border lines.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use std::collections::HashMap;
+ /// use tabled::{Table, settings::style::{Style, Line, RawStyle}};
+ ///
+ /// let mut style = RawStyle::from(Style::re_structured_text());
+ ///
+ /// let mut lines = HashMap::new();
+ /// lines.insert(1, Style::extended().get_horizontal());
+ /// style.set_verticals(lines);
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", i)))
+ /// .with(style)
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "=======╠=====\n",
+ /// " &str ═ i32 \n",
+ /// "======= =====\n",
+ /// " Hello ═ 0 \n",
+ /// " Hello ═ 1 \n",
+ /// " Hello ═ 2 \n",
+ /// "=======╣=====",
+ /// ),
+ /// )
+ /// ```
+ pub fn set_verticals(&mut self, lines: HashMap<usize, Line>) -> &mut Self {
+ self.verticals = lines;
+ self
+ }
+
+ /// Insert a vertical line into specific column location.
+ pub fn insert_vertical(&mut self, column: usize, line: Line) -> &mut Self {
+ let _ = self.verticals.insert(column, line);
+ self
+ }
+
+ /// Get a left char.
+ pub fn get_left(&self) -> Option<char> {
+ self.borders.left
+ }
+
+ /// Get a left intersection char.
+ pub fn get_left_intersection(&self) -> Option<char> {
+ self.borders.left_intersection
+ }
+
+ /// Get a right char.
+ pub fn get_right(&self) -> Option<char> {
+ self.borders.right
+ }
+
+ /// Get a right intersection char.
+ pub fn get_right_intersection(&self) -> Option<char> {
+ self.borders.right_intersection
+ }
+
+ /// Get a top char.
+ pub fn get_top(&self) -> Option<char> {
+ self.borders.top
+ }
+
+ /// Get a top left char.
+ pub fn get_top_left(&self) -> Option<char> {
+ self.borders.top_left
+ }
+
+ /// Get a top right char.
+ pub fn get_top_right(&self) -> Option<char> {
+ self.borders.top_right
+ }
+
+ /// Get a top intersection char.
+ pub fn get_top_intersection(&self) -> Option<char> {
+ self.borders.top_intersection
+ }
+
+ /// Get a bottom intersection char.
+ pub fn get_bottom(&self) -> Option<char> {
+ self.borders.bottom
+ }
+
+ /// Get a bottom intersection char.
+ pub fn get_bottom_left(&self) -> Option<char> {
+ self.borders.bottom_left
+ }
+
+ /// Get a bottom intersection char.
+ pub fn get_bottom_right(&self) -> Option<char> {
+ self.borders.bottom_right
+ }
+
+ /// Get a bottom intersection char.
+ pub fn get_bottom_intersection(&self) -> Option<char> {
+ self.borders.bottom_intersection
+ }
+
+ /// Returns an outer border of the style.
+ pub fn get_frame(&self) -> Border {
+ Border::from(crate::grid::config::Border {
+ top: self.borders.top,
+ bottom: self.borders.bottom,
+ left: self.borders.left,
+ right: self.borders.right,
+ left_top_corner: self.borders.top_left,
+ right_top_corner: self.borders.top_right,
+ left_bottom_corner: self.borders.bottom_left,
+ right_bottom_corner: self.borders.bottom_right,
+ })
+ }
+
+ /// Returns an general borders configuration of the style.
+ pub fn get_borders(&self) -> Borders<char> {
+ self.borders
+ }
+}
+
+impl From<Borders<char>> for RawStyle {
+ fn from(borders: Borders<char>) -> Self {
+ Self {
+ borders,
+ horizontals: HashMap::new(),
+ verticals: HashMap::new(),
+ colors: Borders::default(),
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for RawStyle
+where
+ R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dimension: &mut D) {
+ (&self).change(records, cfg, dimension)
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for &RawStyle {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ cfg.clear_theme();
+
+ cfg.set_borders(self.borders);
+
+ for (&row, line) in &self.horizontals {
+ cfg.insert_horizontal_line(row, config::HorizontalLine::from(*line));
+ }
+
+ for (&col, line) in &self.verticals {
+ cfg.insert_vertical_line(col, config::VerticalLine::from(*line));
+ }
+
+ if !self.colors.is_empty() {
+ cfg.set_borders_color(self.colors.clone());
+ }
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines> From<Style<T, B, L, R, H, V, HLines, VLines>> for RawStyle
+where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+{
+ fn from(style: Style<T, B, L, R, H, V, HLines, VLines>) -> Self {
+ let horizontals = style
+ .get_horizontals()
+ .clone()
+ .into_iter()
+ .map(|hr| (hr.index, hr.line))
+ .collect();
+
+ let verticals = style
+ .get_verticals()
+ .clone()
+ .into_iter()
+ .map(|hr| (hr.index, hr.line))
+ .collect();
+
+ Self {
+ borders: *style.get_borders(),
+ horizontals,
+ verticals,
+ colors: Borders::default(),
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/style/span_border_correction.rs b/vendor/tabled/src/settings/style/span_border_correction.rs
new file mode 100644
index 000000000..665a12788
--- /dev/null
+++ b/vendor/tabled/src/settings/style/span_border_correction.rs
@@ -0,0 +1,216 @@
+//! This module contains [`BorderSpanCorrection`] structure, which can be useful when [`Span`] is used, and
+//! you want to fix the intersections symbols which are left intact by default.
+//!
+//! [`Span`]: crate::settings::span::Span
+
+use crate::{
+ grid::{
+ config::{ColoredConfig, Position, SpannedConfig},
+ records::{ExactRecords, Records},
+ },
+ settings::TableOption,
+};
+
+/// A correctness function of style for [`Table`] which has [`Span`]s.
+///
+/// Try to fix the style when table contains spans.
+///
+/// By default [`Style`] doesn't implies any logic to better render split lines when
+/// [`Span`] is used.
+///
+/// So this function can be used to set the split lines in regard of spans used.
+///
+/// # Example
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{
+/// Modify, style::{Style, BorderSpanCorrection},
+/// Format, Span, object::Cell
+/// }
+/// };
+///
+/// let data = vec![
+/// ("09", "June", "2022"),
+/// ("10", "July", "2022"),
+/// ];
+///
+/// let mut table = Table::new(data);
+/// table.with(Modify::new((0, 0)).with("date").with(Span::column(3)));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// concat!(
+/// "+----+------+------+\n",
+/// "| date |\n",
+/// "+----+------+------+\n",
+/// "| 09 | June | 2022 |\n",
+/// "+----+------+------+\n",
+/// "| 10 | July | 2022 |\n",
+/// "+----+------+------+",
+/// )
+/// );
+///
+/// table.with(BorderSpanCorrection);
+///
+/// assert_eq!(
+/// table.to_string(),
+/// concat!(
+/// "+------------------+\n",
+/// "| date |\n",
+/// "+----+------+------+\n",
+/// "| 09 | June | 2022 |\n",
+/// "+----+------+------+\n",
+/// "| 10 | July | 2022 |\n",
+/// "+----+------+------+",
+/// )
+/// );
+/// ```
+/// See [`BorderSpanCorrection`].
+///
+/// [`Table`]: crate::Table
+/// [`Span`]: crate::settings::span::Span
+/// [`Style`]: crate::settings::Style
+/// [`Style::correct_spans`]: crate::settings::style::BorderSpanCorrection
+#[derive(Debug)]
+pub struct BorderSpanCorrection;
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderSpanCorrection
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let shape = (records.count_rows(), records.count_columns());
+ correct_span_styles(cfg, shape);
+ }
+}
+
+fn correct_span_styles(cfg: &mut SpannedConfig, shape: (usize, usize)) {
+ for ((row, c), span) in cfg.get_column_spans() {
+ for col in c..c + span {
+ if col == 0 {
+ continue;
+ }
+
+ let is_first = col == c;
+ let has_up = row > 0 && has_left(cfg, (row - 1, col), shape);
+ let has_down = row + 1 < shape.0 && has_left(cfg, (row + 1, col), shape);
+
+ let borders = cfg.get_borders();
+
+ let mut border = cfg.get_border((row, col), shape);
+
+ let has_top_border = border.left_top_corner.is_some() && border.top.is_some();
+ if has_top_border {
+ if has_up && is_first {
+ border.left_top_corner = borders.intersection;
+ } else if has_up {
+ border.left_top_corner = borders.bottom_intersection;
+ } else if is_first {
+ border.left_top_corner = borders.top_intersection;
+ } else {
+ border.left_top_corner = border.top;
+ }
+ }
+
+ let has_bottom_border = border.left_bottom_corner.is_some() && border.bottom.is_some();
+ if has_bottom_border {
+ if has_down && is_first {
+ border.left_bottom_corner = borders.intersection;
+ } else if has_down {
+ border.left_bottom_corner = borders.top_intersection;
+ } else if is_first {
+ border.left_bottom_corner = borders.bottom_intersection;
+ } else {
+ border.left_bottom_corner = border.bottom;
+ }
+ }
+
+ cfg.set_border((row, col), border);
+ }
+ }
+
+ for ((r, col), span) in cfg.get_row_spans() {
+ for row in r + 1..r + span {
+ let mut border = cfg.get_border((row, col), shape);
+ let borders = cfg.get_borders();
+
+ let has_left_border = border.left_top_corner.is_some();
+ if has_left_border {
+ let has_left = col > 0 && has_top(cfg, (row, col - 1), shape);
+ if has_left {
+ border.left_top_corner = borders.right_intersection;
+ } else {
+ border.left_top_corner = borders.vertical;
+ }
+ }
+
+ let has_right_border = border.right_top_corner.is_some();
+ if has_right_border {
+ let has_right = col + 1 < shape.1 && has_top(cfg, (row, col + 1), shape);
+ if has_right {
+ border.right_top_corner = borders.left_intersection;
+ } else {
+ border.right_top_corner = borders.vertical;
+ }
+ }
+
+ cfg.set_border((row, col), border);
+ }
+ }
+
+ let cells = iter_totaly_spanned_cells(cfg, shape).collect::<Vec<_>>();
+ for (row, col) in cells {
+ if row == 0 {
+ continue;
+ }
+
+ let mut border = cfg.get_border((row, col), shape);
+ let borders = cfg.get_borders();
+
+ let has_right = col + 1 < shape.1 && has_top(cfg, (row, col + 1), shape);
+ let has_up = has_left(cfg, (row - 1, col), shape);
+ if has_up && !has_right {
+ border.right_top_corner = borders.right_intersection;
+ }
+
+ let has_down = row + 1 < shape.0 && has_left(cfg, (row + 1, col), shape);
+ if has_down {
+ border.left_bottom_corner = borders.top_intersection;
+ }
+
+ cfg.set_border((row, col), border);
+ }
+}
+
+fn has_left(cfg: &SpannedConfig, pos: Position, shape: (usize, usize)) -> bool {
+ if cfg.is_cell_covered_by_both_spans(pos) || cfg.is_cell_covered_by_column_span(pos) {
+ return false;
+ }
+
+ let border = cfg.get_border(pos, shape);
+ border.left.is_some() || border.left_top_corner.is_some() || border.left_bottom_corner.is_some()
+}
+
+fn has_top(cfg: &SpannedConfig, pos: Position, shape: (usize, usize)) -> bool {
+ if cfg.is_cell_covered_by_both_spans(pos) || cfg.is_cell_covered_by_row_span(pos) {
+ return false;
+ }
+
+ let border = cfg.get_border(pos, shape);
+ border.top.is_some() || border.left_top_corner.is_some() || border.right_top_corner.is_some()
+}
+
+fn iter_totaly_spanned_cells(
+ cfg: &SpannedConfig,
+ shape: (usize, usize),
+) -> impl Iterator<Item = Position> + '_ {
+ // todo: can be optimized
+ let (count_rows, count_cols) = shape;
+ (0..count_rows).flat_map(move |row| {
+ (0..count_cols)
+ .map(move |col| (row, col))
+ .filter(move |&p| cfg.is_cell_covered_by_both_spans(p))
+ })
+}
diff --git a/vendor/tabled/src/settings/style/vertical_line.rs b/vendor/tabled/src/settings/style/vertical_line.rs
new file mode 100644
index 000000000..adbef2c82
--- /dev/null
+++ b/vendor/tabled/src/settings/style/vertical_line.rs
@@ -0,0 +1,50 @@
+#[cfg(feature = "std")]
+use crate::grid::config::{ColoredConfig, VerticalLine as VLine};
+
+use super::Line;
+
+/// A horizontal split line which can be used to set a border.
+#[cfg_attr(not(feature = "std"), allow(dead_code))]
+#[derive(Debug, Clone)]
+pub struct VerticalLine {
+ pub(crate) index: usize,
+ pub(crate) line: Line,
+}
+
+impl VerticalLine {
+ /// Creates a new horizontal split line.
+ pub const fn new(index: usize, line: Line) -> Self {
+ Self { index, line }
+ }
+
+ /// Sets a horizontal character.
+ pub const fn main(mut self, c: Option<char>) -> Self {
+ self.line.main = c;
+ self
+ }
+
+ /// Sets a vertical intersection character.
+ pub const fn intersection(mut self, c: Option<char>) -> Self {
+ self.line.intersection = c;
+ self
+ }
+
+ /// Sets a top character.
+ pub const fn top(mut self, c: Option<char>) -> Self {
+ self.line.connector1 = c;
+ self
+ }
+
+ /// Sets a bottom character.
+ pub const fn bottom(mut self, c: Option<char>) -> Self {
+ self.line.connector2 = c;
+ self
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, D> crate::settings::TableOption<R, D, ColoredConfig> for VerticalLine {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ cfg.insert_vertical_line(self.index, VLine::from(self.line));
+ }
+}
diff --git a/vendor/tabled/src/settings/table_option.rs b/vendor/tabled/src/settings/table_option.rs
new file mode 100644
index 000000000..e20aa4480
--- /dev/null
+++ b/vendor/tabled/src/settings/table_option.rs
@@ -0,0 +1,30 @@
+/// A trait which is responsible for configuration of a [`Table`].
+///
+/// [`Table`]: crate::Table
+pub trait TableOption<R, D, C> {
+ /// The function allows modification of records and a grid configuration.
+ fn change(self, records: &mut R, cfg: &mut C, dimension: &mut D);
+}
+
+impl<T, R, D, C> TableOption<R, D, C> for &[T]
+where
+ for<'a> &'a T: TableOption<R, D, C>,
+{
+ fn change(self, records: &mut R, cfg: &mut C, dimension: &mut D) {
+ for opt in self {
+ opt.change(records, cfg, dimension)
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<T, R, D, C> TableOption<R, D, C> for Vec<T>
+where
+ T: TableOption<R, D, C>,
+{
+ fn change(self, records: &mut R, cfg: &mut C, dimension: &mut D) {
+ for opt in self {
+ opt.change(records, cfg, dimension)
+ }
+ }
+}
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<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);
+ }
+ }
+}
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<Vec<String>>,
+ colors: Vec<Option<Color>>,
+ 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<I>(names: I) -> Self
+ where
+ I: IntoIterator,
+ I::Item: Into<String>,
+ {
+ let names = names.into_iter().map(Into::into).collect::<Vec<_>>();
+ 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<I>(self, colors: I) -> Self
+ where
+ I: IntoIterator,
+ I::Item: Into<Option<Color>>,
+ {
+ let colors = colors.into_iter().map(Into::into).collect::<Vec<_>>();
+ 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<VecRecords<CellInfo<String>>, CompleteDimensionVecRecords<'static>, ColoredConfig>
+ for ColumnNames
+{
+ fn change(
+ self,
+ records: &mut VecRecords<CellInfo<String>>,
+ 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::<Vec<_>>();
+
+ 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::<Vec<_>>();
+
+ 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<usize> {
+ 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<Color>], 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;
diff --git a/vendor/tabled/src/settings/width/justify.rs b/vendor/tabled/src/settings/width/justify.rs
new file mode 100644
index 000000000..03b2afe0d
--- /dev/null
+++ b/vendor/tabled/src/settings/width/justify.rs
@@ -0,0 +1,96 @@
+//! This module contains [`Justify`] structure, used to set an exact width to each column.
+
+use crate::{
+ grid::config::ColoredConfig,
+ grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ settings::{
+ measurement::{Max, Measurement, Min},
+ CellOption, TableOption, Width,
+ },
+};
+
+/// Justify sets all columns widths to the set value.
+///
+/// Be aware that it doesn't consider padding.
+/// So if you want to set a exact width you might need to use [`Padding`] to set it to 0.
+///
+/// ## Examples
+///
+/// ```
+/// use tabled::{Table, settings::{Width, Style, object::Segment, Padding, Modify}};
+///
+/// let data = ["Hello", "World", "!"];
+///
+/// let table = Table::new(&data)
+/// .with(Style::markdown())
+/// .with(Modify::new(Segment::all()).with(Padding::zero()))
+/// .with(Width::justify(3));
+/// ```
+///
+/// [`Max`] usage to justify by a max column width.
+///
+/// ```
+/// use tabled::{Table, settings::{width::Justify, Style}};
+///
+/// let data = ["Hello", "World", "!"];
+///
+/// let table = Table::new(&data)
+/// .with(Style::markdown())
+/// .with(Justify::max());
+/// ```
+///
+/// [`Padding`]: crate::settings::Padding
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Justify<W> {
+ width: W,
+}
+
+impl<W> Justify<W>
+where
+ W: Measurement<Width>,
+{
+ /// Creates a new [`Justify`] instance.
+ ///
+ /// Be aware that [`Padding`] is not considered when comparing the width.
+ ///
+ /// [`Padding`]: crate::settings::Padding
+ pub fn new(width: W) -> Self {
+ Self { width }
+ }
+}
+
+impl Justify<Max> {
+ /// Creates a new Justify instance with a Max width used as a value.
+ pub fn max() -> Self {
+ Self { width: Max }
+ }
+}
+
+impl Justify<Min> {
+ /// Creates a new Justify instance with a Min width used as a value.
+ pub fn min() -> Self {
+ Self { width: Min }
+ }
+}
+
+impl<R, D, W> TableOption<R, D, ColoredConfig> for Justify<W>
+where
+ W: Measurement<Width>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let width = self.width.measure(&*records, cfg);
+
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ for row in 0..count_rows {
+ for col in 0..count_columns {
+ let pos = (row, col).into();
+ CellOption::change(Width::increase(width), records, cfg, pos);
+ CellOption::change(Width::truncate(width), records, cfg, pos);
+ }
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/width/min_width.rs b/vendor/tabled/src/settings/width/min_width.rs
new file mode 100644
index 000000000..afb9ae302
--- /dev/null
+++ b/vendor/tabled/src/settings/width/min_width.rs
@@ -0,0 +1,205 @@
+//! This module contains [`MinWidth`] structure, used to increase width of a [`Table`]s or a cell on a [`Table`].
+//!
+//! [`Table`]: crate::Table
+
+use std::marker::PhantomData;
+
+use crate::{
+ grid::config::ColoredConfig,
+ grid::config::Entity,
+ grid::dimension::CompleteDimensionVecRecords,
+ grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ grid::util::string::{get_lines, string_width_multiline},
+ settings::{
+ measurement::Measurement,
+ peaker::{Peaker, PriorityNone},
+ CellOption, TableOption, Width,
+ },
+};
+
+use super::util::get_table_widths_with_total;
+
+/// [`MinWidth`] changes a content in case if it's length is lower then the boundary.
+///
+/// It can be applied to a whole table.
+///
+/// It does nothing in case if the content's length is bigger then the boundary.
+///
+/// Be aware that further changes of the table may cause the width being not set.
+/// For example applying [`Padding`] after applying [`MinWidth`] will make the former have no affect.
+/// (You should use [`Padding`] first).
+///
+/// Be aware that it doesn't consider padding.
+/// So if you want to set a exact width you might need to use [`Padding`] to set it to 0.
+///
+/// ## Examples
+///
+/// Cell change
+///
+/// ```
+/// use tabled::{Table, settings::{object::Segment, Width, Style, Modify}};
+///
+/// let data = ["Hello", "World", "!"];
+///
+/// let table = Table::new(&data)
+/// .with(Style::markdown())
+/// .with(Modify::new(Segment::all()).with(Width::increase(10)));
+/// ```
+/// Table change
+///
+/// ```
+/// use tabled::{Table, settings::Width};
+///
+/// let table = Table::new(&["Hello World!"]).with(Width::increase(5));
+/// ```
+///
+/// [`Padding`]: crate::settings::Padding
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct MinWidth<W = usize, P = PriorityNone> {
+ width: W,
+ fill: char,
+ _priority: PhantomData<P>,
+}
+
+impl<W> MinWidth<W>
+where
+ W: Measurement<Width>,
+{
+ /// Creates a new instance of [`MinWidth`].
+ pub fn new(width: W) -> Self {
+ Self {
+ width,
+ fill: ' ',
+ _priority: PhantomData,
+ }
+ }
+}
+
+impl<W, P> MinWidth<W, P> {
+ /// Set's a fill character which will be used to fill the space
+ /// when increasing the length of the string to the set boundary.
+ ///
+ /// Used only if chaning cells.
+ pub fn fill_with(mut self, c: char) -> Self {
+ self.fill = c;
+ self
+ }
+
+ /// Priority defines the logic by which a increase of width will be applied when is done for the whole table.
+ ///
+ /// - [`PriorityNone`] which inc the columns one after another.
+ /// - [`PriorityMax`] inc the biggest columns first.
+ /// - [`PriorityMin`] inc the lowest columns first.
+ ///
+ /// [`PriorityMax`]: crate::settings::peaker::PriorityMax
+ /// [`PriorityMin`]: crate::settings::peaker::PriorityMin
+ pub fn priority<PP: Peaker>(self) -> MinWidth<W, PP> {
+ MinWidth {
+ fill: self.fill,
+ width: self.width,
+ _priority: PhantomData,
+ }
+ }
+}
+
+impl<W, R> CellOption<R, ColoredConfig> for MinWidth<W>
+where
+ W: Measurement<Width>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let width = self.width.measure(&*records, cfg);
+
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_columns) {
+ let is_valid_pos = pos.0 < count_rows && pos.1 < count_columns;
+ if !is_valid_pos {
+ continue;
+ }
+
+ let cell = records.get_text(pos);
+ let cell_width = string_width_multiline(cell);
+ if cell_width >= width {
+ continue;
+ }
+
+ let content = increase_width(cell, width, self.fill);
+ records.set(pos, content);
+ }
+ }
+}
+
+impl<W, P, R> TableOption<R, CompleteDimensionVecRecords<'static>, ColoredConfig> for MinWidth<W, P>
+where
+ W: Measurement<Width>,
+ P: Peaker,
+ R: Records + ExactRecords + PeekableRecords,
+ for<'a> &'a R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return;
+ }
+
+ let nessary_width = self.width.measure(&*records, cfg);
+
+ let (widths, total_width) = get_table_widths_with_total(&*records, cfg);
+ if total_width >= nessary_width {
+ return;
+ }
+
+ let widths = get_increase_list(widths, nessary_width, total_width, P::create());
+ let _ = dims.set_widths(widths);
+ }
+}
+
+fn get_increase_list<F>(
+ mut widths: Vec<usize>,
+ need: usize,
+ mut current: usize,
+ mut peaker: F,
+) -> Vec<usize>
+where
+ F: Peaker,
+{
+ while need != current {
+ let col = match peaker.peak(&[], &widths) {
+ Some(col) => col,
+ None => break,
+ };
+
+ widths[col] += 1;
+ current += 1;
+ }
+
+ widths
+}
+
+fn increase_width(s: &str, width: usize, fill_with: char) -> String {
+ use crate::grid::util::string::string_width;
+ use std::{borrow::Cow, iter::repeat};
+
+ get_lines(s)
+ .map(|line| {
+ let length = string_width(&line);
+
+ if length < width {
+ let mut line = line.into_owned();
+ let remain = width - length;
+ line.extend(repeat(fill_with).take(remain));
+ Cow::Owned(line)
+ } else {
+ line
+ }
+ })
+ .collect::<Vec<_>>()
+ .join("\n")
+}
diff --git a/vendor/tabled/src/settings/width/mod.rs b/vendor/tabled/src/settings/width/mod.rs
new file mode 100644
index 000000000..c1202f70f
--- /dev/null
+++ b/vendor/tabled/src/settings/width/mod.rs
@@ -0,0 +1,163 @@
+//! This module contains object which can be used to limit a cell to a given width:
+//!
+//! - [`Truncate`] cuts a cell content to limit width.
+//! - [`Wrap`] split the content via new lines in order to fit max width.
+//! - [`Justify`] sets columns width to the same value.
+//!
+//! To set a a table width, a combination of [`Width::truncate`] or [`Width::wrap`] and [`Width::increase`] can be used.
+//!
+//! ## Example
+//!
+//! ```
+//! use tabled::{Table, settings::Width};
+//!
+//! let table = Table::new(&["Hello World!"])
+//! .with(Width::wrap(7))
+//! .with(Width::increase(7))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "+-----+\n",
+//! "| &st |\n",
+//! "| r |\n",
+//! "+-----+\n",
+//! "| Hel |\n",
+//! "| lo |\n",
+//! "| Wor |\n",
+//! "| ld! |\n",
+//! "+-----+",
+//! )
+//! );
+//! ```
+
+mod justify;
+mod min_width;
+mod truncate;
+mod util;
+mod width_list;
+mod wrap;
+
+use crate::settings::measurement::Measurement;
+
+pub use self::{
+ justify::Justify,
+ min_width::MinWidth,
+ truncate::{SuffixLimit, Truncate},
+ width_list::WidthList,
+ wrap::Wrap,
+};
+
+/// Width allows you to set a min and max width of an object on a [`Table`]
+/// using different strategies.
+///
+/// It also allows you to set a min and max width for a whole table.
+///
+/// You can apply a min and max strategy at the same time with the same value,
+/// the value will be a total table width.
+///
+/// It is an abstract factory.
+///
+/// Beware that borders are not removed when you set a size value to very small.
+/// For example if you set size to 0 the table still be rendered but with all content removed.
+///
+/// Also be aware that it doesn't changes [`Padding`] settings nor it considers them.
+///
+/// The function is color aware if a `color` feature is on.
+///
+/// ## Examples
+///
+/// ### Cell change
+///
+/// ```
+/// use tabled::{Table, settings::{object::Segment, Width, Style, Modify}};
+///
+/// let data = ["Hello", "World", "!"];
+///
+/// let table = Table::new(&data)
+/// .with(Style::markdown())
+/// .with(Modify::new(Segment::all()).with(Width::truncate(3).suffix("...")));
+/// ```
+///
+/// ### Table change
+///
+/// ```
+/// use tabled::{Table, settings::Width};
+///
+/// let table = Table::new(&["Hello World!"]).with(Width::wrap(5));
+/// ```
+///
+/// ### Total width
+///
+/// ```
+/// use tabled::{Table, settings::Width};
+///
+/// let table = Table::new(&["Hello World!"])
+/// .with(Width::wrap(5))
+/// .with(Width::increase(5));
+/// ```
+///
+/// [`Padding`]: crate::settings::Padding
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Width;
+
+impl Width {
+ /// Returns a [`Wrap`] structure.
+ pub fn wrap<W: Measurement<Width>>(width: W) -> Wrap<W> {
+ Wrap::new(width)
+ }
+
+ /// Returns a [`Truncate`] structure.
+ pub fn truncate<W: Measurement<Width>>(width: W) -> Truncate<'static, W> {
+ Truncate::new(width)
+ }
+
+ /// Returns a [`MinWidth`] structure.
+ pub fn increase<W: Measurement<Width>>(width: W) -> MinWidth<W> {
+ MinWidth::new(width)
+ }
+
+ /// Returns a [`Justify`] structure.
+ pub fn justify<W: Measurement<Width>>(width: W) -> Justify<W> {
+ Justify::new(width)
+ }
+
+ /// Create [`WidthList`] to set a table width to a constant list of column widths.
+ ///
+ /// Notice if you provide a list with `.len()` smaller than `Table::count_columns` then it will have no affect.
+ ///
+ /// Also notice that you must provide values bigger than or equal to a real content width, otherwise it may panic.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{Table, settings::Width};
+ ///
+ /// let data = vec![
+ /// ("Some\ndata", "here", "and here"),
+ /// ("Some\ndata on a next", "line", "right here"),
+ /// ];
+ ///
+ /// let table = Table::new(data)
+ /// .with(Width::list([20, 10, 12]))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+--------------------+----------+------------+\n\
+ /// | &str | &str | &str |\n\
+ /// +--------------------+----------+------------+\n\
+ /// | Some | here | and here |\n\
+ /// | data | | |\n\
+ /// +--------------------+----------+------------+\n\
+ /// | Some | line | right here |\n\
+ /// | data on a next | | |\n\
+ /// +--------------------+----------+------------+"
+ /// )
+ /// ```
+ pub fn list<I: IntoIterator<Item = usize>>(rows: I) -> WidthList {
+ WidthList::new(rows.into_iter().collect())
+ }
+}
diff --git a/vendor/tabled/src/settings/width/truncate.rs b/vendor/tabled/src/settings/width/truncate.rs
new file mode 100644
index 000000000..c7336f037
--- /dev/null
+++ b/vendor/tabled/src/settings/width/truncate.rs
@@ -0,0 +1,505 @@
+//! This module contains [`Truncate`] structure, used to decrease width of a [`Table`]s or a cell on a [`Table`] by truncating the width.
+//!
+//! [`Table`]: crate::Table
+
+use std::{borrow::Cow, iter, marker::PhantomData};
+
+use crate::{
+ grid::{
+ config::{ColoredConfig, SpannedConfig},
+ dimension::CompleteDimensionVecRecords,
+ records::{EmptyRecords, ExactRecords, PeekableRecords, Records, RecordsMut},
+ util::string::{string_width, string_width_multiline},
+ },
+ settings::{
+ measurement::Measurement,
+ peaker::{Peaker, PriorityNone},
+ CellOption, TableOption, Width,
+ },
+};
+
+use super::util::{cut_str, get_table_widths, get_table_widths_with_total};
+
+/// Truncate cut the string to a given width if its length exceeds it.
+/// Otherwise keeps the content of a cell untouched.
+///
+/// The function is color aware if a `color` feature is on.
+///
+/// Be aware that it doesn't consider padding.
+/// So if you want to set a exact width you might need to use [`Padding`] to set it to 0.
+///
+/// ## Example
+///
+/// ```
+/// use tabled::{Table, settings::{object::Segment, Width, Modify}};
+///
+/// let table = Table::new(&["Hello World!"])
+/// .with(Modify::new(Segment::all()).with(Width::truncate(3)));
+/// ```
+///
+/// [`Padding`]: crate::settings::Padding
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Truncate<'a, W = usize, P = PriorityNone> {
+ width: W,
+ suffix: Option<TruncateSuffix<'a>>,
+ multiline: bool,
+ _priority: PhantomData<P>,
+}
+#[cfg(feature = "color")]
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+struct TruncateSuffix<'a> {
+ text: Cow<'a, str>,
+ limit: SuffixLimit,
+ try_color: bool,
+}
+
+#[cfg(not(feature = "color"))]
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+struct TruncateSuffix<'a> {
+ text: Cow<'a, str>,
+ limit: SuffixLimit,
+}
+
+impl Default for TruncateSuffix<'_> {
+ fn default() -> Self {
+ Self {
+ text: Cow::default(),
+ limit: SuffixLimit::Cut,
+ #[cfg(feature = "color")]
+ try_color: false,
+ }
+ }
+}
+
+/// A suffix limit settings.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum SuffixLimit {
+ /// Cut the suffix.
+ Cut,
+ /// Don't show the suffix.
+ Ignore,
+ /// Use a string with n chars instead.
+ Replace(char),
+}
+
+impl<W> Truncate<'static, W>
+where
+ W: Measurement<Width>,
+{
+ /// Creates a [`Truncate`] object
+ pub fn new(width: W) -> Truncate<'static, W> {
+ Self {
+ width,
+ multiline: false,
+ suffix: None,
+ _priority: PhantomData,
+ }
+ }
+}
+
+impl<'a, W, P> Truncate<'a, W, P> {
+ /// Sets a suffix which will be appended to a resultant string.
+ ///
+ /// The suffix is used in 3 circumstances:
+ /// 1. If original string is *bigger* than the suffix.
+ /// We cut more of the original string and append the suffix.
+ /// 2. If suffix is bigger than the original string.
+ /// We cut the suffix to fit in the width by default.
+ /// But you can peak the behaviour by using [`Truncate::suffix_limit`]
+ pub fn suffix<S: Into<Cow<'a, str>>>(self, suffix: S) -> Truncate<'a, W, P> {
+ let mut suff = self.suffix.unwrap_or_default();
+ suff.text = suffix.into();
+
+ Truncate {
+ width: self.width,
+ multiline: self.multiline,
+ suffix: Some(suff),
+ _priority: PhantomData,
+ }
+ }
+
+ /// Sets a suffix limit, which is used when the suffix is too big to be used.
+ pub fn suffix_limit(self, limit: SuffixLimit) -> Truncate<'a, W, P> {
+ let mut suff = self.suffix.unwrap_or_default();
+ suff.limit = limit;
+
+ Truncate {
+ width: self.width,
+ multiline: self.multiline,
+ suffix: Some(suff),
+ _priority: PhantomData,
+ }
+ }
+
+ /// Use trancate logic per line, not as a string as a whole.
+ pub fn multiline(self) -> Truncate<'a, W, P> {
+ Truncate {
+ width: self.width,
+ multiline: true,
+ suffix: self.suffix,
+ _priority: self._priority,
+ }
+ }
+
+ #[cfg(feature = "color")]
+ /// Sets a optional logic to try to colorize a suffix.
+ pub fn suffix_try_color(self, color: bool) -> Truncate<'a, W, P> {
+ let mut suff = self.suffix.unwrap_or_default();
+ suff.try_color = color;
+
+ Truncate {
+ width: self.width,
+ multiline: self.multiline,
+ suffix: Some(suff),
+ _priority: PhantomData,
+ }
+ }
+}
+
+impl<'a, W, P> Truncate<'a, W, P> {
+ /// Priority defines the logic by which a truncate will be applied when is done for the whole table.
+ ///
+ /// - [`PriorityNone`] which cuts the columns one after another.
+ /// - [`PriorityMax`] cuts the biggest columns first.
+ /// - [`PriorityMin`] cuts the lowest columns first.
+ ///
+ /// [`PriorityMax`]: crate::settings::peaker::PriorityMax
+ /// [`PriorityMin`]: crate::settings::peaker::PriorityMin
+ pub fn priority<PP: Peaker>(self) -> Truncate<'a, W, PP> {
+ Truncate {
+ width: self.width,
+ multiline: self.multiline,
+ suffix: self.suffix,
+ _priority: PhantomData,
+ }
+ }
+}
+
+impl Truncate<'_, (), ()> {
+ /// Truncate a given string
+ pub fn truncate_text(text: &str, width: usize) -> Cow<'_, str> {
+ truncate_text(text, width, "", false)
+ }
+}
+
+impl<W, P, R> CellOption<R, ColoredConfig> for Truncate<'_, W, P>
+where
+ W: Measurement<Width>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: papergrid::config::Entity) {
+ let available = self.width.measure(&*records, cfg);
+
+ let mut width = available;
+ let mut suffix = Cow::Borrowed("");
+
+ if let Some(x) = self.suffix.as_ref() {
+ let (cutted_suffix, rest_width) = make_suffix(x, width);
+ suffix = cutted_suffix;
+ width = rest_width;
+ };
+
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ let colorize = need_suffix_color_preservation(&self.suffix);
+
+ for pos in entity.iter(count_rows, count_columns) {
+ let is_valid_pos = pos.0 < count_rows && pos.1 < count_columns;
+ if !is_valid_pos {
+ continue;
+ }
+
+ let text = records.get_text(pos);
+
+ let cell_width = string_width_multiline(text);
+ if available >= cell_width {
+ continue;
+ }
+
+ let text =
+ truncate_multiline(text, &suffix, width, available, colorize, self.multiline);
+
+ records.set(pos, text.into_owned());
+ }
+ }
+}
+
+fn truncate_multiline<'a>(
+ text: &'a str,
+ suffix: &'a str,
+ width: usize,
+ twidth: usize,
+ suffix_color: bool,
+ multiline: bool,
+) -> Cow<'a, str> {
+ if multiline {
+ let mut buf = String::new();
+ for (i, line) in crate::grid::util::string::get_lines(text).enumerate() {
+ if i != 0 {
+ buf.push('\n');
+ }
+
+ let line = make_text_truncated(&line, suffix, width, twidth, suffix_color);
+ buf.push_str(&line);
+ }
+
+ Cow::Owned(buf)
+ } else {
+ make_text_truncated(text, suffix, width, twidth, suffix_color)
+ }
+}
+
+fn make_text_truncated<'a>(
+ text: &'a str,
+ suffix: &'a str,
+ width: usize,
+ twidth: usize,
+ suffix_color: bool,
+) -> Cow<'a, str> {
+ if width == 0 {
+ if twidth == 0 {
+ Cow::Borrowed("")
+ } else {
+ Cow::Borrowed(suffix)
+ }
+ } else {
+ truncate_text(text, width, suffix, suffix_color)
+ }
+}
+
+fn need_suffix_color_preservation(_suffix: &Option<TruncateSuffix<'_>>) -> bool {
+ #[cfg(not(feature = "color"))]
+ {
+ false
+ }
+ #[cfg(feature = "color")]
+ {
+ _suffix.as_ref().map_or(false, |s| s.try_color)
+ }
+}
+
+fn make_suffix<'a>(suffix: &'a TruncateSuffix<'_>, width: usize) -> (Cow<'a, str>, usize) {
+ let suffix_length = string_width(&suffix.text);
+ if width > suffix_length {
+ return (Cow::Borrowed(suffix.text.as_ref()), width - suffix_length);
+ }
+
+ match suffix.limit {
+ SuffixLimit::Ignore => (Cow::Borrowed(""), width),
+ SuffixLimit::Cut => {
+ let suffix = cut_str(&suffix.text, width);
+ (suffix, 0)
+ }
+ SuffixLimit::Replace(c) => {
+ let suffix = Cow::Owned(iter::repeat(c).take(width).collect());
+ (suffix, 0)
+ }
+ }
+}
+
+impl<W, P, R> TableOption<R, CompleteDimensionVecRecords<'static>, ColoredConfig>
+ for Truncate<'_, W, P>
+where
+ W: Measurement<Width>,
+ P: Peaker,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return;
+ }
+
+ let width = self.width.measure(&*records, cfg);
+ let (widths, total) = get_table_widths_with_total(&*records, cfg);
+ if total <= width {
+ return;
+ }
+
+ let suffix = self.suffix.as_ref().map(|s| TruncateSuffix {
+ text: Cow::Borrowed(&s.text),
+ limit: s.limit,
+ #[cfg(feature = "color")]
+ try_color: s.try_color,
+ });
+
+ let priority = P::create();
+ let multiline = self.multiline;
+ let widths = truncate_total_width(
+ records, cfg, widths, total, width, priority, suffix, multiline,
+ );
+
+ let _ = dims.set_widths(widths);
+ }
+}
+
+#[allow(clippy::too_many_arguments)]
+fn truncate_total_width<P, R>(
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ mut widths: Vec<usize>,
+ total: usize,
+ width: usize,
+ priority: P,
+ suffix: Option<TruncateSuffix<'_>>,
+ multiline: bool,
+) -> Vec<usize>
+where
+ for<'a> &'a R: Records,
+ P: Peaker,
+ R: Records + PeekableRecords + ExactRecords + RecordsMut<String>,
+{
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ let min_widths = get_table_widths(EmptyRecords::new(count_rows, count_columns), cfg);
+
+ decrease_widths(&mut widths, &min_widths, total, width, priority);
+
+ let points = get_decrease_cell_list(cfg, &widths, &min_widths, (count_rows, count_columns));
+
+ for ((row, col), width) in points {
+ let mut truncate = Truncate::new(width);
+ truncate.suffix = suffix.clone();
+ truncate.multiline = multiline;
+ CellOption::change(truncate, records, cfg, (row, col).into());
+ }
+
+ widths
+}
+
+fn truncate_text<'a>(
+ text: &'a str,
+ width: usize,
+ suffix: &str,
+ _suffix_color: bool,
+) -> Cow<'a, str> {
+ let content = cut_str(text, width);
+ if suffix.is_empty() {
+ return content;
+ }
+
+ #[cfg(feature = "color")]
+ {
+ if _suffix_color {
+ if let Some(block) = ansi_str::get_blocks(text).last() {
+ if block.has_ansi() {
+ let style = block.style();
+ Cow::Owned(format!(
+ "{}{}{}{}",
+ content,
+ style.start(),
+ suffix,
+ style.end()
+ ))
+ } else {
+ let mut content = content.into_owned();
+ content.push_str(suffix);
+ Cow::Owned(content)
+ }
+ } else {
+ let mut content = content.into_owned();
+ content.push_str(suffix);
+ Cow::Owned(content)
+ }
+ } else {
+ let mut content = content.into_owned();
+ content.push_str(suffix);
+ Cow::Owned(content)
+ }
+ }
+
+ #[cfg(not(feature = "color"))]
+ {
+ let mut content = content.into_owned();
+ content.push_str(suffix);
+ Cow::Owned(content)
+ }
+}
+
+fn get_decrease_cell_list(
+ cfg: &SpannedConfig,
+ widths: &[usize],
+ min_widths: &[usize],
+ shape: (usize, usize),
+) -> Vec<((usize, usize), usize)> {
+ let mut points = Vec::new();
+ (0..shape.1).for_each(|col| {
+ (0..shape.0)
+ .filter(|&row| cfg.is_cell_visible((row, col)))
+ .for_each(|row| {
+ let (width, width_min) = match cfg.get_column_span((row, col)) {
+ Some(span) => {
+ let width = (col..col + span).map(|i| widths[i]).sum::<usize>();
+ let min_width = (col..col + span).map(|i| min_widths[i]).sum::<usize>();
+ let count_borders = count_borders(cfg, col, col + span, shape.1);
+ (width + count_borders, min_width + count_borders)
+ }
+ None => (widths[col], min_widths[col]),
+ };
+
+ if width >= width_min {
+ let padding = cfg.get_padding((row, col).into());
+ let width = width.saturating_sub(padding.left.size + padding.right.size);
+
+ points.push(((row, col), width));
+ }
+ });
+ });
+
+ points
+}
+
+fn decrease_widths<F>(
+ widths: &mut [usize],
+ min_widths: &[usize],
+ total_width: usize,
+ mut width: usize,
+ mut peeaker: F,
+) where
+ F: Peaker,
+{
+ let mut empty_list = 0;
+ for col in 0..widths.len() {
+ if widths[col] == 0 || widths[col] <= min_widths[col] {
+ empty_list += 1;
+ }
+ }
+
+ while width != total_width {
+ if empty_list == widths.len() {
+ break;
+ }
+
+ let col = match peeaker.peak(min_widths, widths) {
+ Some(col) => col,
+ None => break,
+ };
+
+ if widths[col] == 0 || widths[col] <= min_widths[col] {
+ continue;
+ }
+
+ widths[col] -= 1;
+
+ if widths[col] == 0 || widths[col] <= min_widths[col] {
+ empty_list += 1;
+ }
+
+ width += 1;
+ }
+}
+
+fn count_borders(cfg: &SpannedConfig, start: usize, end: usize, count_columns: usize) -> usize {
+ (start..end)
+ .skip(1)
+ .filter(|&i| cfg.has_vertical(i, count_columns))
+ .count()
+}
diff --git a/vendor/tabled/src/settings/width/util.rs b/vendor/tabled/src/settings/width/util.rs
new file mode 100644
index 000000000..92cc48c41
--- /dev/null
+++ b/vendor/tabled/src/settings/width/util.rs
@@ -0,0 +1,265 @@
+use std::borrow::Cow;
+
+use crate::{
+ grid::config::SpannedConfig, grid::dimension::SpannedGridDimension, grid::records::Records,
+};
+
+pub(crate) fn get_table_widths<R: Records>(records: R, cfg: &SpannedConfig) -> Vec<usize> {
+ SpannedGridDimension::width(records, cfg)
+}
+
+pub(crate) fn get_table_widths_with_total<R: Records>(
+ records: R,
+ cfg: &SpannedConfig,
+) -> (Vec<usize>, usize) {
+ let widths = SpannedGridDimension::width(records, cfg);
+ let total_width = get_table_total_width(&widths, cfg);
+ (widths, total_width)
+}
+
+fn get_table_total_width(list: &[usize], cfg: &SpannedConfig) -> usize {
+ let margin = cfg.get_margin();
+ list.iter().sum::<usize>()
+ + cfg.count_vertical(list.len())
+ + margin.left.size
+ + margin.right.size
+}
+
+/// The function cuts the string to a specific width.
+///
+/// BE AWARE: width is expected to be in bytes.
+pub(crate) fn cut_str(s: &str, width: usize) -> Cow<'_, str> {
+ #[cfg(feature = "color")]
+ {
+ const REPLACEMENT: char = '\u{FFFD}';
+
+ let stripped = ansi_str::AnsiStr::ansi_strip(s);
+ let (length, count_unknowns, _) = split_at_pos(&stripped, width);
+
+ let mut buf = ansi_str::AnsiStr::ansi_cut(s, ..length);
+ if count_unknowns > 0 {
+ let mut b = buf.into_owned();
+ b.extend(std::iter::repeat(REPLACEMENT).take(count_unknowns));
+ buf = Cow::Owned(b);
+ }
+
+ buf
+ }
+
+ #[cfg(not(feature = "color"))]
+ {
+ cut_str_basic(s, width)
+ }
+}
+
+/// The function cuts the string to a specific width.
+///
+/// BE AWARE: width is expected to be in bytes.
+#[cfg(not(feature = "color"))]
+pub(crate) fn cut_str_basic(s: &str, width: usize) -> Cow<'_, str> {
+ const REPLACEMENT: char = '\u{FFFD}';
+
+ let (length, count_unknowns, _) = split_at_pos(s, width);
+ let buf = &s[..length];
+ if count_unknowns == 0 {
+ return Cow::Borrowed(buf);
+ }
+
+ let mut buf = buf.to_owned();
+ buf.extend(std::iter::repeat(REPLACEMENT).take(count_unknowns));
+
+ Cow::Owned(buf)
+}
+
+/// The function splits a string in the position and
+/// returns a exact number of bytes before the position and in case of a split in an unicode grapheme
+/// a width of a character which was tried to be splited in.
+///
+/// BE AWARE: pos is expected to be in bytes.
+pub(crate) fn split_at_pos(s: &str, pos: usize) -> (usize, usize, usize) {
+ let mut length = 0;
+ let mut i = 0;
+ for c in s.chars() {
+ if i == pos {
+ break;
+ };
+
+ let c_width = unicode_width::UnicodeWidthChar::width(c).unwrap_or_default();
+
+ // We cut the chars which takes more then 1 symbol to display,
+ // in order to archive the necessary width.
+ if i + c_width > pos {
+ let count = pos - i;
+ return (length, count, c.len_utf8());
+ }
+
+ i += c_width;
+ length += c.len_utf8();
+ }
+
+ (length, 0, 0)
+}
+
+/// Strip OSC codes from `s`. If `s` is a single OSC8 hyperlink, with no other text, then return
+/// (s_with_all_hyperlinks_removed, Some(url)). If `s` does not meet this description, then return
+/// (s_with_all_hyperlinks_removed, None). Any ANSI color sequences in `s` will be retained. See
+/// <https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>
+///
+/// The function is based on Dan Davison <https://github.com/dandavison> delta <https://github.com/dandavison/delta> ansi library.
+#[cfg(feature = "color")]
+pub(crate) fn strip_osc(text: &str) -> (String, Option<String>) {
+ #[derive(Debug)]
+ enum ExtractOsc8HyperlinkState {
+ ExpectOsc8Url,
+ ExpectFirstText,
+ ExpectMoreTextOrTerminator,
+ SeenOneHyperlink,
+ WillNotReturnUrl,
+ }
+
+ use ExtractOsc8HyperlinkState::*;
+
+ let mut url = None;
+ let mut state = ExpectOsc8Url;
+ let mut buf = String::with_capacity(text.len());
+
+ for el in ansitok::parse_ansi(text) {
+ match el.kind() {
+ ansitok::ElementKind::Osc => match state {
+ ExpectOsc8Url => {
+ url = Some(&text[el.start()..el.end()]);
+ state = ExpectFirstText;
+ }
+ ExpectMoreTextOrTerminator => state = SeenOneHyperlink,
+ _ => state = WillNotReturnUrl,
+ },
+ ansitok::ElementKind::Sgr => buf.push_str(&text[el.start()..el.end()]),
+ ansitok::ElementKind::Csi => buf.push_str(&text[el.start()..el.end()]),
+ ansitok::ElementKind::Esc => {}
+ ansitok::ElementKind::Text => {
+ buf.push_str(&text[el.start()..el.end()]);
+ match state {
+ ExpectFirstText => state = ExpectMoreTextOrTerminator,
+ ExpectMoreTextOrTerminator => {}
+ _ => state = WillNotReturnUrl,
+ }
+ }
+ }
+ }
+
+ match state {
+ WillNotReturnUrl => (buf, None),
+ _ => {
+ let url = url.and_then(|s| {
+ s.strip_prefix("\x1b]8;;")
+ .and_then(|s| s.strip_suffix('\x1b'))
+ });
+ if let Some(url) = url {
+ (buf, Some(url.to_string()))
+ } else {
+ (buf, None)
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use crate::grid::util::string::string_width;
+
+ #[cfg(feature = "color")]
+ use owo_colors::{colors::Yellow, OwoColorize};
+
+ #[test]
+ fn strip_test() {
+ assert_eq!(cut_str("123456", 0), "");
+ assert_eq!(cut_str("123456", 3), "123");
+ assert_eq!(cut_str("123456", 10), "123456");
+
+ assert_eq!(cut_str("a week ago", 4), "a we");
+
+ assert_eq!(cut_str("😳😳😳😳😳", 0), "");
+ assert_eq!(cut_str("😳😳😳😳😳", 3), "😳�");
+ assert_eq!(cut_str("😳😳😳😳😳", 4), "😳😳");
+ assert_eq!(cut_str("😳😳😳😳😳", 20), "😳😳😳😳😳");
+
+ assert_eq!(cut_str("🏳️🏳️", 0), "");
+ assert_eq!(cut_str("🏳️🏳️", 1), "🏳");
+ assert_eq!(cut_str("🏳️🏳️", 2), "🏳\u{fe0f}🏳");
+ assert_eq!(string_width("🏳️🏳️"), string_width("🏳\u{fe0f}🏳"));
+
+ assert_eq!(cut_str("🎓", 1), "�");
+ assert_eq!(cut_str("🎓", 2), "🎓");
+
+ assert_eq!(cut_str("🥿", 1), "�");
+ assert_eq!(cut_str("🥿", 2), "🥿");
+
+ assert_eq!(cut_str("🩰", 1), "�");
+ assert_eq!(cut_str("🩰", 2), "🩰");
+
+ assert_eq!(cut_str("👍🏿", 1), "�");
+ assert_eq!(cut_str("👍🏿", 2), "👍");
+ assert_eq!(cut_str("👍🏿", 3), "👍�");
+ assert_eq!(cut_str("👍🏿", 4), "👍🏿");
+
+ assert_eq!(cut_str("🇻🇬", 1), "🇻");
+ assert_eq!(cut_str("🇻🇬", 2), "🇻🇬");
+ assert_eq!(cut_str("🇻🇬", 3), "🇻🇬");
+ assert_eq!(cut_str("🇻🇬", 4), "🇻🇬");
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn strip_color_test() {
+ let numbers = "123456".red().on_bright_black().to_string();
+
+ assert_eq!(cut_str(&numbers, 0), "\u{1b}[31;100m\u{1b}[39m\u{1b}[49m");
+ assert_eq!(
+ cut_str(&numbers, 3),
+ "\u{1b}[31;100m123\u{1b}[39m\u{1b}[49m"
+ );
+ assert_eq!(cut_str(&numbers, 10), "\u{1b}[31;100m123456\u{1b}[0m");
+
+ let emojies = "😳😳😳😳😳".red().on_bright_black().to_string();
+
+ assert_eq!(cut_str(&emojies, 0), "\u{1b}[31;100m\u{1b}[39m\u{1b}[49m");
+ assert_eq!(
+ cut_str(&emojies, 3),
+ "\u{1b}[31;100m😳\u{1b}[39m\u{1b}[49m�"
+ );
+ assert_eq!(
+ cut_str(&emojies, 4),
+ "\u{1b}[31;100m😳😳\u{1b}[39m\u{1b}[49m"
+ );
+ assert_eq!(cut_str(&emojies, 20), "\u{1b}[31;100m😳😳😳😳😳\u{1b}[0m");
+
+ let emojies = "🏳️🏳️".red().on_bright_black().to_string();
+
+ assert_eq!(cut_str(&emojies, 0), "\u{1b}[31;100m\u{1b}[39m\u{1b}[49m");
+ assert_eq!(cut_str(&emojies, 1), "\u{1b}[31;100m🏳\u{1b}[39m\u{1b}[49m");
+ assert_eq!(
+ cut_str(&emojies, 2),
+ "\u{1b}[31;100m🏳\u{fe0f}🏳\u{1b}[39m\u{1b}[49m"
+ );
+ assert_eq!(
+ string_width(&emojies),
+ string_width("\u{1b}[31;100m🏳\u{fe0f}🏳\u{1b}[39m\u{1b}[49m")
+ );
+ }
+
+ #[test]
+ #[cfg(feature = "color")]
+ fn test_color_strip() {
+ let s = "Collored string"
+ .fg::<Yellow>()
+ .on_truecolor(12, 200, 100)
+ .blink()
+ .to_string();
+ assert_eq!(
+ cut_str(&s, 1),
+ "\u{1b}[5m\u{1b}[48;2;12;200;100m\u{1b}[33mC\u{1b}[25m\u{1b}[39m\u{1b}[49m"
+ )
+ }
+}
diff --git a/vendor/tabled/src/settings/width/width_list.rs b/vendor/tabled/src/settings/width/width_list.rs
new file mode 100644
index 000000000..7547b97f3
--- /dev/null
+++ b/vendor/tabled/src/settings/width/width_list.rs
@@ -0,0 +1,50 @@
+use std::iter::FromIterator;
+
+use crate::{
+ grid::dimension::CompleteDimensionVecRecords, grid::records::Records, settings::TableOption,
+};
+
+/// A structure used to set [`Table`] width via a list of columns widths.
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct WidthList {
+ list: Vec<usize>,
+}
+
+impl WidthList {
+ /// Creates a new object.
+ pub fn new(list: Vec<usize>) -> Self {
+ Self { list }
+ }
+}
+
+impl From<Vec<usize>> for WidthList {
+ fn from(list: Vec<usize>) -> Self {
+ Self::new(list)
+ }
+}
+
+impl FromIterator<usize> for WidthList {
+ fn from_iter<T: IntoIterator<Item = usize>>(iter: T) -> Self {
+ Self::new(iter.into_iter().collect())
+ }
+}
+
+impl<R, C> TableOption<R, CompleteDimensionVecRecords<'static>, C> for WidthList
+where
+ R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ _: &mut C,
+ dimension: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ if self.list.len() < records.count_columns() {
+ return;
+ }
+
+ let _ = dimension.set_widths(self.list);
+ }
+}
diff --git a/vendor/tabled/src/settings/width/wrap.rs b/vendor/tabled/src/settings/width/wrap.rs
new file mode 100644
index 000000000..96a370408
--- /dev/null
+++ b/vendor/tabled/src/settings/width/wrap.rs
@@ -0,0 +1,1468 @@
+//! This module contains [`Wrap`] structure, used to decrease width of a [`Table`]s or a cell on a [`Table`] by wrapping it's content
+//! to a new line.
+//!
+//! [`Table`]: crate::Table
+
+use std::marker::PhantomData;
+
+use crate::{
+ grid::config::ColoredConfig,
+ grid::dimension::CompleteDimensionVecRecords,
+ grid::records::{EmptyRecords, ExactRecords, PeekableRecords, Records, RecordsMut},
+ grid::{config::Entity, config::SpannedConfig, util::string::string_width_multiline},
+ settings::{
+ measurement::Measurement,
+ peaker::{Peaker, PriorityNone},
+ width::Width,
+ CellOption, TableOption,
+ },
+};
+
+use super::util::{get_table_widths, get_table_widths_with_total, split_at_pos};
+
+/// Wrap wraps a string to a new line in case it exceeds the provided max boundary.
+/// Otherwise keeps the content of a cell untouched.
+///
+/// The function is color aware if a `color` feature is on.
+///
+/// Be aware that it doesn't consider padding.
+/// So if you want to set a exact width you might need to use [`Padding`] to set it to 0.
+///
+/// ## Example
+///
+/// ```
+/// use tabled::{Table, settings::{object::Segment, width::Width, Modify}};
+///
+/// let table = Table::new(&["Hello World!"])
+/// .with(Modify::new(Segment::all()).with(Width::wrap(3)));
+/// ```
+///
+/// [`Padding`]: crate::settings::Padding
+#[derive(Debug, Clone)]
+pub struct Wrap<W = usize, P = PriorityNone> {
+ width: W,
+ keep_words: bool,
+ _priority: PhantomData<P>,
+}
+
+impl<W> Wrap<W> {
+ /// Creates a [`Wrap`] object
+ pub fn new(width: W) -> Self
+ where
+ W: Measurement<Width>,
+ {
+ Wrap {
+ width,
+ keep_words: false,
+ _priority: PhantomData,
+ }
+ }
+}
+
+impl<W, P> Wrap<W, P> {
+ /// Priority defines the logic by which a truncate will be applied when is done for the whole table.
+ ///
+ /// - [`PriorityNone`] which cuts the columns one after another.
+ /// - [`PriorityMax`] cuts the biggest columns first.
+ /// - [`PriorityMin`] cuts the lowest columns first.
+ ///
+ /// Be aware that it doesn't consider padding.
+ /// So if you want to set a exact width you might need to use [`Padding`] to set it to 0.
+ ///
+ /// [`Padding`]: crate::settings::Padding
+ /// [`PriorityMax`]: crate::settings::peaker::PriorityMax
+ /// [`PriorityMin`]: crate::settings::peaker::PriorityMin
+ pub fn priority<PP>(self) -> Wrap<W, PP> {
+ Wrap {
+ width: self.width,
+ keep_words: self.keep_words,
+ _priority: PhantomData,
+ }
+ }
+
+ /// Set the keep words option.
+ ///
+ /// If a wrapping point will be in a word, [`Wrap`] will
+ /// preserve a word (if possible) and wrap the string before it.
+ pub fn keep_words(mut self) -> Self {
+ self.keep_words = true;
+ self
+ }
+}
+
+impl Wrap<(), ()> {
+ /// Wrap a given string
+ pub fn wrap_text(text: &str, width: usize, keeping_words: bool) -> String {
+ wrap_text(text, width, keeping_words)
+ }
+}
+
+impl<W, P, R> TableOption<R, CompleteDimensionVecRecords<'static>, ColoredConfig> for Wrap<W, P>
+where
+ W: Measurement<Width>,
+ P: Peaker,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return;
+ }
+
+ let width = self.width.measure(&*records, cfg);
+ let (widths, total) = get_table_widths_with_total(&*records, cfg);
+ if width >= total {
+ return;
+ }
+
+ let priority = P::create();
+ let keep_words = self.keep_words;
+ let widths = wrap_total_width(records, cfg, widths, total, width, keep_words, priority);
+
+ let _ = dims.set_widths(widths);
+ }
+}
+
+impl<W, R> CellOption<R, ColoredConfig> for Wrap<W>
+where
+ W: Measurement<Width>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let width = self.width.measure(&*records, cfg);
+
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_columns) {
+ let is_valid_pos = pos.0 < records.count_rows() && pos.1 < records.count_columns();
+ if !is_valid_pos {
+ continue;
+ }
+
+ let text = records.get_text(pos);
+ let cell_width = string_width_multiline(text);
+ if cell_width <= width {
+ continue;
+ }
+
+ let wrapped = wrap_text(text, width, self.keep_words);
+ records.set(pos, wrapped);
+ }
+ }
+}
+
+fn wrap_total_width<R, P>(
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ mut widths: Vec<usize>,
+ total_width: usize,
+ width: usize,
+ keep_words: bool,
+ priority: P,
+) -> Vec<usize>
+where
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ P: Peaker,
+ for<'a> &'a R: Records,
+{
+ let shape = (records.count_rows(), records.count_columns());
+ let min_widths = get_table_widths(EmptyRecords::from(shape), cfg);
+
+ decrease_widths(&mut widths, &min_widths, total_width, width, priority);
+
+ let points = get_decrease_cell_list(cfg, &widths, &min_widths, shape);
+
+ for ((row, col), width) in points {
+ let mut wrap = Wrap::new(width);
+ wrap.keep_words = keep_words;
+ <Wrap as CellOption<_, _>>::change(wrap, records, cfg, (row, col).into());
+ }
+
+ widths
+}
+
+#[cfg(not(feature = "color"))]
+pub(crate) fn wrap_text(text: &str, width: usize, keep_words: bool) -> String {
+ if width == 0 {
+ return String::new();
+ }
+
+ if keep_words {
+ split_keeping_words(text, width, "\n")
+ } else {
+ chunks(text, width).join("\n")
+ }
+}
+
+#[cfg(feature = "color")]
+pub(crate) fn wrap_text(text: &str, width: usize, keep_words: bool) -> String {
+ use super::util::strip_osc;
+
+ if width == 0 {
+ return String::new();
+ }
+
+ let (text, url): (String, Option<String>) = strip_osc(text);
+ let (prefix, suffix) = build_link_prefix_suffix(url);
+
+ if keep_words {
+ split_keeping_words(&text, width, &prefix, &suffix)
+ } else {
+ chunks(&text, width, &prefix, &suffix).join("\n")
+ }
+}
+
+#[cfg(feature = "color")]
+fn build_link_prefix_suffix(url: Option<String>) -> (String, String) {
+ match url {
+ Some(url) => {
+ // https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
+ let osc8 = "\x1b]8;;";
+ let st = "\x1b\\";
+
+ (format!("{osc8}{url}{st}"), format!("{osc8}{st}"))
+ }
+ None => ("".to_string(), "".to_string()),
+ }
+}
+
+#[cfg(not(feature = "color"))]
+fn chunks(s: &str, width: usize) -> Vec<String> {
+ if width == 0 {
+ return Vec::new();
+ }
+
+ const REPLACEMENT: char = '\u{FFFD}';
+
+ let mut buf = String::with_capacity(width);
+ let mut list = Vec::new();
+ let mut i = 0;
+ for c in s.chars() {
+ let c_width = unicode_width::UnicodeWidthChar::width(c).unwrap_or_default();
+ if i + c_width > width {
+ let count_unknowns = width - i;
+ buf.extend(std::iter::repeat(REPLACEMENT).take(count_unknowns));
+ i += count_unknowns;
+ } else {
+ buf.push(c);
+ i += c_width;
+ }
+
+ if i == width {
+ list.push(buf);
+ buf = String::with_capacity(width);
+ i = 0;
+ }
+ }
+
+ if !buf.is_empty() {
+ list.push(buf);
+ }
+
+ list
+}
+
+#[cfg(feature = "color")]
+fn chunks(s: &str, width: usize, prefix: &str, suffix: &str) -> Vec<String> {
+ use std::fmt::Write;
+
+ if width == 0 {
+ return Vec::new();
+ }
+
+ let mut list = Vec::new();
+ let mut line = String::with_capacity(width);
+ let mut line_width = 0;
+
+ for b in ansi_str::get_blocks(s) {
+ let text_style = b.style();
+ let mut text_slice = b.text();
+ if text_slice.is_empty() {
+ continue;
+ }
+
+ let available_space = width - line_width;
+ if available_space == 0 {
+ list.push(line);
+ line = String::with_capacity(width);
+ line_width = 0;
+ }
+
+ line.push_str(prefix);
+ let _ = write!(&mut line, "{}", text_style.start());
+
+ while !text_slice.is_empty() {
+ let available_space = width - line_width;
+
+ let part_width = unicode_width::UnicodeWidthStr::width(text_slice);
+ if part_width <= available_space {
+ line.push_str(text_slice);
+ line_width += part_width;
+
+ if available_space == 0 {
+ let _ = write!(&mut line, "{}", text_style.end());
+ line.push_str(suffix);
+ list.push(line);
+ line = String::with_capacity(width);
+ line.push_str(prefix);
+ line_width = 0;
+ let _ = write!(&mut line, "{}", text_style.start());
+ }
+
+ break;
+ }
+
+ let (lhs, rhs, (unknowns, split_char)) = split_string_at(text_slice, available_space);
+
+ text_slice = &rhs[split_char..];
+
+ line.push_str(lhs);
+ line_width += unicode_width::UnicodeWidthStr::width(lhs);
+
+ const REPLACEMENT: char = '\u{FFFD}';
+ line.extend(std::iter::repeat(REPLACEMENT).take(unknowns));
+ line_width += unknowns;
+
+ if line_width == width {
+ let _ = write!(&mut line, "{}", text_style.end());
+ line.push_str(suffix);
+ list.push(line);
+ line = String::with_capacity(width);
+ line.push_str(prefix);
+ line_width = 0;
+ let _ = write!(&mut line, "{}", text_style.start());
+ }
+ }
+
+ if line_width > 0 {
+ let _ = write!(&mut line, "{}", text_style.end());
+ }
+ }
+
+ if line_width > 0 {
+ line.push_str(suffix);
+ list.push(line);
+ }
+
+ list
+}
+
+#[cfg(not(feature = "color"))]
+fn split_keeping_words(s: &str, width: usize, sep: &str) -> String {
+ const REPLACEMENT: char = '\u{FFFD}';
+
+ let mut lines = Vec::new();
+ let mut line = String::with_capacity(width);
+ let mut line_width = 0;
+
+ let mut is_first_word = true;
+
+ for word in s.split(' ') {
+ if !is_first_word {
+ let line_has_space = line_width < width;
+ if line_has_space {
+ line.push(' ');
+ line_width += 1;
+ is_first_word = false;
+ }
+ }
+
+ if is_first_word {
+ is_first_word = false;
+ }
+
+ let word_width = unicode_width::UnicodeWidthStr::width(word);
+
+ let line_has_space = line_width + word_width <= width;
+ if line_has_space {
+ line.push_str(word);
+ line_width += word_width;
+ continue;
+ }
+
+ if word_width <= width {
+ // the word can be fit to 'width' so we put it on new line
+
+ line.extend(std::iter::repeat(' ').take(width - line_width));
+ lines.push(line);
+
+ line = String::with_capacity(width);
+ line_width = 0;
+
+ line.push_str(word);
+ line_width += word_width;
+ is_first_word = false;
+ } else {
+ // the word is too long any way so we split it
+
+ let mut word_part = word;
+ while !word_part.is_empty() {
+ let available_space = width - line_width;
+ let (lhs, rhs, (unknowns, split_char)) =
+ split_string_at(word_part, available_space);
+
+ word_part = &rhs[split_char..];
+ line_width += unicode_width::UnicodeWidthStr::width(lhs) + unknowns;
+ is_first_word = false;
+
+ line.push_str(lhs);
+ line.extend(std::iter::repeat(REPLACEMENT).take(unknowns));
+
+ if line_width == width {
+ lines.push(line);
+ line = String::with_capacity(width);
+ line_width = 0;
+ is_first_word = true;
+ }
+ }
+ }
+ }
+
+ if line_width > 0 {
+ line.extend(std::iter::repeat(' ').take(width - line_width));
+ lines.push(line);
+ }
+
+ lines.join(sep)
+}
+
+#[cfg(feature = "color")]
+fn split_keeping_words(text: &str, width: usize, prefix: &str, suffix: &str) -> String {
+ if text.is_empty() || width == 0 {
+ return String::new();
+ }
+
+ let stripped_text = ansi_str::AnsiStr::ansi_strip(text);
+ let mut word_width = 0;
+ let mut word_chars = 0;
+ let mut blocks = parsing::Blocks::new(ansi_str::get_blocks(text));
+ let mut buf = parsing::MultilineBuffer::new(width);
+ buf.set_prefix(prefix);
+ buf.set_suffix(suffix);
+
+ for c in stripped_text.chars() {
+ match c {
+ ' ' => {
+ parsing::handle_word(&mut buf, &mut blocks, word_chars, word_width, 1);
+ word_chars = 0;
+ word_width = 0;
+ }
+ '\n' => {
+ parsing::handle_word(&mut buf, &mut blocks, word_chars, word_width, 1);
+ word_chars = 0;
+ word_width = 0;
+ }
+ _ => {
+ word_width += unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
+ word_chars += 1;
+ }
+ }
+ }
+
+ if word_chars > 0 {
+ parsing::handle_word(&mut buf, &mut blocks, word_chars, word_width, 0);
+ buf.finish_line(&blocks);
+ }
+
+ buf.into_string()
+}
+
+#[cfg(feature = "color")]
+mod parsing {
+ use ansi_str::{AnsiBlock, AnsiBlockIter, Style};
+ use std::fmt::Write;
+
+ pub(super) struct Blocks<'a> {
+ iter: AnsiBlockIter<'a>,
+ current: Option<RelativeBlock<'a>>,
+ }
+
+ impl<'a> Blocks<'a> {
+ pub(super) fn new(iter: AnsiBlockIter<'a>) -> Self {
+ Self {
+ iter,
+ current: None,
+ }
+ }
+
+ pub(super) fn next_block(&mut self) -> Option<RelativeBlock<'a>> {
+ self.current
+ .take()
+ .or_else(|| self.iter.next().map(RelativeBlock::new))
+ }
+ }
+
+ pub(super) struct RelativeBlock<'a> {
+ block: AnsiBlock<'a>,
+ pos: usize,
+ }
+
+ impl<'a> RelativeBlock<'a> {
+ pub(super) fn new(block: AnsiBlock<'a>) -> Self {
+ Self { block, pos: 0 }
+ }
+
+ pub(super) fn get_text(&self) -> &str {
+ &self.block.text()[self.pos..]
+ }
+
+ pub(super) fn get_origin(&self) -> &str {
+ self.block.text()
+ }
+
+ pub(super) fn get_style(&self) -> &Style {
+ self.block.style()
+ }
+ }
+
+ pub(super) struct MultilineBuffer<'a> {
+ buf: String,
+ width_last: usize,
+ width: usize,
+ prefix: &'a str,
+ suffix: &'a str,
+ }
+
+ impl<'a> MultilineBuffer<'a> {
+ pub(super) fn new(width: usize) -> Self {
+ Self {
+ buf: String::new(),
+ width_last: 0,
+ prefix: "",
+ suffix: "",
+ width,
+ }
+ }
+
+ pub(super) fn into_string(self) -> String {
+ self.buf
+ }
+
+ pub(super) fn set_suffix(&mut self, suffix: &'a str) {
+ self.suffix = suffix;
+ }
+
+ pub(super) fn set_prefix(&mut self, prefix: &'a str) {
+ self.prefix = prefix;
+ }
+
+ pub(super) fn max_width(&self) -> usize {
+ self.width
+ }
+
+ pub(super) fn available_width(&self) -> usize {
+ self.width - self.width_last
+ }
+
+ pub(super) fn fill(&mut self, c: char) -> usize {
+ debug_assert_eq!(unicode_width::UnicodeWidthChar::width(c), Some(1));
+
+ let rest_width = self.available_width();
+ for _ in 0..rest_width {
+ self.buf.push(c);
+ }
+
+ rest_width
+ }
+
+ pub(super) fn set_next_line(&mut self, blocks: &Blocks<'_>) {
+ if let Some(block) = &blocks.current {
+ let _ = self
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().end()));
+ }
+
+ self.buf.push_str(self.suffix);
+
+ let _ = self.fill(' ');
+ self.buf.push('\n');
+ self.width_last = 0;
+
+ self.buf.push_str(self.prefix);
+
+ if let Some(block) = &blocks.current {
+ let _ = self
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().start()));
+ }
+ }
+
+ pub(super) fn finish_line(&mut self, blocks: &Blocks<'_>) {
+ if let Some(block) = &blocks.current {
+ let _ = self
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().end()));
+ }
+
+ self.buf.push_str(self.suffix);
+
+ let _ = self.fill(' ');
+ self.width_last = 0;
+ }
+
+ pub(super) fn read_chars(&mut self, block: &RelativeBlock<'_>, n: usize) -> (usize, usize) {
+ let mut count_chars = 0;
+ let mut count_bytes = 0;
+ for c in block.get_text().chars() {
+ if count_chars == n {
+ break;
+ }
+
+ count_chars += 1;
+ count_bytes += c.len_utf8();
+
+ let cwidth = unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
+
+ let available_space = self.width - self.width_last;
+ if available_space == 0 {
+ let _ = self
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().end()));
+ self.buf.push_str(self.suffix);
+ self.buf.push('\n');
+ self.buf.push_str(self.prefix);
+ let _ = self
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().start()));
+ self.width_last = 0;
+ }
+
+ let is_enough_space = self.width_last + cwidth <= self.width;
+ if !is_enough_space {
+ // thereatically a cwidth can be 2 but buf_width is 1
+ // but it handled here too;
+
+ const REPLACEMENT: char = '\u{FFFD}';
+ let _ = self.fill(REPLACEMENT);
+ self.width_last = self.width;
+ } else {
+ self.buf.push(c);
+ self.width_last += cwidth;
+ }
+ }
+
+ (count_chars, count_bytes)
+ }
+
+ pub(super) fn read_chars_unchecked(
+ &mut self,
+ block: &RelativeBlock<'_>,
+ n: usize,
+ ) -> (usize, usize) {
+ let mut count_chars = 0;
+ let mut count_bytes = 0;
+ for c in block.get_text().chars() {
+ if count_chars == n {
+ break;
+ }
+
+ count_chars += 1;
+ count_bytes += c.len_utf8();
+
+ let cwidth = unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
+ self.width_last += cwidth;
+
+ self.buf.push(c);
+ }
+
+ debug_assert!(self.width_last <= self.width);
+
+ (count_chars, count_bytes)
+ }
+ }
+
+ pub(super) fn read_chars(buf: &mut MultilineBuffer<'_>, blocks: &mut Blocks<'_>, n: usize) {
+ let mut n = n;
+ while n > 0 {
+ let is_new_block = blocks.current.is_none();
+ let mut block = blocks.next_block().expect("Must never happen");
+ if is_new_block {
+ buf.buf.push_str(buf.prefix);
+ let _ = buf
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().start()));
+ }
+
+ let (read_count, read_bytes) = buf.read_chars(&block, n);
+
+ if block.pos + read_bytes == block.get_origin().len() {
+ let _ = buf
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().end()));
+ } else {
+ block.pos += read_bytes;
+ blocks.current = Some(block);
+ }
+
+ n -= read_count;
+ }
+ }
+
+ pub(super) fn read_chars_unchecked(
+ buf: &mut MultilineBuffer<'_>,
+ blocks: &mut Blocks<'_>,
+ n: usize,
+ ) {
+ let mut n = n;
+ while n > 0 {
+ let is_new_block = blocks.current.is_none();
+ let mut block = blocks.next_block().expect("Must never happen");
+
+ if is_new_block {
+ buf.buf.push_str(buf.prefix);
+ let _ = buf
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().start()));
+ }
+
+ let (read_count, read_bytes) = buf.read_chars_unchecked(&block, n);
+
+ if block.pos + read_bytes == block.get_origin().len() {
+ let _ = buf
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().end()));
+ } else {
+ block.pos += read_bytes;
+ blocks.current = Some(block);
+ }
+
+ n -= read_count;
+ }
+ }
+
+ pub(super) fn handle_word(
+ buf: &mut MultilineBuffer<'_>,
+ blocks: &mut Blocks<'_>,
+ word_chars: usize,
+ word_width: usize,
+ additional_read: usize,
+ ) {
+ if word_chars > 0 {
+ let has_line_space = word_width <= buf.available_width();
+ let is_word_too_big = word_width > buf.max_width();
+
+ if is_word_too_big {
+ read_chars(buf, blocks, word_chars + additional_read);
+ } else if has_line_space {
+ read_chars_unchecked(buf, blocks, word_chars);
+ if additional_read > 0 {
+ read_chars(buf, blocks, additional_read);
+ }
+ } else {
+ buf.set_next_line(&*blocks);
+ read_chars_unchecked(buf, blocks, word_chars);
+ if additional_read > 0 {
+ read_chars(buf, blocks, additional_read);
+ }
+ }
+
+ return;
+ }
+
+ let has_current_line_space = additional_read <= buf.available_width();
+ if has_current_line_space {
+ read_chars_unchecked(buf, blocks, additional_read);
+ } else {
+ buf.set_next_line(&*blocks);
+ read_chars_unchecked(buf, blocks, additional_read);
+ }
+ }
+}
+
+fn split_string_at(text: &str, at: usize) -> (&str, &str, (usize, usize)) {
+ let (length, count_unknowns, split_char_size) = split_at_pos(text, at);
+ let (lhs, rhs) = text.split_at(length);
+
+ (lhs, rhs, (count_unknowns, split_char_size))
+}
+
+fn decrease_widths<F>(
+ widths: &mut [usize],
+ min_widths: &[usize],
+ total_width: usize,
+ mut width: usize,
+ mut peeaker: F,
+) where
+ F: Peaker,
+{
+ let mut empty_list = 0;
+ for col in 0..widths.len() {
+ if widths[col] == 0 || widths[col] <= min_widths[col] {
+ empty_list += 1;
+ }
+ }
+
+ while width != total_width {
+ if empty_list == widths.len() {
+ break;
+ }
+
+ let col = match peeaker.peak(min_widths, widths) {
+ Some(col) => col,
+ None => break,
+ };
+
+ if widths[col] == 0 || widths[col] <= min_widths[col] {
+ continue;
+ }
+
+ widths[col] -= 1;
+
+ if widths[col] == 0 || widths[col] <= min_widths[col] {
+ empty_list += 1;
+ }
+
+ width += 1;
+ }
+}
+
+fn get_decrease_cell_list(
+ cfg: &SpannedConfig,
+ widths: &[usize],
+ min_widths: &[usize],
+ shape: (usize, usize),
+) -> Vec<((usize, usize), usize)> {
+ let mut points = Vec::new();
+ (0..shape.1).for_each(|col| {
+ (0..shape.0)
+ .filter(|&row| cfg.is_cell_visible((row, col)))
+ .for_each(|row| {
+ let (width, width_min) = match cfg.get_column_span((row, col)) {
+ Some(span) => {
+ let width = (col..col + span).map(|i| widths[i]).sum::<usize>();
+ let min_width = (col..col + span).map(|i| min_widths[i]).sum::<usize>();
+ let count_borders = count_borders(cfg, col, col + span, shape.1);
+ (width + count_borders, min_width + count_borders)
+ }
+ None => (widths[col], min_widths[col]),
+ };
+
+ if width >= width_min {
+ let padding = cfg.get_padding((row, col).into());
+ let width = width.saturating_sub(padding.left.size + padding.right.size);
+
+ points.push(((row, col), width));
+ }
+ });
+ });
+
+ points
+}
+
+fn count_borders(cfg: &SpannedConfig, start: usize, end: usize, count_columns: usize) -> usize {
+ (start..end)
+ .skip(1)
+ .filter(|&i| cfg.has_vertical(i, count_columns))
+ .count()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn split_test() {
+ #[cfg(not(feature = "color"))]
+ let split = |text, width| chunks(text, width).join("\n");
+
+ #[cfg(feature = "color")]
+ let split = |text, width| chunks(text, width, "", "").join("\n");
+
+ assert_eq!(split("123456", 0), "");
+
+ assert_eq!(split("123456", 1), "1\n2\n3\n4\n5\n6");
+ assert_eq!(split("123456", 2), "12\n34\n56");
+ assert_eq!(split("12345", 2), "12\n34\n5");
+ assert_eq!(split("123456", 6), "123456");
+ assert_eq!(split("123456", 10), "123456");
+
+ assert_eq!(split("😳😳😳😳😳", 1), "�\n�\n�\n�\n�");
+ assert_eq!(split("😳😳😳😳😳", 2), "😳\n😳\n😳\n😳\n😳");
+ assert_eq!(split("😳😳😳😳😳", 3), "😳�\n😳�\n😳");
+ assert_eq!(split("😳😳😳😳😳", 6), "😳😳😳\n😳😳");
+ assert_eq!(split("😳😳😳😳😳", 20), "😳😳😳😳😳");
+
+ assert_eq!(split("😳123😳", 1), "�\n1\n2\n3\n�");
+ assert_eq!(split("😳12😳3", 1), "�\n1\n2\n�\n3");
+ }
+
+ #[test]
+ fn chunks_test() {
+ #[allow(clippy::redundant_closure)]
+ #[cfg(not(feature = "color"))]
+ let chunks = |text, width| chunks(text, width);
+
+ #[cfg(feature = "color")]
+ let chunks = |text, width| chunks(text, width, "", "");
+
+ assert_eq!(chunks("123456", 0), [""; 0]);
+
+ assert_eq!(chunks("123456", 1), ["1", "2", "3", "4", "5", "6"]);
+ assert_eq!(chunks("123456", 2), ["12", "34", "56"]);
+ assert_eq!(chunks("12345", 2), ["12", "34", "5"]);
+
+ assert_eq!(chunks("😳😳😳😳😳", 1), ["�", "�", "�", "�", "�"]);
+ assert_eq!(chunks("😳😳😳😳😳", 2), ["😳", "😳", "😳", "😳", "😳"]);
+ assert_eq!(chunks("😳😳😳😳😳", 3), ["😳�", "😳�", "😳"]);
+ }
+
+ #[cfg(not(feature = "color"))]
+ #[test]
+ fn split_by_line_keeping_words_test() {
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "\n");
+
+ assert_eq!(split_keeping_words("123456", 1), "1\n2\n3\n4\n5\n6");
+ assert_eq!(split_keeping_words("123456", 2), "12\n34\n56");
+ assert_eq!(split_keeping_words("12345", 2), "12\n34\n5 ");
+
+ assert_eq!(split_keeping_words("😳😳😳😳😳", 1), "�\n�\n�\n�\n�");
+
+ assert_eq!(split_keeping_words("111 234 1", 4), "111 \n234 \n1 ");
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn split_by_line_keeping_words_test() {
+ #[cfg(feature = "color")]
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "", "");
+
+ assert_eq!(split_keeping_words("123456", 1), "1\n2\n3\n4\n5\n6");
+ assert_eq!(split_keeping_words("123456", 2), "12\n34\n56");
+ assert_eq!(split_keeping_words("12345", 2), "12\n34\n5 ");
+
+ assert_eq!(split_keeping_words("😳😳😳😳😳", 1), "�\n�\n�\n�\n�");
+
+ assert_eq!(split_keeping_words("111 234 1", 4), "111 \n234 \n1 ");
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn split_by_line_keeping_words_color_test() {
+ #[cfg(feature = "color")]
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "", "");
+
+ #[cfg(not(feature = "color"))]
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "\n");
+
+ let text = "\u{1b}[36mJapanese “vacancy” button\u{1b}[0m";
+
+ assert_eq!(split_keeping_words(text, 2), "\u{1b}[36mJa\u{1b}[39m\n\u{1b}[36mpa\u{1b}[39m\n\u{1b}[36mne\u{1b}[39m\n\u{1b}[36mse\u{1b}[39m\n\u{1b}[36m “\u{1b}[39m\n\u{1b}[36mva\u{1b}[39m\n\u{1b}[36mca\u{1b}[39m\n\u{1b}[36mnc\u{1b}[39m\n\u{1b}[36my”\u{1b}[39m\n\u{1b}[36m b\u{1b}[39m\n\u{1b}[36mut\u{1b}[39m\n\u{1b}[36mto\u{1b}[39m\n\u{1b}[36mn\u{1b}[39m ");
+ assert_eq!(split_keeping_words(text, 1), "\u{1b}[36mJ\u{1b}[39m\n\u{1b}[36ma\u{1b}[39m\n\u{1b}[36mp\u{1b}[39m\n\u{1b}[36ma\u{1b}[39m\n\u{1b}[36mn\u{1b}[39m\n\u{1b}[36me\u{1b}[39m\n\u{1b}[36ms\u{1b}[39m\n\u{1b}[36me\u{1b}[39m\n\u{1b}[36m \u{1b}[39m\n\u{1b}[36m“\u{1b}[39m\n\u{1b}[36mv\u{1b}[39m\n\u{1b}[36ma\u{1b}[39m\n\u{1b}[36mc\u{1b}[39m\n\u{1b}[36ma\u{1b}[39m\n\u{1b}[36mn\u{1b}[39m\n\u{1b}[36mc\u{1b}[39m\n\u{1b}[36my\u{1b}[39m\n\u{1b}[36m”\u{1b}[39m\n\u{1b}[36m \u{1b}[39m\n\u{1b}[36mb\u{1b}[39m\n\u{1b}[36mu\u{1b}[39m\n\u{1b}[36mt\u{1b}[39m\n\u{1b}[36mt\u{1b}[39m\n\u{1b}[36mo\u{1b}[39m\n\u{1b}[36mn\u{1b}[39m");
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn split_by_line_keeping_words_color_2_test() {
+ use ansi_str::AnsiStr;
+
+ #[cfg(feature = "color")]
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "", "");
+
+ #[cfg(not(feature = "color"))]
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "\n");
+
+ let text = "\u{1b}[37mTigre Ecuador OMYA Andina 3824909999 Calcium carbonate Colombia\u{1b}[0m";
+
+ assert_eq!(
+ split_keeping_words(text, 2)
+ .ansi_split("\n")
+ .collect::<Vec<_>>(),
+ [
+ "\u{1b}[37mTi\u{1b}[39m",
+ "\u{1b}[37mgr\u{1b}[39m",
+ "\u{1b}[37me \u{1b}[39m",
+ "\u{1b}[37mEc\u{1b}[39m",
+ "\u{1b}[37mua\u{1b}[39m",
+ "\u{1b}[37mdo\u{1b}[39m",
+ "\u{1b}[37mr \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mOM\u{1b}[39m",
+ "\u{1b}[37mYA\u{1b}[39m",
+ "\u{1b}[37m A\u{1b}[39m",
+ "\u{1b}[37mnd\u{1b}[39m",
+ "\u{1b}[37min\u{1b}[39m",
+ "\u{1b}[37ma \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m38\u{1b}[39m",
+ "\u{1b}[37m24\u{1b}[39m",
+ "\u{1b}[37m90\u{1b}[39m",
+ "\u{1b}[37m99\u{1b}[39m",
+ "\u{1b}[37m99\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mCa\u{1b}[39m",
+ "\u{1b}[37mlc\u{1b}[39m",
+ "\u{1b}[37miu\u{1b}[39m",
+ "\u{1b}[37mm \u{1b}[39m",
+ "\u{1b}[37mca\u{1b}[39m",
+ "\u{1b}[37mrb\u{1b}[39m",
+ "\u{1b}[37mon\u{1b}[39m",
+ "\u{1b}[37mat\u{1b}[39m",
+ "\u{1b}[37me \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mCo\u{1b}[39m",
+ "\u{1b}[37mlo\u{1b}[39m",
+ "\u{1b}[37mmb\u{1b}[39m",
+ "\u{1b}[37mia\u{1b}[39m"
+ ]
+ );
+
+ assert_eq!(
+ split_keeping_words(text, 1)
+ .ansi_split("\n")
+ .collect::<Vec<_>>(),
+ [
+ "\u{1b}[37mT\u{1b}[39m",
+ "\u{1b}[37mi\u{1b}[39m",
+ "\u{1b}[37mg\u{1b}[39m",
+ "\u{1b}[37mr\u{1b}[39m",
+ "\u{1b}[37me\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mE\u{1b}[39m",
+ "\u{1b}[37mc\u{1b}[39m",
+ "\u{1b}[37mu\u{1b}[39m",
+ "\u{1b}[37ma\u{1b}[39m",
+ "\u{1b}[37md\u{1b}[39m",
+ "\u{1b}[37mo\u{1b}[39m",
+ "\u{1b}[37mr\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mO\u{1b}[39m",
+ "\u{1b}[37mM\u{1b}[39m",
+ "\u{1b}[37mY\u{1b}[39m",
+ "\u{1b}[37mA\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mA\u{1b}[39m",
+ "\u{1b}[37mn\u{1b}[39m",
+ "\u{1b}[37md\u{1b}[39m",
+ "\u{1b}[37mi\u{1b}[39m",
+ "\u{1b}[37mn\u{1b}[39m",
+ "\u{1b}[37ma\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m3\u{1b}[39m",
+ "\u{1b}[37m8\u{1b}[39m",
+ "\u{1b}[37m2\u{1b}[39m",
+ "\u{1b}[37m4\u{1b}[39m",
+ "\u{1b}[37m9\u{1b}[39m",
+ "\u{1b}[37m0\u{1b}[39m",
+ "\u{1b}[37m9\u{1b}[39m",
+ "\u{1b}[37m9\u{1b}[39m",
+ "\u{1b}[37m9\u{1b}[39m",
+ "\u{1b}[37m9\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mC\u{1b}[39m",
+ "\u{1b}[37ma\u{1b}[39m",
+ "\u{1b}[37ml\u{1b}[39m",
+ "\u{1b}[37mc\u{1b}[39m",
+ "\u{1b}[37mi\u{1b}[39m",
+ "\u{1b}[37mu\u{1b}[39m",
+ "\u{1b}[37mm\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mc\u{1b}[39m",
+ "\u{1b}[37ma\u{1b}[39m",
+ "\u{1b}[37mr\u{1b}[39m",
+ "\u{1b}[37mb\u{1b}[39m",
+ "\u{1b}[37mo\u{1b}[39m",
+ "\u{1b}[37mn\u{1b}[39m",
+ "\u{1b}[37ma\u{1b}[39m",
+ "\u{1b}[37mt\u{1b}[39m",
+ "\u{1b}[37me\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mC\u{1b}[39m",
+ "\u{1b}[37mo\u{1b}[39m",
+ "\u{1b}[37ml\u{1b}[39m",
+ "\u{1b}[37mo\u{1b}[39m",
+ "\u{1b}[37mm\u{1b}[39m",
+ "\u{1b}[37mb\u{1b}[39m",
+ "\u{1b}[37mi\u{1b}[39m",
+ "\u{1b}[37ma\u{1b}[39m"
+ ]
+ )
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn split_by_line_keeping_words_color_3_test() {
+ let split = |text, width| split_keeping_words(text, width, "", "");
+ assert_eq!(
+ split(
+ "\u{1b}[37m🚵🏻🚵🏻🚵🏻🚵🏻🚵🏻🚵🏻🚵🏻🚵🏻🚵🏻🚵🏻\u{1b}[0m",
+ 3,
+ ),
+ "\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m",
+ );
+ assert_eq!(
+ split("\u{1b}[37mthis is a long sentence\u{1b}[0m", 7),
+ "\u{1b}[37mthis is\u{1b}[39m\n\u{1b}[37m a long\u{1b}[39m\n\u{1b}[37m senten\u{1b}[39m\n\u{1b}[37mce\u{1b}[39m "
+ );
+ assert_eq!(
+ split("\u{1b}[37mHello World\u{1b}[0m", 7),
+ "\u{1b}[37mHello \u{1b}[39m \n\u{1b}[37mWorld\u{1b}[39m "
+ );
+ assert_eq!(
+ split("\u{1b}[37mHello Wo\u{1b}[37mrld\u{1b}[0m", 7),
+ "\u{1b}[37mHello \u{1b}[39m \n\u{1b}[37mWo\u{1b}[39m\u{1b}[37mrld\u{1b}[39m "
+ );
+ assert_eq!(
+ split("\u{1b}[37mHello Wo\u{1b}[37mrld\u{1b}[0m", 8),
+ "\u{1b}[37mHello \u{1b}[39m \n\u{1b}[37mWo\u{1b}[39m\u{1b}[37mrld\u{1b}[39m "
+ );
+ }
+
+ #[cfg(not(feature = "color"))]
+ #[test]
+ fn split_keeping_words_4_test() {
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "\n");
+
+ assert_eq!(split_keeping_words("12345678", 3,), "123\n456\n78 ");
+ assert_eq!(split_keeping_words("12345678", 2,), "12\n34\n56\n78");
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn split_keeping_words_4_test() {
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "", "");
+
+ #[cfg(not(feature = "color"))]
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "\n");
+
+ assert_eq!(split_keeping_words("12345678", 3,), "123\n456\n78 ");
+ assert_eq!(split_keeping_words("12345678", 2,), "12\n34\n56\n78");
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn chunks_test_with_prefix_and_suffix() {
+ assert_eq!(chunks("123456", 0, "^", "$"), ["^$"; 0]);
+
+ assert_eq!(
+ chunks("123456", 1, "^", "$"),
+ ["^1$", "^2$", "^3$", "^4$", "^5$", "^6$"]
+ );
+ assert_eq!(chunks("123456", 2, "^", "$"), ["^12$", "^34$", "^56$"]);
+ assert_eq!(chunks("12345", 2, "^", "$"), ["^12$", "^34$", "^5$"]);
+
+ assert_eq!(
+ chunks("😳😳😳😳😳", 1, "^", "$"),
+ ["^�$", "^�$", "^�$", "^�$", "^�$"]
+ );
+ assert_eq!(
+ chunks("😳😳😳😳😳", 2, "^", "$"),
+ ["^😳$", "^😳$", "^😳$", "^😳$", "^😳$"]
+ );
+ assert_eq!(
+ chunks("😳😳😳😳😳", 3, "^", "$"),
+ ["^😳�$", "^😳�$", "^😳$"]
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn split_by_line_keeping_words_test_with_prefix_and_suffix() {
+ assert_eq!(
+ split_keeping_words("123456", 1, "^", "$"),
+ "^1$\n^2$\n^3$\n^4$\n^5$\n^6$"
+ );
+ assert_eq!(
+ split_keeping_words("123456", 2, "^", "$"),
+ "^12$\n^34$\n^56$"
+ );
+ assert_eq!(
+ split_keeping_words("12345", 2, "^", "$"),
+ "^12$\n^34$\n^5$ "
+ );
+
+ assert_eq!(
+ split_keeping_words("😳😳😳😳😳", 1, "^", "$"),
+ "^�$\n^�$\n^�$\n^�$\n^�$"
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn split_by_line_keeping_words_color_2_test_with_prefix_and_suffix() {
+ use ansi_str::AnsiStr;
+
+ let text = "\u{1b}[37mTigre Ecuador OMYA Andina 3824909999 Calcium carbonate Colombia\u{1b}[0m";
+
+ assert_eq!(
+ split_keeping_words(text, 2, "^", "$")
+ .ansi_split("\n")
+ .collect::<Vec<_>>(),
+ [
+ "^\u{1b}[37mTi\u{1b}[39m$",
+ "^\u{1b}[37mgr\u{1b}[39m$",
+ "^\u{1b}[37me \u{1b}[39m$",
+ "^\u{1b}[37mEc\u{1b}[39m$",
+ "^\u{1b}[37mua\u{1b}[39m$",
+ "^\u{1b}[37mdo\u{1b}[39m$",
+ "^\u{1b}[37mr \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mOM\u{1b}[39m$",
+ "^\u{1b}[37mYA\u{1b}[39m$",
+ "^\u{1b}[37m A\u{1b}[39m$",
+ "^\u{1b}[37mnd\u{1b}[39m$",
+ "^\u{1b}[37min\u{1b}[39m$",
+ "^\u{1b}[37ma \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m38\u{1b}[39m$",
+ "^\u{1b}[37m24\u{1b}[39m$",
+ "^\u{1b}[37m90\u{1b}[39m$",
+ "^\u{1b}[37m99\u{1b}[39m$",
+ "^\u{1b}[37m99\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mCa\u{1b}[39m$",
+ "^\u{1b}[37mlc\u{1b}[39m$",
+ "^\u{1b}[37miu\u{1b}[39m$",
+ "^\u{1b}[37mm \u{1b}[39m$",
+ "^\u{1b}[37mca\u{1b}[39m$",
+ "^\u{1b}[37mrb\u{1b}[39m$",
+ "^\u{1b}[37mon\u{1b}[39m$",
+ "^\u{1b}[37mat\u{1b}[39m$",
+ "^\u{1b}[37me \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mCo\u{1b}[39m$",
+ "^\u{1b}[37mlo\u{1b}[39m$",
+ "^\u{1b}[37mmb\u{1b}[39m$",
+ "^\u{1b}[37mia\u{1b}[39m$"
+ ]
+ );
+
+ assert_eq!(
+ split_keeping_words(text, 1, "^", "$")
+ .ansi_split("\n")
+ .collect::<Vec<_>>(),
+ [
+ "^\u{1b}[37mT\u{1b}[39m$",
+ "^\u{1b}[37mi\u{1b}[39m$",
+ "^\u{1b}[37mg\u{1b}[39m$",
+ "^\u{1b}[37mr\u{1b}[39m$",
+ "^\u{1b}[37me\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mE\u{1b}[39m$",
+ "^\u{1b}[37mc\u{1b}[39m$",
+ "^\u{1b}[37mu\u{1b}[39m$",
+ "^\u{1b}[37ma\u{1b}[39m$",
+ "^\u{1b}[37md\u{1b}[39m$",
+ "^\u{1b}[37mo\u{1b}[39m$",
+ "^\u{1b}[37mr\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mO\u{1b}[39m$",
+ "^\u{1b}[37mM\u{1b}[39m$",
+ "^\u{1b}[37mY\u{1b}[39m$",
+ "^\u{1b}[37mA\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mA\u{1b}[39m$",
+ "^\u{1b}[37mn\u{1b}[39m$",
+ "^\u{1b}[37md\u{1b}[39m$",
+ "^\u{1b}[37mi\u{1b}[39m$",
+ "^\u{1b}[37mn\u{1b}[39m$",
+ "^\u{1b}[37ma\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m3\u{1b}[39m$",
+ "^\u{1b}[37m8\u{1b}[39m$",
+ "^\u{1b}[37m2\u{1b}[39m$",
+ "^\u{1b}[37m4\u{1b}[39m$",
+ "^\u{1b}[37m9\u{1b}[39m$",
+ "^\u{1b}[37m0\u{1b}[39m$",
+ "^\u{1b}[37m9\u{1b}[39m$",
+ "^\u{1b}[37m9\u{1b}[39m$",
+ "^\u{1b}[37m9\u{1b}[39m$",
+ "^\u{1b}[37m9\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mC\u{1b}[39m$",
+ "^\u{1b}[37ma\u{1b}[39m$",
+ "^\u{1b}[37ml\u{1b}[39m$",
+ "^\u{1b}[37mc\u{1b}[39m$",
+ "^\u{1b}[37mi\u{1b}[39m$",
+ "^\u{1b}[37mu\u{1b}[39m$",
+ "^\u{1b}[37mm\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mc\u{1b}[39m$",
+ "^\u{1b}[37ma\u{1b}[39m$",
+ "^\u{1b}[37mr\u{1b}[39m$",
+ "^\u{1b}[37mb\u{1b}[39m$",
+ "^\u{1b}[37mo\u{1b}[39m$",
+ "^\u{1b}[37mn\u{1b}[39m$",
+ "^\u{1b}[37ma\u{1b}[39m$",
+ "^\u{1b}[37mt\u{1b}[39m$",
+ "^\u{1b}[37me\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mC\u{1b}[39m$",
+ "^\u{1b}[37mo\u{1b}[39m$",
+ "^\u{1b}[37ml\u{1b}[39m$",
+ "^\u{1b}[37mo\u{1b}[39m$",
+ "^\u{1b}[37mm\u{1b}[39m$",
+ "^\u{1b}[37mb\u{1b}[39m$",
+ "^\u{1b}[37mi\u{1b}[39m$",
+ "^\u{1b}[37ma\u{1b}[39m$"
+ ]
+ )
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn chunks_wrap_2() {
+ let text = "\u{1b}[30mDebian\u{1b}[0m\u{1b}[31mDebian\u{1b}[0m\u{1b}[32mDebian\u{1b}[0m\u{1b}[33mDebian\u{1b}[0m\u{1b}[34mDebian\u{1b}[0m\u{1b}[35mDebian\u{1b}[0m\u{1b}[36mDebian\u{1b}[0m\u{1b}[37mDebian\u{1b}[0m\u{1b}[40mDebian\u{1b}[0m\u{1b}[41mDebian\u{1b}[0m\u{1b}[42mDebian\u{1b}[0m\u{1b}[43mDebian\u{1b}[0m\u{1b}[44mDebian\u{1b}[0m";
+ assert_eq!(
+ chunks(text, 30, "", ""),
+ [
+ "\u{1b}[30mDebian\u{1b}[39m\u{1b}[31mDebian\u{1b}[39m\u{1b}[32mDebian\u{1b}[39m\u{1b}[33mDebian\u{1b}[39m\u{1b}[34mDebian\u{1b}[39m",
+ "\u{1b}[35mDebian\u{1b}[39m\u{1b}[36mDebian\u{1b}[39m\u{1b}[37mDebian\u{1b}[39m\u{1b}[40mDebian\u{1b}[49m\u{1b}[41mDebian\u{1b}[49m",
+ "\u{1b}[42mDebian\u{1b}[49m\u{1b}[43mDebian\u{1b}[49m\u{1b}[44mDebian\u{1b}[49m",
+ ]
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn chunks_wrap_3() {
+ let text = "\u{1b}[37mCreate bytes from the \u{1b}[0m\u{1b}[7;34marg\u{1b}[0m\u{1b}[37muments.\u{1b}[0m";
+
+ assert_eq!(
+ chunks(text, 22, "", ""),
+ [
+ "\u{1b}[37mCreate bytes from the \u{1b}[39m",
+ "\u{1b}[7m\u{1b}[34marg\u{1b}[27m\u{1b}[39m\u{1b}[37muments.\u{1b}[39m"
+ ]
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn chunks_wrap_3_keeping_words() {
+ let text = "\u{1b}[37mCreate bytes from the \u{1b}[0m\u{1b}[7;34marg\u{1b}[0m\u{1b}[37muments.\u{1b}[0m";
+
+ assert_eq!(
+ split_keeping_words(text, 22, "", ""),
+ "\u{1b}[37mCreate bytes from the \u{1b}[39m\n\u{1b}[7m\u{1b}[34marg\u{1b}[27m\u{1b}[39m\u{1b}[37muments.\u{1b}[39m "
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn chunks_wrap_4() {
+ let text = "\u{1b}[37mReturns the floor of a number (l\u{1b}[0m\u{1b}[41;37marg\u{1b}[0m\u{1b}[37mest integer less than or equal to that number).\u{1b}[0m";
+
+ assert_eq!(
+ chunks(text, 10, "", ""),
+ [
+ "\u{1b}[37mReturns th\u{1b}[39m",
+ "\u{1b}[37me floor of\u{1b}[39m",
+ "\u{1b}[37m a number \u{1b}[39m",
+ "\u{1b}[37m(l\u{1b}[39m\u{1b}[37m\u{1b}[41marg\u{1b}[39m\u{1b}[49m\u{1b}[37mest i\u{1b}[39m",
+ "\u{1b}[37mnteger les\u{1b}[39m",
+ "\u{1b}[37ms than or \u{1b}[39m",
+ "\u{1b}[37mequal to t\u{1b}[39m",
+ "\u{1b}[37mhat number\u{1b}[39m",
+ "\u{1b}[37m).\u{1b}[39m",
+ ]
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn chunks_wrap_4_keeping_words() {
+ let text = "\u{1b}[37mReturns the floor of a number (l\u{1b}[0m\u{1b}[41;37marg\u{1b}[0m\u{1b}[37mest integer less than or equal to that number).\u{1b}[0m";
+ assert_eq!(
+ split_keeping_words(text, 10, "", ""),
+ concat!(
+ "\u{1b}[37mReturns \u{1b}[39m \n",
+ "\u{1b}[37mthe floor \u{1b}[39m\n",
+ "\u{1b}[37mof a \u{1b}[39m \n",
+ "\u{1b}[37mnumber \u{1b}[39m \n",
+ "\u{1b}[37m(l\u{1b}[39m\u{1b}[37m\u{1b}[41marg\u{1b}[39m\u{1b}[49m\u{1b}[37mest \u{1b}[39m \n",
+ "\u{1b}[37minteger \u{1b}[39m \n",
+ "\u{1b}[37mless than \u{1b}[39m\n",
+ "\u{1b}[37mor equal \u{1b}[39m \n",
+ "\u{1b}[37mto that \u{1b}[39m \n",
+ "\u{1b}[37mnumber).\u{1b}[39m ",
+ )
+ );
+ }
+}
+
+// \u{1b}[37mReturns \u{1b}[39m\n
+// \u{1b}[37mthe floor \u{1b}[39m\n
+// \u{1b}[37mof a \u{1b}[39m\n
+// \u{1b}[37mnumber \u{1b}[39m\u{1b}[49m\n
+// \u{1b}[37m\u{1b}[41m(l\u{1b}[39m\u{1b}[37m\u{1b}[41marg\u{1b}[39m\u{1b}[49m\u{1b}[37mest \u{1b}[39m\n
+// \u{1b}[37minteger \u{1b}[39m\n
+// \u{1b}[37mless than \u{1b}[39m\n
+// \u{1b}[37mor equal \u{1b}[39m\n
+// \u{1b}[37mto that \u{1b}[39m\n
+// \u{1b}[37mnumber).\u{1b}[39m "
+
+//
+//
+
+// \u{1b}[37mReturns \u{1b}[39m\n
+// \u{1b}[37mthe floor \u{1b}[39m\n
+// \u{1b}[37mof a \u{1b}[39m\n
+// \u{1b}[37mnumber \u{1b}[39m\u{1b}[49m\n
+// \u{1b}[37m\u{1b}[41m(l\u{1b}[39m\u{1b}[37m\u{1b}[41marg\u{1b}[39m\u{1b}[49m\u{1b}[37mest \u{1b}[39m\n
+// \u{1b}[37minteger \u{1b}[39m\n
+// \u{1b}[37mless than \u{1b}[39m\n
+// \u{1b}[37mor equal \u{1b}[39m\n
+// \u{1b}[37mto that \u{1b}[39m\n
+// \u{1b}[37mnumber).\u{1b}[39m "
+
+// "\u{1b}[37mReturns\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37mthe\u{1b}[37m floor\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37mof\u{1b}[37m a\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37mnumber\u{1b}[37m \u{1b}[39m\u{1b}[49m\n
+// \u{1b}[37m\u{1b}[41m(l\u{1b}[39m\u{1b}[37m\u{1b}[41marg\u{1b}[39m\u{1b}[49m\u{1b}[37mest\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37minteger\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37mless\u{1b}[37m than\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37mor\u{1b}[37m equal\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37mto\u{1b}[37m that\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37mnumber).\u{1b}[39m "
diff --git a/vendor/tabled/src/tabled.rs b/vendor/tabled/src/tabled.rs
new file mode 100644
index 000000000..636173dc0
--- /dev/null
+++ b/vendor/tabled/src/tabled.rs
@@ -0,0 +1,150 @@
+use std::borrow::Cow;
+
+/// Tabled a trait responsible for providing a header fields and a row fields.
+///
+/// It's urgent that `header` len is equal to `fields` len.
+///
+/// ```text
+/// Self::headers().len() == self.fields().len()
+/// ```
+pub trait Tabled {
+ /// A length of fields and headers,
+ /// which must be the same.
+ const LENGTH: usize;
+
+ /// Fields method must return a list of cells.
+ ///
+ /// The cells will be placed in the same row, preserving the order.
+ fn fields(&self) -> Vec<Cow<'_, str>>;
+ /// Headers must return a list of column names.
+ fn headers() -> Vec<Cow<'static, str>>;
+}
+
+impl<T> Tabled for &T
+where
+ T: Tabled,
+{
+ const LENGTH: usize = T::LENGTH;
+
+ fn fields(&self) -> Vec<Cow<'_, str>> {
+ T::fields(self)
+ }
+ fn headers() -> Vec<Cow<'static, str>> {
+ T::headers()
+ }
+}
+
+impl<T> Tabled for Box<T>
+where
+ T: Tabled,
+{
+ const LENGTH: usize = T::LENGTH;
+
+ fn fields(&self) -> Vec<Cow<'_, str>> {
+ T::fields(self)
+ }
+ fn headers() -> Vec<Cow<'static, str>> {
+ T::headers()
+ }
+}
+
+macro_rules! tuple_table {
+ ( $($name:ident)+ ) => {
+ impl<$($name: Tabled),+> Tabled for ($($name,)+){
+ const LENGTH: usize = $($name::LENGTH+)+ 0;
+
+ fn fields(&self) -> Vec<Cow<'_, str>> {
+ #![allow(non_snake_case)]
+ let ($($name,)+) = self;
+ let mut fields = Vec::with_capacity(Self::LENGTH);
+ $(fields.append(&mut $name.fields());)+
+ fields
+ }
+
+ fn headers() -> Vec<Cow<'static, str>> {
+ let mut fields = Vec::with_capacity(Self::LENGTH);
+ $(fields.append(&mut $name::headers());)+
+ fields
+ }
+ }
+ };
+}
+
+tuple_table! { A }
+tuple_table! { A B }
+tuple_table! { A B C }
+tuple_table! { A B C D }
+tuple_table! { A B C D E }
+tuple_table! { A B C D E F }
+
+macro_rules! default_table {
+ ( $t:ty ) => {
+ impl Tabled for $t {
+ const LENGTH: usize = 1;
+
+ fn fields(&self) -> Vec<Cow<'_, str>> {
+ vec![Cow::Owned(self.to_string())]
+ }
+ fn headers() -> Vec<Cow<'static, str>> {
+ vec![Cow::Borrowed(stringify!($t))]
+ }
+ }
+ };
+
+ ( $t:ty = borrowed ) => {
+ impl Tabled for $t {
+ const LENGTH: usize = 1;
+
+ fn fields(&self) -> Vec<Cow<'_, str>> {
+ vec![Cow::Borrowed(self)]
+ }
+ fn headers() -> Vec<Cow<'static, str>> {
+ vec![Cow::Borrowed(stringify!($t))]
+ }
+ }
+ };
+}
+
+default_table!(&str = borrowed);
+default_table!(str = borrowed);
+default_table!(String);
+
+default_table!(char);
+
+default_table!(bool);
+
+default_table!(isize);
+default_table!(usize);
+
+default_table!(u8);
+default_table!(u16);
+default_table!(u32);
+default_table!(u64);
+default_table!(u128);
+
+default_table!(i8);
+default_table!(i16);
+default_table!(i32);
+default_table!(i64);
+default_table!(i128);
+
+default_table!(f32);
+default_table!(f64);
+
+impl<T, const N: usize> Tabled for [T; N]
+where
+ T: std::fmt::Display,
+{
+ const LENGTH: usize = N;
+
+ fn fields(&self) -> Vec<Cow<'_, str>> {
+ self.iter()
+ .map(ToString::to_string)
+ .map(Cow::Owned)
+ .collect()
+ }
+
+ fn headers() -> Vec<Cow<'static, str>> {
+ (0..N).map(|i| Cow::Owned(format!("{i}"))).collect()
+ }
+}
diff --git a/vendor/tabled/src/tables/compact.rs b/vendor/tabled/src/tables/compact.rs
new file mode 100644
index 000000000..14c1e2b5b
--- /dev/null
+++ b/vendor/tabled/src/tables/compact.rs
@@ -0,0 +1,309 @@
+//! This module contains a [`CompactTable`] table.
+//!
+//! In contrast to [`Table`] [`CompactTable`] does no allocations but it consumes an iterator.
+//! It's useful when you don't want to re/allocate a buffer for your data.
+//!
+//! # Example
+//!
+//! It works smoothly with arrays.
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//!use tabled::{settings::Style, tables::CompactTable};
+//!
+//! let data = [
+//! ["FreeBSD", "1993", "William and Lynne Jolitz", "?"],
+//! ["OpenBSD", "1995", "Theo de Raadt", ""],
+//! ["HardenedBSD", "2014", "Oliver Pinter and Shawn Webb", ""],
+//! ];
+//!
+//! let table = CompactTable::from(data)
+//! .with(Style::psql())
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! " FreeBSD | 1993 | William and Lynne Jolitz | ? \n",
+//! "-------------+------+------------------------------+---\n",
+//! " OpenBSD | 1995 | Theo de Raadt | \n",
+//! " HardenedBSD | 2014 | Oliver Pinter and Shawn Webb | ",
+//! )
+//! );
+//! ```
+//!
+//! But it's default creation requires to be given an estimated cell width, and the amount of columns.
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//!use tabled::{settings::Style, tables::CompactTable};
+//!
+//! let data = [
+//! ["FreeBSD", "1993", "William and Lynne Jolitz", "?"],
+//! ["OpenBSD", "1995", "Theo de Raadt", ""],
+//! ["HardenedBSD", "2014", "Oliver Pinter and Shawn Webb", ""],
+//! ];
+//!
+//! // See what will happen if the given width is too narrow
+//!
+//! let table = CompactTable::new(&data)
+//! .columns(4)
+//! .width(5)
+//! .with(Style::ascii())
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! "+-----+-----+-----+-----+\n\
+//! | FreeBSD | 1993 | William and Lynne Jolitz | ? |\n\
+//! |-----+-----+-----+-----|\n\
+//! | OpenBSD | 1995 | Theo de Raadt | |\n\
+//! |-----+-----+-----+-----|\n\
+//! | HardenedBSD | 2014 | Oliver Pinter and Shawn Webb | |\n\
+//! +-----+-----+-----+-----+"
+//! );
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use core::cmp::max;
+use core::fmt;
+
+use crate::{
+ grid::{
+ config::{AlignmentHorizontal, CompactConfig, Indent, Sides},
+ dimension::{ConstDimension, ConstSize, Dimension},
+ records::{
+ into_records::{LimitColumns, LimitRows},
+ IntoRecords, IterRecords,
+ },
+ util::string::string_width,
+ CompactGrid,
+ },
+ settings::{Style, TableOption},
+};
+
+/// A table which consumes an [`IntoRecords`] iterator.
+/// It assumes that the content has only single line.
+#[derive(Debug, Clone)]
+pub struct CompactTable<I, D> {
+ records: I,
+ cfg: CompactConfig,
+ dims: D,
+ count_columns: usize,
+ count_rows: Option<usize>,
+}
+
+impl<I> CompactTable<I, ConstDimension<0, 0>> {
+ /// Creates a new [`CompactTable`] structure with a width dimension for all columns.
+ pub const fn new(iter: I) -> Self
+ where
+ I: IntoRecords,
+ {
+ Self {
+ records: iter,
+ cfg: create_config(),
+ count_columns: 0,
+ count_rows: None,
+ dims: ConstDimension::new(ConstSize::Value(2), ConstSize::Value(1)),
+ }
+ }
+}
+
+impl<I, const ROWS: usize, const COLS: usize> CompactTable<I, ConstDimension<COLS, ROWS>> {
+ /// Set a height for each row.
+ pub fn height<S: Into<ConstSize<COUNT_ROWS>>, const COUNT_ROWS: usize>(
+ self,
+ size: S,
+ ) -> CompactTable<I, ConstDimension<COLS, COUNT_ROWS>> {
+ let (width, _) = self.dims.into();
+ CompactTable {
+ dims: ConstDimension::new(width, size.into()),
+ records: self.records,
+ cfg: self.cfg,
+ count_columns: self.count_columns,
+ count_rows: self.count_rows,
+ }
+ }
+
+ /// Set a width for each column.
+ pub fn width<S: Into<ConstSize<COUNT_COLUMNS>>, const COUNT_COLUMNS: usize>(
+ self,
+ size: S,
+ ) -> CompactTable<I, ConstDimension<COUNT_COLUMNS, ROWS>> {
+ let (_, height) = self.dims.into();
+ CompactTable {
+ dims: ConstDimension::new(size.into(), height),
+ records: self.records,
+ cfg: self.cfg,
+ count_columns: self.count_columns,
+ count_rows: self.count_rows,
+ }
+ }
+}
+
+impl<I, D> CompactTable<I, D> {
+ /// Creates a new [`CompactTable`] structure with a known dimension.
+ ///
+ /// Notice that the function wont call [`Estimate`].
+ ///
+ /// [`Estimate`]: crate::grid::dimension::Estimate
+ pub fn with_dimension(iter: I, dimension: D) -> Self
+ where
+ I: IntoRecords,
+ {
+ Self {
+ records: iter,
+ dims: dimension,
+ cfg: create_config(),
+ count_columns: 0,
+ count_rows: None,
+ }
+ }
+
+ /// With is a generic function which applies options to the [`CompactTable`].
+ pub fn with<O>(mut self, option: O) -> Self
+ where
+ for<'a> O: TableOption<IterRecords<&'a I>, D, CompactConfig>,
+ {
+ let mut records = IterRecords::new(&self.records, self.count_columns, self.count_rows);
+ option.change(&mut records, &mut self.cfg, &mut self.dims);
+
+ self
+ }
+
+ /// Limit a number of rows.
+ pub const fn rows(mut self, count_rows: usize) -> Self {
+ self.count_rows = Some(count_rows);
+ self
+ }
+
+ /// Limit a number of columns.
+ pub const fn columns(mut self, count: usize) -> Self {
+ self.count_columns = count;
+ self
+ }
+
+ /// Returns a table config.
+ pub fn get_config(&self) -> &CompactConfig {
+ &self.cfg
+ }
+
+ /// Returns a table config.
+ pub fn get_config_mut(&mut self) -> &mut CompactConfig {
+ &mut self.cfg
+ }
+
+ /// Format table into [fmt::Write]er.
+ pub fn fmt<W>(self, writer: W) -> fmt::Result
+ where
+ I: IntoRecords,
+ D: Dimension,
+ W: fmt::Write,
+ {
+ build_grid(
+ writer,
+ self.records,
+ self.dims,
+ self.cfg,
+ self.count_columns,
+ self.count_rows,
+ )
+ }
+
+ /// Format table into a writer.
+ #[cfg(feature = "std")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+ pub fn build<W>(self, writer: W) -> std::io::Result<()>
+ where
+ I: IntoRecords,
+ D: Dimension,
+ W: std::io::Write,
+ {
+ let writer = super::util::utf8_writer::UTF8Writer::new(writer);
+ self.fmt(writer)
+ .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))
+ }
+
+ /// Build a string.
+ ///
+ /// We can't implement [`std::string::ToString`] cause it does takes `&self` reference.
+ #[allow(clippy::inherent_to_string)]
+ #[cfg(feature = "std")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+ pub fn to_string(self) -> String
+ where
+ I: IntoRecords,
+ D: Dimension,
+ {
+ let mut buf = String::new();
+ self.fmt(&mut buf).unwrap();
+ buf
+ }
+}
+
+impl<T, const ROWS: usize, const COLS: usize> From<[[T; COLS]; ROWS]>
+ for CompactTable<[[T; COLS]; ROWS], ConstDimension<COLS, ROWS>>
+where
+ T: AsRef<str>,
+{
+ fn from(mat: [[T; COLS]; ROWS]) -> Self {
+ let mut width = [0; COLS];
+ for row in mat.iter() {
+ for (col, text) in row.iter().enumerate() {
+ let text = text.as_ref();
+ let text_width = string_width(text);
+ width[col] = max(width[col], text_width);
+ }
+ }
+
+ // add padding
+ for w in &mut width {
+ *w += 2;
+ }
+
+ let dims = ConstDimension::new(ConstSize::List(width), ConstSize::Value(1));
+ Self::with_dimension(mat, dims).columns(COLS).rows(ROWS)
+ }
+}
+
+fn build_grid<W: fmt::Write, I: IntoRecords, D: Dimension>(
+ writer: W,
+ records: I,
+ dims: D,
+ config: CompactConfig,
+ cols: usize,
+ rows: Option<usize>,
+) -> Result<(), fmt::Error> {
+ match rows {
+ Some(limit) => {
+ let records = LimitRows::new(records, limit);
+ let records = LimitColumns::new(records, cols);
+ let records = IterRecords::new(records, cols, rows);
+ CompactGrid::new(records, dims, config).build(writer)
+ }
+ None => {
+ let records = LimitColumns::new(records, cols);
+ let records = IterRecords::new(records, cols, rows);
+ CompactGrid::new(records, dims, config).build(writer)
+ }
+ }
+}
+
+const fn create_config() -> CompactConfig {
+ CompactConfig::empty()
+ .set_padding(Sides::new(
+ Indent::spaced(1),
+ Indent::spaced(1),
+ Indent::zero(),
+ Indent::zero(),
+ ))
+ .set_alignment_horizontal(AlignmentHorizontal::Left)
+ .set_borders(*Style::ascii().get_borders())
+}
+
+impl<R, D> TableOption<R, D, CompactConfig> for CompactConfig {
+ fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
+ *cfg = self;
+ }
+}
diff --git a/vendor/tabled/src/tables/extended.rs b/vendor/tabled/src/tables/extended.rs
new file mode 100644
index 000000000..478505c4b
--- /dev/null
+++ b/vendor/tabled/src/tables/extended.rs
@@ -0,0 +1,338 @@
+//! This module contains an [`ExtendedTable`] structure which is useful in cases where
+//! a structure has a lot of fields.
+//!
+#![cfg_attr(feature = "derive", doc = "```")]
+#![cfg_attr(not(feature = "derive"), doc = "```ignore")]
+//! use tabled::{Tabled, tables::ExtendedTable};
+//!
+//! #[derive(Tabled)]
+//! struct Language {
+//! name: &'static str,
+//! designed_by: &'static str,
+//! invented_year: usize,
+//! }
+//!
+//! let languages = vec![
+//! Language{
+//! name: "C",
+//! designed_by: "Dennis Ritchie",
+//! invented_year: 1972
+//! },
+//! Language{
+//! name: "Rust",
+//! designed_by: "Graydon Hoare",
+//! invented_year: 2010
+//! },
+//! Language{
+//! name: "Go",
+//! designed_by: "Rob Pike",
+//! invented_year: 2009
+//! },
+//! ];
+//!
+//! let table = ExtendedTable::new(languages).to_string();
+//!
+//! let expected = "-[ RECORD 0 ]-+---------------\n\
+//! name | C\n\
+//! designed_by | Dennis Ritchie\n\
+//! invented_year | 1972\n\
+//! -[ RECORD 1 ]-+---------------\n\
+//! name | Rust\n\
+//! designed_by | Graydon Hoare\n\
+//! invented_year | 2010\n\
+//! -[ RECORD 2 ]-+---------------\n\
+//! name | Go\n\
+//! designed_by | Rob Pike\n\
+//! invented_year | 2009";
+//!
+//! assert_eq!(table, expected);
+//! ```
+
+use std::borrow::Cow;
+use std::fmt::{self, Display};
+
+use crate::grid::util::string::string_width;
+use crate::Tabled;
+
+/// `ExtendedTable` display data in a 'expanded display mode' from postgresql.
+/// It may be useful for a large data sets with a lot of fields.
+///
+/// See 'Examples' in <https://www.postgresql.org/docs/current/app-psql.html>.
+///
+/// It escapes strings to resolve a multi-line ones.
+/// Because of that ANSI sequences will be not be rendered too so colores will not be showed.
+///
+/// ```
+/// use tabled::tables::ExtendedTable;
+///
+/// let data = vec!["Hello", "2021"];
+/// let table = ExtendedTable::new(&data).to_string();
+///
+/// assert_eq!(
+/// table,
+/// concat!(
+/// "-[ RECORD 0 ]-\n",
+/// "&str | Hello\n",
+/// "-[ RECORD 1 ]-\n",
+/// "&str | 2021",
+/// )
+/// );
+/// ```
+#[derive(Debug, Clone)]
+pub struct ExtendedTable {
+ fields: Vec<String>,
+ records: Vec<Vec<String>>,
+}
+
+impl ExtendedTable {
+ /// Creates a new instance of `ExtendedTable`
+ pub fn new<T>(iter: impl IntoIterator<Item = T>) -> Self
+ where
+ T: Tabled,
+ {
+ let data = iter
+ .into_iter()
+ .map(|i| {
+ i.fields()
+ .into_iter()
+ .map(|s| s.escape_debug().to_string())
+ .collect()
+ })
+ .collect();
+ let header = T::headers()
+ .into_iter()
+ .map(|s| s.escape_debug().to_string())
+ .collect();
+
+ Self {
+ records: data,
+ fields: header,
+ }
+ }
+
+ /// Truncates table to a set width value for a table.
+ /// It returns a success inticator, where `false` means it's not possible to set the table width,
+ /// because of the given arguments.
+ ///
+ /// It tries to not affect fields, but if there's no enough space all records will be deleted and fields will be cut.
+ ///
+ /// The minimum width is 14.
+ pub fn truncate(&mut self, max: usize, suffix: &str) -> bool {
+ // -[ RECORD 0 ]-
+ let teplate_width = self.records.len().to_string().len() + 13;
+ let min_width = teplate_width;
+ if max < min_width {
+ return false;
+ }
+
+ let suffix_width = string_width(suffix);
+ if max < suffix_width {
+ return false;
+ }
+
+ let max = max - suffix_width;
+
+ let fields_max_width = self
+ .fields
+ .iter()
+ .map(|s| string_width(s))
+ .max()
+ .unwrap_or_default();
+
+ // 3 is a space for ' | '
+ let fields_affected = max < fields_max_width + 3;
+ if fields_affected {
+ if max < 3 {
+ return false;
+ }
+
+ let max = max - 3;
+
+ if max < suffix_width {
+ return false;
+ }
+
+ let max = max - suffix_width;
+
+ truncate_fields(&mut self.fields, max, suffix);
+ truncate_records(&mut self.records, 0, suffix);
+ } else {
+ let max = max - fields_max_width - 3 - suffix_width;
+ truncate_records(&mut self.records, max, suffix);
+ }
+
+ true
+ }
+}
+
+impl From<Vec<Vec<String>>> for ExtendedTable {
+ fn from(mut data: Vec<Vec<String>>) -> Self {
+ if data.is_empty() {
+ return Self {
+ fields: vec![],
+ records: vec![],
+ };
+ }
+
+ let fields = data.remove(0);
+
+ Self {
+ fields,
+ records: data,
+ }
+ }
+}
+
+impl Display for ExtendedTable {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.records.is_empty() {
+ return Ok(());
+ }
+
+ // It's possible that field|header can be a multiline string so
+ // we escape it and trim \" chars.
+ let fields = self.fields.iter().collect::<Vec<_>>();
+
+ let max_field_width = fields
+ .iter()
+ .map(|s| string_width(s))
+ .max()
+ .unwrap_or_default();
+
+ let max_values_length = self
+ .records
+ .iter()
+ .map(|record| record.iter().map(|s| string_width(s)).max())
+ .max()
+ .unwrap_or_default()
+ .unwrap_or_default();
+
+ for (i, records) in self.records.iter().enumerate() {
+ write_header_template(f, i, max_field_width, max_values_length)?;
+
+ for (value, field) in records.iter().zip(fields.iter()) {
+ writeln!(f)?;
+ write_record(f, field, value, max_field_width)?;
+ }
+
+ let is_last_record = i + 1 == self.records.len();
+ if !is_last_record {
+ writeln!(f)?;
+ }
+ }
+
+ Ok(())
+ }
+}
+
+fn truncate_records(records: &mut Vec<Vec<String>>, max_width: usize, suffix: &str) {
+ for fields in records {
+ truncate_fields(fields, max_width, suffix);
+ }
+}
+
+fn truncate_fields(records: &mut Vec<String>, max_width: usize, suffix: &str) {
+ for text in records {
+ truncate(text, max_width, suffix);
+ }
+}
+
+fn write_header_template(
+ f: &mut fmt::Formatter<'_>,
+ index: usize,
+ max_field_width: usize,
+ max_values_length: usize,
+) -> fmt::Result {
+ let mut template = format!("-[ RECORD {index} ]-");
+ let default_template_length = template.len();
+
+ // 3 - is responsible for ' | ' formatting
+ let max_line_width = std::cmp::max(
+ max_field_width + 3 + max_values_length,
+ default_template_length,
+ );
+ let rest_to_print = max_line_width - default_template_length;
+ if rest_to_print > 0 {
+ // + 1 is a space after field name and we get a next pos so its +2
+ if max_field_width + 2 > default_template_length {
+ let part1 = (max_field_width + 1) - default_template_length;
+ let part2 = rest_to_print - part1 - 1;
+
+ template.extend(
+ std::iter::repeat('-')
+ .take(part1)
+ .chain(std::iter::once('+'))
+ .chain(std::iter::repeat('-').take(part2)),
+ );
+ } else {
+ template.extend(std::iter::repeat('-').take(rest_to_print));
+ }
+ }
+
+ write!(f, "{template}")?;
+
+ Ok(())
+}
+
+fn write_record(
+ f: &mut fmt::Formatter<'_>,
+ field: &str,
+ value: &str,
+ max_field_width: usize,
+) -> fmt::Result {
+ write!(f, "{field:max_field_width$} | {value}")
+}
+
+fn truncate(text: &mut String, max: usize, suffix: &str) {
+ let original_len = text.len();
+
+ if max == 0 || text.is_empty() {
+ *text = String::new();
+ } else {
+ *text = cut_str_basic(text, max).into_owned();
+ }
+
+ let cut_was_done = text.len() < original_len;
+ if !suffix.is_empty() && cut_was_done {
+ text.push_str(suffix);
+ }
+}
+
+fn cut_str_basic(s: &str, width: usize) -> Cow<'_, str> {
+ const REPLACEMENT: char = '\u{FFFD}';
+
+ let (length, count_unknowns, _) = split_at_pos(s, width);
+ let buf = &s[..length];
+ if count_unknowns == 0 {
+ return Cow::Borrowed(buf);
+ }
+
+ let mut buf = buf.to_owned();
+ buf.extend(std::iter::repeat(REPLACEMENT).take(count_unknowns));
+
+ Cow::Owned(buf)
+}
+
+fn split_at_pos(s: &str, pos: usize) -> (usize, usize, usize) {
+ let mut length = 0;
+ let mut i = 0;
+ for c in s.chars() {
+ if i == pos {
+ break;
+ };
+
+ let c_width = unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
+
+ // We cut the chars which takes more then 1 symbol to display,
+ // in order to archive the necessary width.
+ if i + c_width > pos {
+ let count = pos - i;
+ return (length, count, c.len_utf8());
+ }
+
+ i += c_width;
+ length += c.len_utf8();
+ }
+
+ (length, 0, 0)
+}
diff --git a/vendor/tabled/src/tables/iter.rs b/vendor/tabled/src/tables/iter.rs
new file mode 100644
index 000000000..af485ac08
--- /dev/null
+++ b/vendor/tabled/src/tables/iter.rs
@@ -0,0 +1,344 @@
+//! This module contains a [`IterTable`] table.
+//!
+//! In contrast to [`Table`] [`IterTable`] does no allocations but it consumes an iterator.
+//! It's useful when you don't want to re/allocate a buffer for your data.
+//!
+//! # Example
+//!
+//! ```
+//! use tabled::{grid::records::IterRecords, tables::IterTable};
+//!
+//! let iterator = vec![vec!["First", "row"], vec!["Second", "row"]];
+//! let records = IterRecords::new(iterator, 2, Some(2));
+//! let table = IterTable::new(records);
+//!
+//! let s = table.to_string();
+//!
+//! assert_eq!(
+//! s,
+//! "+--------+-----+\n\
+//! | First | row |\n\
+//! +--------+-----+\n\
+//! | Second | row |\n\
+//! +--------+-----+",
+//! );
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use std::{fmt, io};
+
+use crate::{
+ grid::{
+ colors::NoColors,
+ config::{AlignmentHorizontal, CompactConfig, Indent, Sides, SpannedConfig},
+ dimension::{CompactGridDimension, DimensionValue, StaticDimension},
+ records::{
+ into_records::{
+ truncate_records::ExactValue, BufColumns, BufRows, LimitColumns, LimitRows,
+ TruncateContent,
+ },
+ IntoRecords, IterRecords,
+ },
+ Grid,
+ },
+ settings::{Style, TableOption},
+};
+
+use super::util::utf8_writer::UTF8Writer;
+
+/// A table which consumes an [`IntoRecords`] iterator.
+///
+/// To be able to build table we need a dimensions.
+/// If no width and count_columns is set, [`IterTable`] will sniff the records, by
+/// keeping a number of rows buffered (You can set the number via [`IterTable::sniff`]).
+#[derive(Debug, Clone)]
+pub struct IterTable<I> {
+ records: I,
+ cfg: CompactConfig,
+ table: Settings,
+}
+
+#[derive(Debug, Clone)]
+struct Settings {
+ sniff: usize,
+ count_columns: Option<usize>,
+ count_rows: Option<usize>,
+ width: Option<usize>,
+ height: Option<usize>,
+}
+
+impl<I> IterTable<I> {
+ /// Creates a new [`IterTable`] structure.
+ pub fn new(iter: I) -> Self
+ where
+ I: IntoRecords,
+ {
+ Self {
+ records: iter,
+ cfg: create_config(),
+ table: Settings {
+ sniff: 1000,
+ count_columns: None,
+ count_rows: None,
+ height: None,
+ width: None,
+ },
+ }
+ }
+
+ /// With is a generic function which applies options to the [`IterTable`].
+ pub fn with<O>(mut self, option: O) -> Self
+ where
+ for<'a> O: TableOption<IterRecords<&'a I>, StaticDimension, CompactConfig>,
+ {
+ let count_columns = self.table.count_columns.unwrap_or(0);
+ let mut records = IterRecords::new(&self.records, count_columns, self.table.count_rows);
+ let mut dims = StaticDimension::new(DimensionValue::Exact(0), DimensionValue::Exact(1));
+ option.change(&mut records, &mut self.cfg, &mut dims);
+
+ self
+ }
+
+ /// Limit a number of columns.
+ pub fn columns(mut self, count_columns: usize) -> Self {
+ self.table.count_columns = Some(count_columns);
+ self
+ }
+
+ /// Limit a number of rows.
+ pub fn rows(mut self, count_rows: usize) -> Self {
+ self.table.count_rows = Some(count_rows);
+ self
+ }
+
+ /// Limit an amount of rows will be read for dimension estimations.
+ pub fn sniff(mut self, count: usize) -> Self {
+ self.table.sniff = count;
+ self
+ }
+
+ /// Set a height for each row.
+ pub fn height(mut self, size: usize) -> Self {
+ self.table.height = Some(size);
+ self
+ }
+
+ /// Set a width for each column.
+ pub fn width(mut self, size: usize) -> Self {
+ self.table.width = Some(size);
+ self
+ }
+
+ /// Build a string.
+ ///
+ /// We can't implement [`std::string::ToString`] cause it does takes `&self` reference.
+ #[allow(clippy::inherent_to_string)]
+ pub fn to_string(self) -> String
+ where
+ I: IntoRecords,
+ {
+ let mut buf = String::new();
+ self.fmt(&mut buf).expect("safe");
+
+ buf
+ }
+
+ /// Format table into [`io::Write`]r.
+ pub fn build<W>(self, writer: W) -> io::Result<()>
+ where
+ I: IntoRecords,
+ W: io::Write,
+ {
+ let writer = UTF8Writer::new(writer);
+ self.fmt(writer)
+ .map_err(|err| io::Error::new(io::ErrorKind::Other, err))
+ }
+
+ /// Format table into [fmt::Write]er.
+ pub fn fmt<W>(self, writer: W) -> fmt::Result
+ where
+ I: IntoRecords,
+ W: fmt::Write,
+ {
+ build_grid(writer, self.records, self.cfg, self.table)
+ }
+}
+
+fn build_grid<W: fmt::Write, I: IntoRecords>(
+ f: W,
+ iter: I,
+ cfg: CompactConfig,
+ opts: Settings,
+) -> fmt::Result {
+ let dont_sniff = opts.width.is_some() && opts.count_columns.is_some();
+ if dont_sniff {
+ build_table_with_static_dims(f, iter, cfg, opts)
+ } else if opts.width.is_none() {
+ build_table_sniffing_with_unknown_width(f, iter, cfg, opts)
+ } else {
+ build_table_sniffing_with_known_width(f, iter, cfg, opts)
+ }
+}
+
+fn build_table_with_static_dims<W, I>(
+ f: W,
+ iter: I,
+ cfg: CompactConfig,
+ opts: Settings,
+) -> fmt::Result
+where
+ W: fmt::Write,
+ I: IntoRecords,
+{
+ let count_columns = opts.count_columns.unwrap();
+ let width = opts.width.unwrap();
+ let height = opts.height.unwrap_or(1);
+ let contentw = ExactValue::Exact(width);
+ let pad = cfg.get_padding();
+ let w = DimensionValue::Exact(width + pad.left.size + pad.right.size);
+ let h = DimensionValue::Exact(height + pad.top.size + pad.bottom.size);
+ let dims = StaticDimension::new(w, h);
+ let cfg = SpannedConfig::from(cfg);
+
+ match opts.count_rows {
+ Some(limit) => {
+ let records = LimitRows::new(iter, limit);
+ let records = build_records(records, contentw, count_columns, Some(limit));
+ Grid::new(records, dims, cfg, NoColors).build(f)
+ }
+ None => {
+ let records = build_records(iter, contentw, count_columns, None);
+ Grid::new(records, dims, cfg, NoColors).build(f)
+ }
+ }
+}
+
+fn build_table_sniffing_with_unknown_width<W, I>(
+ f: W,
+ iter: I,
+ cfg: CompactConfig,
+ opts: Settings,
+) -> fmt::Result
+where
+ W: fmt::Write,
+ I: IntoRecords,
+{
+ let records = BufRows::new(iter, opts.sniff);
+ let records = BufColumns::from(records);
+
+ let count_columns = get_count_columns(&opts, records.as_slice());
+
+ let (mut width, height) = {
+ let records = LimitColumns::new(records.as_slice(), count_columns);
+ let records = IterRecords::new(records, count_columns, None);
+ CompactGridDimension::dimension(records, &cfg)
+ };
+
+ let padding = cfg.get_padding();
+ let pad = padding.left.size + padding.right.size;
+ let padv = padding.top.size + padding.bottom.size;
+
+ if opts.sniff == 0 {
+ width = std::iter::repeat(pad)
+ .take(count_columns)
+ .collect::<Vec<_>>();
+ }
+
+ let content_width = ExactValue::List(width.iter().map(|i| i.saturating_sub(pad)).collect());
+ let dims_width = DimensionValue::List(width);
+
+ let height_exact = opts.height.unwrap_or(1) + padv;
+ let mut dims_height = DimensionValue::Partial(height, height_exact);
+
+ if opts.height.is_some() {
+ dims_height = DimensionValue::Exact(height_exact);
+ }
+
+ let dims = StaticDimension::new(dims_width, dims_height);
+ let cfg = SpannedConfig::from(cfg);
+
+ match opts.count_rows {
+ Some(limit) => {
+ let records = LimitRows::new(records, limit);
+ let records = build_records(records, content_width, count_columns, Some(limit));
+ Grid::new(records, dims, cfg, NoColors).build(f)
+ }
+ None => {
+ let records = build_records(records, content_width, count_columns, None);
+ Grid::new(records, dims, cfg, NoColors).build(f)
+ }
+ }
+}
+
+fn build_table_sniffing_with_known_width<W, I>(
+ f: W,
+ iter: I,
+ cfg: CompactConfig,
+ opts: Settings,
+) -> fmt::Result
+where
+ W: fmt::Write,
+ I: IntoRecords,
+{
+ let records = BufRows::new(iter, opts.sniff);
+ let records = BufColumns::from(records);
+
+ let count_columns = get_count_columns(&opts, records.as_slice());
+
+ let width = opts.width.unwrap();
+ let contentw = ExactValue::Exact(width);
+
+ let padding = cfg.get_padding();
+ let pad = padding.left.size + padding.right.size;
+ let padv = padding.top.size + padding.bottom.size;
+
+ let height = opts.height.unwrap_or(1) + padv;
+ let dimsh = DimensionValue::Exact(height);
+ let dimsw = DimensionValue::Exact(width + pad);
+ let dims = StaticDimension::new(dimsw, dimsh);
+
+ let cfg = SpannedConfig::from(cfg);
+
+ match opts.count_rows {
+ Some(limit) => {
+ let records = LimitRows::new(records, limit);
+ let records = build_records(records, contentw, count_columns, Some(limit));
+ Grid::new(records, dims, cfg, NoColors).build(f)
+ }
+ None => {
+ let records = build_records(records, contentw, count_columns, None);
+ Grid::new(records, dims, cfg, NoColors).build(f)
+ }
+ }
+}
+
+fn get_count_columns(opts: &Settings, buf: &[Vec<String>]) -> usize {
+ match opts.count_columns {
+ Some(size) => size,
+ None => buf.iter().map(|row| row.len()).max().unwrap_or(0),
+ }
+}
+
+fn create_config() -> CompactConfig {
+ CompactConfig::default()
+ .set_padding(Sides::new(
+ Indent::spaced(1),
+ Indent::spaced(1),
+ Indent::default(),
+ Indent::default(),
+ ))
+ .set_alignment_horizontal(AlignmentHorizontal::Left)
+ .set_borders(*Style::ascii().get_borders())
+}
+
+fn build_records<I: IntoRecords>(
+ records: I,
+ width: ExactValue<'_>,
+ count_columns: usize,
+ count_rows: Option<usize>,
+) -> IterRecords<LimitColumns<TruncateContent<'_, I>>> {
+ let records = TruncateContent::new(records, width);
+ let records = LimitColumns::new(records, count_columns);
+ IterRecords::new(records, count_columns, count_rows)
+}
diff --git a/vendor/tabled/src/tables/mod.rs b/vendor/tabled/src/tables/mod.rs
new file mode 100644
index 000000000..e0c4bf794
--- /dev/null
+++ b/vendor/tabled/src/tables/mod.rs
@@ -0,0 +1,48 @@
+//! Module contains a list of table representatives.
+//!
+//! ## [`Table`]
+//!
+//! A default table implementation.
+//!
+//! ## [`IterTable`]
+//!
+//! Just like [`Table`] but it's API is a bit different to serve better in context
+//! where there is a memory limit.
+//!
+//! ## [`ExtendedTable`]
+//!
+//! It's a table which is useful for large amount of data.
+//!
+//! ## [`PoolTable`]
+//!
+//! A table with a greather controll of a layout.
+
+mod compact;
+mod util;
+
+#[cfg(feature = "std")]
+mod extended;
+#[cfg(feature = "std")]
+mod iter;
+#[cfg(feature = "std")]
+mod table;
+#[cfg(feature = "std")]
+mod table_pool;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use table::Table;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use iter::IterTable;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use extended::ExtendedTable;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use table_pool::{PoolTable, TableValue};
+
+pub use compact::CompactTable;
diff --git a/vendor/tabled/src/tables/table.rs b/vendor/tabled/src/tables/table.rs
new file mode 100644
index 000000000..d6ff37661
--- /dev/null
+++ b/vendor/tabled/src/tables/table.rs
@@ -0,0 +1,464 @@
+//! This module contains a main table representation [`Table`].
+
+use core::ops::DerefMut;
+use std::{borrow::Cow, fmt, iter::FromIterator};
+
+use crate::{
+ builder::Builder,
+ grid::{
+ colors::NoColors,
+ config::{
+ AlignmentHorizontal, ColorMap, ColoredConfig, CompactConfig, Entity, Formatting,
+ Indent, Sides, SpannedConfig,
+ },
+ dimension::{CompleteDimensionVecRecords, Dimension, Estimate, PeekableDimension},
+ records::{
+ vec_records::{CellInfo, VecRecords},
+ ExactRecords, Records,
+ },
+ PeekableGrid,
+ },
+ settings::{Style, TableOption},
+ Tabled,
+};
+
+/// The structure provides an interface for building a table for types that implements [`Tabled`].
+///
+/// To build a string representation of a table you must use a [`std::fmt::Display`].
+/// Or simply call `.to_string()` method.
+///
+/// The default table [`Style`] is [`Style::ascii`],
+/// with a 1 left and right [`Padding`].
+///
+/// ## Example
+///
+/// ### Basic usage
+///
+/// ```rust,no_run
+/// use tabled::Table;
+///
+/// let table = Table::new(&["Year", "2021"]);
+/// ```
+///
+/// ### With settings
+///
+/// ```rust,no_run
+/// use tabled::{Table, settings::{Style, Alignment}};
+///
+/// let data = vec!["Hello", "2021"];
+/// let mut table = Table::new(&data);
+/// table.with(Style::psql()).with(Alignment::left());
+///
+/// println!("{}", table);
+/// ```
+///
+/// [`Padding`]: crate::settings::Padding
+/// [`Style`]: crate::settings::Style
+/// [`Style::ascii`]: crate::settings::Style::ascii
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Table {
+ records: VecRecords<CellInfo<String>>,
+ config: ColoredConfig,
+ dimension: CompleteDimensionVecRecords<'static>,
+}
+
+impl Table {
+ /// New creates a Table instance.
+ ///
+ /// If you use a reference iterator you'd better use [`FromIterator`] instead.
+ /// As it has a different lifetime constraints and make less copies therefore.
+ pub fn new<I, T>(iter: I) -> Self
+ where
+ I: IntoIterator<Item = T>,
+ T: Tabled,
+ {
+ let mut header = Vec::with_capacity(T::LENGTH);
+ for text in T::headers() {
+ let text = text.into_owned();
+ let cell = CellInfo::new(text);
+ header.push(cell);
+ }
+
+ let mut records = vec![header];
+ for row in iter.into_iter() {
+ let mut list = Vec::with_capacity(T::LENGTH);
+ for text in row.fields().into_iter() {
+ let text = text.into_owned();
+ let cell = CellInfo::new(text);
+
+ list.push(cell);
+ }
+
+ records.push(list);
+ }
+
+ let records = VecRecords::new(records);
+
+ Self {
+ records,
+ config: ColoredConfig::new(configure_grid()),
+ dimension: CompleteDimensionVecRecords::default(),
+ }
+ }
+
+ /// Creates a builder from a data set given.
+ ///
+ /// # Example
+ ///
+ ///
+ #[cfg_attr(feature = "derive", doc = "```")]
+ #[cfg_attr(not(feature = "derive"), doc = "```ignore")]
+ /// use tabled::{
+ /// Table, Tabled,
+ /// settings::{object::Segment, Modify, Alignment}
+ /// };
+ ///
+ /// #[derive(Tabled)]
+ /// struct User {
+ /// name: &'static str,
+ /// #[tabled(inline("device::"))]
+ /// device: Device,
+ /// }
+ ///
+ /// #[derive(Tabled)]
+ /// enum Device {
+ /// PC,
+ /// Mobile
+ /// }
+ ///
+ /// let data = vec![
+ /// User { name: "Vlad", device: Device::Mobile },
+ /// User { name: "Dimitry", device: Device::PC },
+ /// User { name: "John", device: Device::PC },
+ /// ];
+ ///
+ /// let mut table = Table::builder(data)
+ /// .index()
+ /// .column(0)
+ /// .transpose()
+ /// .build()
+ /// .with(Modify::new(Segment::new(1.., 1..)).with(Alignment::center()))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+----------------+------+---------+------+\n\
+ /// | name | Vlad | Dimitry | John |\n\
+ /// +----------------+------+---------+------+\n\
+ /// | device::PC | | + | + |\n\
+ /// +----------------+------+---------+------+\n\
+ /// | device::Mobile | + | | |\n\
+ /// +----------------+------+---------+------+"
+ /// )
+ /// ```
+ pub fn builder<I, T>(iter: I) -> Builder
+ where
+ T: Tabled,
+ I: IntoIterator<Item = T>,
+ {
+ let mut records = Vec::new();
+ for row in iter {
+ let mut list = Vec::with_capacity(T::LENGTH);
+ for text in row.fields().into_iter() {
+ list.push(text.into_owned());
+ }
+
+ records.push(list);
+ }
+
+ let mut b = Builder::from(records);
+ let _ = b.set_header(T::headers()).hint_column_size(T::LENGTH);
+
+ b
+ }
+
+ /// With is a generic function which applies options to the [`Table`].
+ ///
+ /// It applies settings immediately.
+ pub fn with<O>(&mut self, option: O) -> &mut Self
+ where
+ O: TableOption<
+ VecRecords<CellInfo<String>>,
+ CompleteDimensionVecRecords<'static>,
+ ColoredConfig,
+ >,
+ {
+ self.dimension.clear_width();
+ self.dimension.clear_height();
+
+ option.change(&mut self.records, &mut self.config, &mut self.dimension);
+
+ self
+ }
+
+ /// Returns a table shape (count rows, count columns).
+ pub fn shape(&self) -> (usize, usize) {
+ (self.count_rows(), self.count_columns())
+ }
+
+ /// Returns an amount of rows in the table.
+ pub fn count_rows(&self) -> usize {
+ self.records.count_rows()
+ }
+
+ /// Returns an amount of columns in the table.
+ pub fn count_columns(&self) -> usize {
+ self.records.count_columns()
+ }
+
+ /// Returns a table shape (count rows, count columns).
+ pub fn is_empty(&self) -> bool {
+ let (count_rows, count_cols) = self.shape();
+ count_rows == 0 || count_cols == 0
+ }
+
+ /// Returns total widths of a table, including margin and horizontal lines.
+ pub fn total_height(&self) -> usize {
+ let mut dims = CompleteDimensionVecRecords::from_origin(&self.dimension);
+ dims.estimate(&self.records, self.config.as_ref());
+
+ let total = (0..self.count_rows())
+ .map(|row| dims.get_height(row))
+ .sum::<usize>();
+ let counth = self.config.count_horizontal(self.count_rows());
+
+ let margin = self.config.get_margin();
+
+ total + counth + margin.top.size + margin.bottom.size
+ }
+
+ /// Returns total widths of a table, including margin and vertical lines.
+ pub fn total_width(&self) -> usize {
+ let mut dims = CompleteDimensionVecRecords::from_origin(&self.dimension);
+ dims.estimate(&self.records, self.config.as_ref());
+
+ let total = (0..self.count_columns())
+ .map(|col| dims.get_width(col))
+ .sum::<usize>();
+ let countv = self.config.count_vertical(self.count_columns());
+
+ let margin = self.config.get_margin();
+
+ total + countv + margin.left.size + margin.right.size
+ }
+
+ /// Returns a table config.
+ pub fn get_config(&self) -> &ColoredConfig {
+ &self.config
+ }
+
+ /// Returns a table config.
+ pub fn get_config_mut(&mut self) -> &mut ColoredConfig {
+ &mut self.config
+ }
+
+ /// Returns a used records.
+ pub fn get_records(&self) -> &VecRecords<CellInfo<String>> {
+ &self.records
+ }
+
+ /// Returns a used records.
+ pub fn get_records_mut(&mut self) -> &mut VecRecords<CellInfo<String>> {
+ &mut self.records
+ }
+}
+
+impl Default for Table {
+ fn default() -> Self {
+ Self {
+ records: VecRecords::default(),
+ config: ColoredConfig::new(configure_grid()),
+ dimension: CompleteDimensionVecRecords::default(),
+ }
+ }
+}
+
+impl fmt::Display for Table {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.is_empty() {
+ return Ok(());
+ }
+
+ let config = use_format_configuration(f, self);
+ let colors = self.config.get_colors();
+
+ if !self.dimension.is_empty() {
+ let mut dims = self.dimension.clone();
+ dims.estimate(&self.records, config.as_ref());
+
+ print_grid(f, &self.records, &config, &dims, colors)
+ } else {
+ let mut dims = PeekableDimension::default();
+ dims.estimate(&self.records, &config);
+
+ print_grid(f, &self.records, &config, &dims, colors)
+ }
+ }
+}
+
+impl<T, V> FromIterator<T> for Table
+where
+ T: IntoIterator<Item = V>,
+ V: Into<String>,
+{
+ fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
+ Builder::from_iter(iter.into_iter().map(|i| i.into_iter().map(|s| s.into()))).build()
+ }
+}
+
+impl From<Builder> for Table {
+ fn from(builder: Builder) -> Self {
+ let data: Vec<Vec<CellInfo<String>>> = builder.into();
+ let records = VecRecords::new(data);
+
+ Self {
+ records,
+ config: ColoredConfig::new(configure_grid()),
+ dimension: CompleteDimensionVecRecords::default(),
+ }
+ }
+}
+
+impl From<Table> for Builder {
+ fn from(val: Table) -> Self {
+ let count_columns = val.count_columns();
+ let data: Vec<Vec<CellInfo<String>>> = val.records.into();
+ let mut builder = Builder::from(data);
+ let _ = builder.hint_column_size(count_columns);
+ builder
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for CompactConfig {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ *cfg.deref_mut() = self.into();
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for ColoredConfig {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ *cfg = self;
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for SpannedConfig {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ *cfg.deref_mut() = self;
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for &SpannedConfig {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ *cfg.deref_mut() = self.clone();
+ }
+}
+
+fn convert_fmt_alignment(alignment: fmt::Alignment) -> AlignmentHorizontal {
+ match alignment {
+ fmt::Alignment::Left => AlignmentHorizontal::Left,
+ fmt::Alignment::Right => AlignmentHorizontal::Right,
+ fmt::Alignment::Center => AlignmentHorizontal::Center,
+ }
+}
+
+fn table_padding(alignment: fmt::Alignment, available: usize) -> (usize, usize) {
+ match alignment {
+ fmt::Alignment::Left => (available, 0),
+ fmt::Alignment::Right => (0, available),
+ fmt::Alignment::Center => {
+ let left = available / 2;
+ let right = available - left;
+ (left, right)
+ }
+ }
+}
+
+fn configure_grid() -> SpannedConfig {
+ let mut cfg = SpannedConfig::default();
+ cfg.set_padding(
+ Entity::Global,
+ Sides::new(
+ Indent::spaced(1),
+ Indent::spaced(1),
+ Indent::default(),
+ Indent::default(),
+ ),
+ );
+ cfg.set_alignment_horizontal(Entity::Global, AlignmentHorizontal::Left);
+ cfg.set_formatting(Entity::Global, Formatting::new(false, false, false));
+ cfg.set_borders(*Style::ascii().get_borders());
+
+ cfg
+}
+
+fn use_format_configuration<'a>(
+ f: &mut fmt::Formatter<'_>,
+ table: &'a Table,
+) -> Cow<'a, SpannedConfig> {
+ if f.align().is_some() || f.width().is_some() {
+ let mut cfg = table.config.as_ref().clone();
+
+ set_align_table(f, &mut cfg);
+ set_width_table(f, &mut cfg, table);
+
+ Cow::Owned(cfg)
+ } else {
+ Cow::Borrowed(table.config.as_ref())
+ }
+}
+
+fn set_align_table(f: &fmt::Formatter<'_>, cfg: &mut SpannedConfig) {
+ if let Some(alignment) = f.align() {
+ let alignment = convert_fmt_alignment(alignment);
+ cfg.set_alignment_horizontal(Entity::Global, alignment);
+ }
+}
+
+fn set_width_table(f: &fmt::Formatter<'_>, cfg: &mut SpannedConfig, table: &Table) {
+ if let Some(width) = f.width() {
+ let total_width = table.total_width();
+ if total_width >= width {
+ return;
+ }
+
+ let mut fill = f.fill();
+ if fill == char::default() {
+ fill = ' ';
+ }
+
+ let available = width - total_width;
+ let alignment = f.align().unwrap_or(fmt::Alignment::Left);
+ let (left, right) = table_padding(alignment, available);
+
+ let mut margin = cfg.get_margin();
+ margin.left.size += left;
+ margin.right.size += right;
+
+ if (margin.left.size > 0 && margin.left.fill == char::default()) || fill != char::default()
+ {
+ margin.left.fill = fill;
+ }
+
+ if (margin.right.size > 0 && margin.right.fill == char::default())
+ || fill != char::default()
+ {
+ margin.right.fill = fill;
+ }
+
+ cfg.set_margin(margin);
+ }
+}
+
+fn print_grid<F: fmt::Write, D: Dimension>(
+ f: &mut F,
+ records: &VecRecords<CellInfo<String>>,
+ cfg: &SpannedConfig,
+ dims: D,
+ colors: &ColorMap,
+) -> fmt::Result {
+ if !colors.is_empty() {
+ PeekableGrid::new(records, cfg, &dims, colors).build(f)
+ } else {
+ PeekableGrid::new(records, cfg, &dims, NoColors).build(f)
+ }
+}
diff --git a/vendor/tabled/src/tables/table_pool.rs b/vendor/tabled/src/tables/table_pool.rs
new file mode 100644
index 000000000..07d2a5437
--- /dev/null
+++ b/vendor/tabled/src/tables/table_pool.rs
@@ -0,0 +1,1607 @@
+use core::fmt::{self, Display, Formatter};
+
+use crate::{
+ grid::{
+ config::{AlignmentHorizontal, CompactMultilineConfig, Indent, Sides},
+ dimension::{DimensionPriority, PoolTableDimension},
+ records::EmptyRecords,
+ records::IntoRecords,
+ },
+ settings::{Style, TableOption},
+};
+
+/// [`PoolTable`] is a table which allows a greater set of possibilities for cell alignment.
+/// It's data is not aligned in any way by default.
+///
+/// It works similar to the main [`Table`] by default.
+///
+///
+/// ```
+/// use tabled::tables::PoolTable;
+///
+/// let data = vec![
+/// vec!["Hello", "World", "!"],
+/// vec!["Salve", "mondo", "!"],
+/// vec!["Hola", "mundo", "!"],
+/// ];
+///
+/// let table = PoolTable::new(data).to_string();
+///
+/// assert_eq!(
+/// table,
+/// "+-------+-------+---+\n\
+/// | Hello | World | ! |\n\
+/// +-------+-------+---+\n\
+/// | Salve | mondo | ! |\n\
+/// +-------+-------+---+\n\
+/// | Hola | mundo | ! |\n\
+/// +-------+-------+---+"
+/// )
+/// ```
+///
+/// But it allows you to have a different number of columns inside the rows.
+///
+/// ```
+/// use tabled::tables::PoolTable;
+///
+/// let data = vec![
+/// vec!["Hello", "World", "!"],
+/// vec!["Salve, mondo!"],
+/// vec!["Hola", "mundo", "", "", "!"],
+/// ];
+///
+/// let table = PoolTable::new(data).to_string();
+///
+/// assert_eq!(
+/// table,
+/// "+---------+---------+----+\n\
+/// | Hello | World | ! |\n\
+/// +---------+---------+----+\n\
+/// | Salve, mondo! |\n\
+/// +------+-------+--+--+---+\n\
+/// | Hola | mundo | | | ! |\n\
+/// +------+-------+--+--+---+"
+/// )
+/// ```
+///
+/// Notice that you also can build a custom table layout by using [`TableValue`].
+///
+/// ```
+/// use tabled::tables::{PoolTable, TableValue};
+///
+/// let message = "Hello\nWorld";
+///
+/// let data = TableValue::Column(vec![
+/// TableValue::Row(vec![
+/// TableValue::Column(vec![
+/// TableValue::Cell(String::from(message)),
+/// ]),
+/// TableValue::Column(vec![
+/// TableValue::Cell(String::from(message)),
+/// TableValue::Row(vec![
+/// TableValue::Cell(String::from(message)),
+/// TableValue::Cell(String::from(message)),
+/// TableValue::Cell(String::from(message)),
+/// ])
+/// ]),
+/// ]),
+/// TableValue::Cell(String::from(message)),
+/// ]);
+///
+/// let table = PoolTable::from(data).to_string();
+///
+/// assert_eq!(
+/// table,
+/// "+-------+-----------------------+\n\
+/// | Hello | Hello |\n\
+/// | World | World |\n\
+/// | +-------+-------+-------+\n\
+/// | | Hello | Hello | Hello |\n\
+/// | | World | World | World |\n\
+/// +-------+-------+-------+-------+\n\
+/// | Hello |\n\
+/// | World |\n\
+/// +-------------------------------+"
+/// )
+/// ```
+///
+/// [`Table`]: crate::Table
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct PoolTable {
+ config: CompactMultilineConfig,
+ dims: PoolTableDimension,
+ value: TableValue,
+}
+
+impl PoolTable {
+ /// Creates a [`PoolTable`] out from a record iterator.
+ pub fn new<I: IntoRecords>(iter: I) -> Self {
+ let value = TableValue::Column(
+ iter.iter_rows()
+ .into_iter()
+ .map(|row| {
+ TableValue::Row(
+ row.into_iter()
+ .map(|cell| cell.as_ref().to_string())
+ .map(TableValue::Cell)
+ .collect(),
+ )
+ })
+ .collect(),
+ );
+
+ Self {
+ config: configure_grid(),
+ dims: PoolTableDimension::new(DimensionPriority::List, DimensionPriority::List),
+ value,
+ }
+ }
+
+ /// A is a generic function which applies options to the [`PoolTable`] configuration.
+ ///
+ /// Notice that it has a limited support of options.
+ ///
+ /// ```
+ /// use tabled::tables::PoolTable;
+ /// use tabled::settings::{Style, Padding};
+ ///
+ /// let data = vec![
+ /// vec!["Hello", "World", "!"],
+ /// vec!["Salve", "mondo", "!"],
+ /// vec!["Hola", "mundo", "!"],
+ /// ];
+ ///
+ /// let table = PoolTable::new(data)
+ /// .with(Style::extended())
+ /// .with(Padding::zero())
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "╔═════╦═════╦═╗\n\
+ /// ║Hello║World║!║\n\
+ /// ╠═════╬═════╬═╣\n\
+ /// ║Salve║mondo║!║\n\
+ /// ╠═════╬═════╬═╣\n\
+ /// ║Hola ║mundo║!║\n\
+ /// ╚═════╩═════╩═╝"
+ /// )
+ /// ```
+ pub fn with<O>(&mut self, option: O) -> &mut Self
+ where
+ O: TableOption<EmptyRecords, PoolTableDimension, CompactMultilineConfig>,
+ {
+ let mut records = EmptyRecords::default();
+ option.change(&mut records, &mut self.config, &mut self.dims);
+
+ self
+ }
+}
+
+impl From<TableValue> for PoolTable {
+ fn from(value: TableValue) -> Self {
+ Self {
+ config: configure_grid(),
+ dims: PoolTableDimension::new(DimensionPriority::List, DimensionPriority::List),
+ value,
+ }
+ }
+}
+
+impl Display for PoolTable {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ print::build_table(&self.value, &self.config, self.dims).fmt(f)
+ }
+}
+
+/// [`TableValue`] a structure which is responsible for a [`PoolTable`] layout.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum TableValue {
+ /// A horizontal row.
+ Row(Vec<TableValue>),
+ /// A vertical column.
+ Column(Vec<TableValue>),
+ /// A single cell.
+ Cell(String),
+}
+
+fn configure_grid() -> CompactMultilineConfig {
+ let pad = Sides::new(
+ Indent::spaced(1),
+ Indent::spaced(1),
+ Indent::default(),
+ Indent::default(),
+ );
+
+ CompactMultilineConfig::default()
+ .set_padding(pad)
+ .set_alignment_horizontal(AlignmentHorizontal::Left)
+ .set_borders(*Style::ascii().get_borders())
+}
+
+impl<R, C> TableOption<R, PoolTableDimension, C> for PoolTableDimension {
+ fn change(self, _: &mut R, _: &mut C, dimension: &mut PoolTableDimension) {
+ *dimension = self;
+ }
+}
+
+impl<R, D> TableOption<R, D, CompactMultilineConfig> for CompactMultilineConfig {
+ fn change(self, _: &mut R, config: &mut CompactMultilineConfig, _: &mut D) {
+ *config = self;
+ }
+}
+
+mod print {
+ use std::{cmp::max, collections::HashMap, iter::repeat};
+
+ use papergrid::{
+ color::StaticColor,
+ config::{Border, Borders},
+ util::string::string_width_multiline,
+ };
+
+ use crate::{
+ builder::Builder,
+ grid::{
+ config::{
+ AlignmentHorizontal, AlignmentVertical, ColoredConfig, CompactMultilineConfig,
+ Indent, Offset, Sides,
+ },
+ dimension::{Dimension, DimensionPriority, Estimate, PoolTableDimension},
+ records::Records,
+ util::string::{count_lines, get_lines, string_dimension, string_width},
+ },
+ settings::{Padding, Style, TableOption},
+ };
+
+ use super::TableValue;
+
+ #[derive(Debug, Default)]
+ struct PrintContext {
+ pos: usize,
+ is_last_col: bool,
+ is_last_row: bool,
+ is_first_col: bool,
+ is_first_row: bool,
+ kv: bool,
+ kv_is_first: bool,
+ list: bool,
+ list_is_first: bool,
+ no_left: bool,
+ no_right: bool,
+ no_bottom: bool,
+ lean_top: bool,
+ top_intersection: bool,
+ top_left: bool,
+ intersections_horizontal: Vec<usize>,
+ intersections_vertical: Vec<usize>,
+ size: Dim,
+ }
+
+ struct CellData {
+ content: String,
+ intersections_horizontal: Vec<usize>,
+ intersections_vertical: Vec<usize>,
+ }
+
+ impl CellData {
+ fn new(content: String, i_horizontal: Vec<usize>, i_vertical: Vec<usize>) -> Self {
+ Self {
+ content,
+ intersections_horizontal: i_horizontal,
+ intersections_vertical: i_vertical,
+ }
+ }
+ }
+
+ pub(super) fn build_table(
+ val: &TableValue,
+ cfg: &CompactMultilineConfig,
+ dims_priority: PoolTableDimension,
+ ) -> String {
+ let dims = collect_table_dimensions(val, cfg);
+ let ctx = PrintContext {
+ is_last_col: true,
+ is_last_row: true,
+ is_first_col: true,
+ is_first_row: true,
+ size: *dims.all.get(&0).unwrap(),
+ ..Default::default()
+ };
+
+ let data = _build_table(val, cfg, &dims, dims_priority, ctx);
+ let mut table = data.content;
+
+ let margin = cfg.get_margin();
+ let has_margin = margin.top.size > 0
+ || margin.bottom.size > 0
+ || margin.left.size > 0
+ || margin.right.size > 0;
+ if has_margin {
+ let color = convert_border_colors(cfg.get_margin_color());
+ table = set_margin(&table, *margin, color);
+ }
+
+ table
+ }
+
+ fn _build_table(
+ val: &TableValue,
+ cfg: &CompactMultilineConfig,
+ dims: &Dimensions,
+ priority: PoolTableDimension,
+ ctx: PrintContext,
+ ) -> CellData {
+ match val {
+ TableValue::Cell(text) => generate_value_cell(text, cfg, ctx),
+ TableValue::Row(list) => {
+ if list.is_empty() {
+ return generate_value_cell("", cfg, ctx);
+ }
+
+ generate_table_row(list, cfg, dims, priority, ctx)
+ }
+ TableValue::Column(list) => {
+ if list.is_empty() {
+ return generate_value_cell("", cfg, ctx);
+ }
+
+ generate_table_column(list, cfg, dims, priority, ctx)
+ }
+ }
+ }
+
+ fn generate_table_column(
+ list: &Vec<TableValue>,
+ cfg: &CompactMultilineConfig,
+ dims: &Dimensions,
+ priority: PoolTableDimension,
+ ctx: PrintContext,
+ ) -> CellData {
+ let array_dims = dims.arrays.get(&ctx.pos).unwrap();
+
+ let height = dims.all.get(&ctx.pos).unwrap().height;
+ let additional_height = ctx.size.height - height;
+ let (chunk_height, mut rest_height) = split_value(additional_height, list.len());
+
+ let mut intersections_horizontal = ctx.intersections_horizontal;
+ let mut intersections_vertical = ctx.intersections_vertical;
+ let mut next_vsplit = false;
+ let mut next_intersections_vertical = vec![];
+
+ let mut builder = Builder::new();
+ for (i, val) in list.iter().enumerate() {
+ let val_pos = *array_dims.index.get(&i).unwrap();
+
+ let mut height = dims.all.get(&val_pos).unwrap().height;
+ match priority.height() {
+ DimensionPriority::First => {
+ if i == 0 {
+ height += additional_height;
+ }
+ }
+ DimensionPriority::Last => {
+ if i + 1 == list.len() {
+ height += additional_height;
+ }
+ }
+ DimensionPriority::List => {
+ height += chunk_height;
+
+ if rest_height > 0 {
+ height += 1;
+ rest_height -= 1; // must be safe
+ }
+ }
+ }
+
+ let size = Dim::new(ctx.size.width, height);
+
+ let (split, intersections_vertical) =
+ short_splits3(&mut intersections_vertical, size.height);
+ let old_split = next_vsplit;
+ next_vsplit = split;
+
+ let is_prev_list_not_first = ctx.list && !ctx.list_is_first;
+ let valctx = PrintContext {
+ pos: val_pos,
+ is_last_col: ctx.is_last_col,
+ is_last_row: ctx.is_last_row && i + 1 == list.len(),
+ is_first_col: ctx.is_first_col,
+ is_first_row: ctx.is_first_row && i == 0,
+ kv: ctx.kv,
+ kv_is_first: ctx.kv_is_first,
+ list: true,
+ list_is_first: i == 0 && !is_prev_list_not_first,
+ no_left: ctx.no_left,
+ no_right: ctx.no_right,
+ no_bottom: ctx.no_bottom && i + 1 == list.len(),
+ lean_top: ctx.lean_top && i == 0,
+ top_intersection: (ctx.top_intersection && i == 0) || old_split,
+ top_left: ctx.top_left || i > 0,
+ intersections_horizontal,
+ intersections_vertical,
+ size,
+ };
+
+ let data = _build_table(val, cfg, dims, priority, valctx);
+ intersections_horizontal = data.intersections_horizontal;
+ next_intersections_vertical.extend(data.intersections_vertical);
+
+ let _ = builder.push_record([data.content]);
+ }
+
+ let table = builder
+ .build()
+ .with(Style::empty())
+ .with(Padding::zero())
+ .to_string();
+
+ CellData::new(table, intersections_horizontal, next_intersections_vertical)
+ }
+
+ fn generate_table_row(
+ list: &Vec<TableValue>,
+ cfg: &CompactMultilineConfig,
+ dims: &Dimensions,
+ priority: PoolTableDimension,
+ ctx: PrintContext,
+ ) -> CellData {
+ let array_dims = dims.arrays.get(&ctx.pos).unwrap();
+
+ let list_width = dims.all.get(&ctx.pos).unwrap().width;
+ let additional_width = ctx.size.width - list_width;
+ let (chunk_width, mut rest_width) = split_value(additional_width, list.len());
+
+ let mut intersections_horizontal = ctx.intersections_horizontal;
+ let mut intersections_vertical = ctx.intersections_vertical;
+ let mut new_intersections_horizontal = vec![];
+ let mut split_next = false;
+
+ let mut buf = Vec::with_capacity(list.len());
+ for (i, val) in list.iter().enumerate() {
+ let val_pos = *array_dims.index.get(&i).unwrap();
+
+ let mut width = dims.all.get(&val_pos).unwrap().width;
+ match priority.width() {
+ DimensionPriority::First => {
+ if i == 0 {
+ width += additional_width;
+ }
+ }
+ DimensionPriority::Last => {
+ if i + 1 == list.len() {
+ width += additional_width;
+ }
+ }
+ DimensionPriority::List => {
+ width += chunk_width;
+
+ if rest_width > 0 {
+ width += 1;
+ rest_width -= 1; // must be safe
+ }
+ }
+ }
+
+ let size = Dim::new(width, ctx.size.height);
+
+ let (split, intersections_horizontal) =
+ short_splits3(&mut intersections_horizontal, width);
+ let old_split = split_next;
+ split_next = split;
+
+ let is_prev_list_not_first = ctx.list && !ctx.list_is_first;
+ let valctx = PrintContext {
+ pos: val_pos,
+ is_first_col: ctx.is_first_col && i == 0,
+ is_last_col: ctx.is_last_col && i + 1 == list.len(),
+ is_last_row: ctx.is_last_row,
+ is_first_row: ctx.is_first_row,
+ kv: false,
+ kv_is_first: false,
+ list: false,
+ list_is_first: !is_prev_list_not_first,
+ no_left: false,
+ no_right: !(ctx.is_last_col && i + 1 == list.len()),
+ no_bottom: false,
+ lean_top: !(ctx.is_first_col && i == 0),
+ top_intersection: (ctx.top_intersection && i == 0) || old_split,
+ top_left: ctx.top_left && i == 0,
+ intersections_horizontal,
+ intersections_vertical,
+ size,
+ };
+
+ let val = _build_table(val, cfg, dims, priority, valctx);
+ intersections_vertical = val.intersections_vertical;
+ new_intersections_horizontal.extend(val.intersections_horizontal.iter());
+ let value = val.content;
+
+ buf.push(value);
+ }
+
+ let mut b = Builder::with_capacity(1);
+ let _ = b.hint_column_size(buf.len()).push_record(buf);
+ let table = b
+ .build()
+ .with(Style::empty())
+ .with(Padding::zero())
+ .to_string();
+
+ CellData::new(table, new_intersections_horizontal, intersections_vertical)
+ }
+
+ fn generate_value_cell(
+ text: &str,
+ cfg: &CompactMultilineConfig,
+ ctx: PrintContext,
+ ) -> CellData {
+ let width = ctx.size.width;
+ let height = ctx.size.height;
+ let table = generate_value_table(text, cfg, ctx);
+ CellData::new(table, vec![width], vec![height])
+ }
+
+ fn generate_value_table(
+ text: &str,
+ cfg: &CompactMultilineConfig,
+ mut ctx: PrintContext,
+ ) -> String {
+ if ctx.size.width == 0 || ctx.size.height == 0 {
+ return String::new();
+ }
+
+ let halignment = cfg.get_alignment_horizontal();
+ let valignment = cfg.get_alignment_vertical();
+ let pad = cfg.get_padding();
+ let pad_color = cfg.get_padding_color();
+ let pad_color = convert_border_colors(pad_color);
+ let lines_alignemnt = cfg.get_formatting().allow_lines_alignment;
+
+ let mut borders = *cfg.get_borders();
+
+ let bottom_intesection = cfg.get_borders().bottom_intersection.unwrap_or(' ');
+ let mut horizontal_splits = short_splits(&mut ctx.intersections_horizontal, ctx.size.width);
+ squash_splits(&mut horizontal_splits);
+
+ let right_intersection = borders.right_intersection.unwrap_or(' ');
+ let mut vertical_splits = short_splits(&mut ctx.intersections_vertical, ctx.size.height);
+ squash_splits(&mut vertical_splits);
+
+ config_borders(&mut borders, &ctx);
+ let border = create_border(borders);
+
+ let borders_colors = *cfg.get_borders_color();
+ let border_color = create_border(borders_colors);
+
+ let mut height = ctx.size.height;
+ height -= pad.top.size + pad.bottom.size;
+
+ let mut width = ctx.size.width;
+ width -= pad.left.size + pad.right.size;
+
+ let count_lines = count_lines(text);
+ let (top, bottom) = indent_vertical(valignment, height, count_lines);
+
+ let mut buf = String::new();
+ print_top_line(
+ &mut buf,
+ border,
+ border_color,
+ &horizontal_splits,
+ bottom_intesection,
+ ctx.size.width,
+ );
+
+ let mut line_index = 0;
+ let mut vertical_splits = &vertical_splits[..];
+
+ for _ in 0..top {
+ let mut border = border;
+ if vertical_splits.first() == Some(&line_index) {
+ border.left = Some(right_intersection);
+ vertical_splits = &vertical_splits[1..];
+ }
+
+ print_line(&mut buf, border, border_color, None, ' ', ctx.size.width);
+ line_index += 1;
+ }
+
+ for _ in 0..pad.top.size {
+ let mut border = border;
+ if vertical_splits.first() == Some(&line_index) {
+ border.left = Some(right_intersection);
+ vertical_splits = &vertical_splits[1..];
+ }
+
+ print_line(
+ &mut buf,
+ border,
+ border_color,
+ pad_color.top,
+ pad.top.fill,
+ ctx.size.width,
+ );
+ line_index += 1;
+ }
+
+ if lines_alignemnt {
+ for line in get_lines(text) {
+ let line_width = string_width(&line);
+ let (left, right) = indent_horizontal(halignment, width, line_width);
+
+ if border.has_left() {
+ let mut c = border.left.unwrap_or(' ');
+ if vertical_splits.first() == Some(&line_index) {
+ c = right_intersection;
+ vertical_splits = &vertical_splits[1..];
+ }
+
+ print_char(&mut buf, c, border_color.left);
+ }
+
+ print_chars(&mut buf, pad.left.fill, pad_color.left, pad.left.size);
+ buf.extend(repeat(' ').take(left));
+ buf.push_str(&line);
+ buf.extend(repeat(' ').take(right));
+ print_chars(&mut buf, pad.right.fill, pad_color.right, pad.right.size);
+
+ if border.has_right() {
+ print_char(&mut buf, border.right.unwrap_or(' '), border_color.right);
+ }
+
+ buf.push('\n');
+
+ line_index += 1;
+ }
+ } else {
+ let text_width = string_width_multiline(text);
+ let (left, _) = indent_horizontal(halignment, width, text_width);
+
+ for line in get_lines(text) {
+ let line_width = string_width(&line);
+ let right = width - line_width - left;
+
+ if border.has_left() {
+ let mut c = border.left.unwrap_or(' ');
+ if vertical_splits.first() == Some(&line_index) {
+ c = right_intersection;
+ vertical_splits = &vertical_splits[1..];
+ }
+
+ print_char(&mut buf, c, border_color.left);
+ }
+
+ print_chars(&mut buf, pad.left.fill, pad_color.left, pad.left.size);
+ buf.extend(repeat(' ').take(left));
+ buf.push_str(&line);
+ buf.extend(repeat(' ').take(right));
+ print_chars(&mut buf, pad.right.fill, pad_color.right, pad.right.size);
+
+ if border.has_right() {
+ print_char(&mut buf, border.right.unwrap_or(' '), border_color.right);
+ }
+
+ buf.push('\n');
+
+ line_index += 1;
+ }
+ }
+
+ for _ in 0..pad.bottom.size {
+ let mut border = border;
+ if vertical_splits.first() == Some(&line_index) {
+ border.left = Some(right_intersection);
+ vertical_splits = &vertical_splits[1..];
+ }
+
+ print_line(
+ &mut buf,
+ border,
+ border_color,
+ pad_color.bottom,
+ pad.bottom.fill,
+ ctx.size.width,
+ );
+
+ line_index += 1;
+ }
+
+ for _ in 0..bottom {
+ let mut border = border;
+ if vertical_splits.first() == Some(&line_index) {
+ border.left = Some(right_intersection);
+ vertical_splits = &vertical_splits[1..];
+ }
+
+ print_line(&mut buf, border, border_color, None, ' ', ctx.size.width);
+ line_index += 1;
+ }
+
+ print_bottom_line(&mut buf, border, border_color, ctx.size.width);
+
+ let _ = buf.remove(buf.len() - 1);
+
+ buf
+ }
+
+ fn print_chars(buf: &mut String, c: char, color: Option<StaticColor>, width: usize) {
+ match color {
+ Some(color) => {
+ buf.push_str(color.get_prefix());
+ buf.extend(repeat(c).take(width));
+ buf.push_str(color.get_suffix());
+ }
+ None => buf.extend(repeat(c).take(width)),
+ }
+ }
+
+ fn print_char(buf: &mut String, c: char, color: Option<StaticColor>) {
+ match color {
+ Some(color) => {
+ buf.push_str(color.get_prefix());
+ buf.push(c);
+ buf.push_str(color.get_suffix());
+ }
+ None => buf.push(c),
+ }
+ }
+
+ fn print_line(
+ buf: &mut String,
+ border: Border<char>,
+ border_color: Border<StaticColor>,
+ color: Option<StaticColor>,
+ c: char,
+ width: usize,
+ ) {
+ if border.has_left() {
+ let c = border.left.unwrap_or(' ');
+ print_char(buf, c, border_color.left);
+ }
+
+ print_chars(buf, c, color, width);
+
+ if border.has_right() {
+ let c = border.right.unwrap_or(' ');
+ print_char(buf, c, border_color.right);
+ }
+
+ buf.push('\n');
+ }
+
+ fn print_top_line(
+ buf: &mut String,
+ border: Border<char>,
+ color: Border<StaticColor>,
+ splits: &[usize],
+ split_char: char,
+ width: usize,
+ ) {
+ if !border.has_top() {
+ return;
+ }
+
+ let mut used_color: Option<StaticColor> = None;
+
+ if border.has_left() {
+ if let Some(color) = color.left_top_corner {
+ used_color = Some(color);
+ buf.push_str(color.get_prefix());
+ }
+
+ let c = border.left_top_corner.unwrap_or(' ');
+ buf.push(c);
+ }
+
+ if let Some(color) = color.top {
+ match used_color {
+ Some(used) => {
+ if used != color {
+ buf.push_str(used.get_suffix());
+ buf.push_str(color.get_prefix());
+ }
+ }
+ None => {
+ buf.push_str(color.get_prefix());
+ used_color = Some(color);
+ }
+ }
+ }
+
+ let c = border.top.unwrap_or(' ');
+ if splits.is_empty() {
+ buf.extend(repeat(c).take(width));
+ } else {
+ let mut splits = splits;
+ for i in 0..width {
+ if splits.first() == Some(&i) {
+ buf.push(split_char);
+ splits = &splits[1..];
+ } else {
+ buf.push(c);
+ }
+ }
+ }
+
+ if border.has_right() {
+ if let Some(color) = color.right_top_corner {
+ match used_color {
+ Some(used) => {
+ if used != color {
+ buf.push_str(used.get_suffix());
+ buf.push_str(color.get_prefix());
+ }
+ }
+ None => {
+ buf.push_str(color.get_prefix());
+ used_color = Some(color);
+ }
+ }
+ }
+
+ let c = border.right_top_corner.unwrap_or(' ');
+ buf.push(c);
+ }
+
+ if let Some(used) = used_color {
+ buf.push_str(used.get_suffix());
+ }
+
+ buf.push('\n');
+ }
+
+ fn print_bottom_line(
+ buf: &mut String,
+ border: Border<char>,
+ color: Border<StaticColor>,
+ width: usize,
+ ) {
+ if !border.has_bottom() {
+ return;
+ }
+
+ let mut used_color: Option<StaticColor> = None;
+
+ if border.has_left() {
+ if let Some(color) = color.left_bottom_corner {
+ used_color = Some(color);
+ buf.push_str(color.get_prefix());
+ }
+
+ let c = border.left_bottom_corner.unwrap_or(' ');
+ buf.push(c);
+ }
+
+ if let Some(color) = color.bottom {
+ match used_color {
+ Some(used) => {
+ if used != color {
+ buf.push_str(used.get_suffix());
+ buf.push_str(color.get_prefix());
+ }
+ }
+ None => {
+ buf.push_str(color.get_prefix());
+ used_color = Some(color);
+ }
+ }
+ }
+
+ let c = border.bottom.unwrap_or(' ');
+ buf.extend(repeat(c).take(width));
+
+ if border.has_right() {
+ if let Some(color) = color.right_bottom_corner {
+ match used_color {
+ Some(used) => {
+ if used != color {
+ buf.push_str(used.get_suffix());
+ buf.push_str(color.get_prefix());
+ }
+ }
+ None => {
+ buf.push_str(color.get_prefix());
+ used_color = Some(color);
+ }
+ }
+ }
+
+ let c = border.right_bottom_corner.unwrap_or(' ');
+ buf.push(c);
+ }
+
+ if let Some(used) = used_color {
+ buf.push_str(used.get_suffix());
+ }
+
+ buf.push('\n');
+ }
+
+ fn create_border<T>(borders: Borders<T>) -> Border<T> {
+ Border {
+ top: borders.top,
+ bottom: borders.bottom,
+ left: borders.left,
+ right: borders.right,
+ left_top_corner: borders.top_left,
+ left_bottom_corner: borders.bottom_left,
+ right_top_corner: borders.top_right,
+ right_bottom_corner: borders.bottom_right,
+ }
+ }
+
+ fn config_borders(borders: &mut Borders<char>, ctx: &PrintContext) {
+ // set top_left
+ {
+ if ctx.kv && ctx.kv_is_first {
+ borders.top_left = borders.top_intersection;
+ }
+
+ if ctx.kv && !ctx.kv_is_first {
+ borders.top_left = borders.intersection;
+ }
+
+ if ctx.kv && ctx.list && !ctx.list_is_first {
+ borders.top_left = borders.left_intersection;
+ }
+
+ if ctx.is_first_col && !ctx.is_first_row {
+ borders.top_left = borders.left_intersection;
+ }
+
+ if ctx.lean_top {
+ borders.top_left = borders.top_intersection;
+ }
+
+ if ctx.top_left {
+ borders.top_left = borders.left_intersection;
+ }
+
+ if ctx.top_intersection {
+ borders.top_left = borders.intersection;
+ }
+ }
+
+ if ctx.is_last_col && !ctx.is_first_row {
+ borders.top_right = borders.right_intersection;
+ }
+
+ if !ctx.is_first_col && ctx.is_last_row {
+ borders.bottom_left = borders.bottom_intersection;
+ }
+
+ if !ctx.is_last_row || ctx.no_bottom {
+ cfg_no_bottom_borders(borders);
+ }
+
+ if ctx.no_right {
+ cfg_no_right_borders(borders);
+ }
+ }
+
+ struct ConfigCell(PrintContext);
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for ConfigCell {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ {
+ // we set a horizontal lines to borders to not complicate logic with cleaning it
+
+ let mut borders = *cfg.get_borders();
+ if let Some(line) = cfg.get_horizontal_line(0) {
+ borders.top = line.main;
+ borders.top_left = line.left;
+ borders.top_right = line.right;
+ }
+
+ if let Some(line) = cfg.get_horizontal_line(1) {
+ borders.bottom = line.main;
+ borders.bottom_left = line.left;
+ borders.bottom_right = line.right;
+ }
+
+ cfg.clear_theme();
+ cfg.set_borders(borders);
+ }
+
+ let mut ctx = self.0;
+
+ let has_vertical = cfg.get_borders().has_left();
+ if !ctx.intersections_horizontal.is_empty() && has_vertical {
+ let mut splits = short_splits(&mut ctx.intersections_horizontal, ctx.size.width);
+ squash_splits(&mut splits);
+
+ let c = cfg.get_borders().bottom_intersection.unwrap_or(' ');
+ cfg_set_top_chars(cfg, &splits, c)
+ }
+
+ let has_horizontal = cfg.get_borders().has_top();
+ if !ctx.intersections_vertical.is_empty() && has_horizontal {
+ let mut splits = short_splits(&mut ctx.intersections_vertical, ctx.size.height);
+ squash_splits(&mut splits);
+
+ let c = cfg.get_borders().right_intersection.unwrap_or(' ');
+ cfg_set_left_chars(cfg, &splits, c)
+ }
+
+ let mut borders = *cfg.get_borders();
+
+ // set top_left
+ {
+ if ctx.kv && ctx.kv_is_first {
+ borders.top_left = borders.top_intersection;
+ }
+
+ if ctx.kv && !ctx.kv_is_first {
+ borders.top_left = borders.intersection;
+ }
+
+ if ctx.kv && ctx.list && !ctx.list_is_first {
+ borders.top_left = borders.left_intersection;
+ }
+
+ if ctx.is_first_col && !ctx.is_first_row {
+ borders.top_left = borders.left_intersection;
+ }
+
+ if ctx.lean_top {
+ borders.top_left = borders.top_intersection;
+ }
+
+ if ctx.top_left {
+ borders.top_left = borders.left_intersection;
+ }
+
+ if ctx.top_intersection {
+ borders.top_left = borders.intersection;
+ }
+ }
+
+ if ctx.is_last_col && !ctx.is_first_row {
+ borders.top_right = borders.right_intersection;
+ }
+
+ if !ctx.is_first_col && ctx.is_last_row {
+ borders.bottom_left = borders.bottom_intersection;
+ }
+
+ if !ctx.is_last_row || ctx.no_bottom {
+ cfg_no_bottom_borders(&mut borders);
+ }
+
+ if ctx.no_right {
+ cfg_no_right_borders(&mut borders);
+ }
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ fn cfg_no_bottom_borders(borders: &mut Borders<char>) {
+ borders.bottom = None;
+ borders.bottom_intersection = None;
+ borders.bottom_left = None;
+ borders.bottom_right = None;
+ borders.horizontal = None;
+ }
+
+ fn cfg_no_right_borders(borders: &mut Borders<char>) {
+ borders.right = None;
+ borders.right_intersection = None;
+ borders.top_right = None;
+ borders.bottom_right = None;
+ borders.vertical = None;
+ }
+
+ fn cfg_set_top_chars(cfg: &mut ColoredConfig, list: &[usize], c: char) {
+ for &split in list {
+ let offset = split;
+ cfg.set_horizontal_char((0, 0), c, Offset::Begin(offset));
+ }
+ }
+
+ fn cfg_set_left_chars(cfg: &mut ColoredConfig, list: &[usize], c: char) {
+ for &offset in list {
+ cfg.set_vertical_char((0, 0), c, Offset::Begin(offset));
+ }
+ }
+
+ struct NoTopBorders;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for NoTopBorders {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.top = None;
+ borders.top_intersection = None;
+ borders.top_left = None;
+ borders.top_right = None;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct NoBottomBorders;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for NoBottomBorders {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.bottom = None;
+ borders.bottom_intersection = None;
+ borders.bottom_left = None;
+ borders.bottom_right = None;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct NoRightBorders;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for NoRightBorders {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.top_right = None;
+ borders.bottom_right = None;
+ borders.right = None;
+ borders.right_intersection = None;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct NoLeftBorders;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for NoLeftBorders {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.top_left = None;
+ borders.bottom_left = None;
+ borders.left = None;
+ borders.left_intersection = None;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct TopLeftChangeTopIntersection;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for TopLeftChangeTopIntersection {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.top_left = borders.top_intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct TopLeftChangeIntersection;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for TopLeftChangeIntersection {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.top_left = borders.intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct TopLeftChangeToLeft;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for TopLeftChangeToLeft {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.top_left = borders.left_intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct TopRightChangeToRight;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for TopRightChangeToRight {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.top_right = borders.right_intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct BottomLeftChangeSplit;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for BottomLeftChangeSplit {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.bottom_left = borders.left_intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct BottomLeftChangeSplitToIntersection;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for BottomLeftChangeSplitToIntersection {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.bottom_left = borders.intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct BottomRightChangeToRight;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for BottomRightChangeToRight {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.bottom_right = borders.right_intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct BottomLeftChangeToBottomIntersection;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for BottomLeftChangeToBottomIntersection {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.bottom_left = borders.bottom_intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct SetBottomChars<'a>(&'a [usize], char);
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for SetBottomChars<'_>
+ where
+ R: Records,
+ for<'a> &'a R: Records,
+ for<'a> D: Dimension + Estimate<&'a R, ColoredConfig>,
+ {
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dims: &mut D) {
+ dims.estimate(&*records, cfg);
+
+ let table_width = (0..records.count_columns())
+ .map(|col| dims.get_width(col))
+ .sum::<usize>()
+ + cfg.count_vertical(records.count_columns());
+ let mut current_width = 0;
+
+ for pos in self.0 {
+ current_width += pos;
+ if current_width > table_width {
+ break;
+ }
+
+ let split_char = self.1;
+ cfg.set_horizontal_char((1, 0), split_char, Offset::Begin(current_width));
+
+ current_width += 1;
+ }
+ }
+ }
+
+ struct SetTopChars<'a>(&'a [usize], char);
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for SetTopChars<'_> {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ for &split in self.0 {
+ let offset = split;
+ cfg.set_horizontal_char((0, 0), self.1, Offset::Begin(offset));
+ }
+ }
+ }
+
+ struct SetLeftChars<'a>(&'a [usize], char);
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for SetLeftChars<'_> {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ for &offset in self.0 {
+ cfg.set_vertical_char((0, 0), self.1, Offset::Begin(offset));
+ }
+ }
+ }
+
+ struct GetTopIntersection(char);
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for &mut GetTopIntersection {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ self.0 = cfg.get_borders().top_intersection.unwrap_or(' ');
+ }
+ }
+
+ struct GetBottomIntersection(char);
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for &mut GetBottomIntersection {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ self.0 = cfg.get_borders().bottom_intersection.unwrap_or(' ');
+ }
+ }
+
+ #[derive(Debug, Default)]
+ struct Dimensions {
+ all: HashMap<usize, Dim>,
+ arrays: HashMap<usize, ArrayDimensions>,
+ }
+
+ #[derive(Debug, Default, Clone, Copy)]
+ struct Dim {
+ width: usize,
+ height: usize,
+ }
+
+ impl Dim {
+ fn new(width: usize, height: usize) -> Self {
+ Self { width, height }
+ }
+ }
+
+ #[derive(Debug, Default)]
+ struct ArrayDimensions {
+ max: Dim,
+ index: HashMap<usize, usize>,
+ }
+
+ fn collect_table_dimensions(val: &TableValue, cfg: &CompactMultilineConfig) -> Dimensions {
+ let mut buf = Dimensions::default();
+ let (dim, _) = __collect_table_dims(&mut buf, val, cfg, 0);
+ let _ = buf.all.insert(0, dim);
+ buf
+ }
+
+ fn __collect_table_dims(
+ buf: &mut Dimensions,
+ val: &TableValue,
+ cfg: &CompactMultilineConfig,
+ pos: usize,
+ ) -> (Dim, usize) {
+ match val {
+ TableValue::Cell(text) => (str_dimension(text, cfg), 0),
+ TableValue::Row(list) => {
+ if list.is_empty() {
+ return (empty_dimension(cfg), 0);
+ }
+
+ let mut index = ArrayDimensions {
+ max: Dim::default(),
+ index: HashMap::with_capacity(list.len()),
+ };
+
+ let mut total_width = 0;
+
+ let mut count_elements = list.len();
+ let mut val_pos = pos + 1;
+ for (i, value) in list.iter().enumerate() {
+ let (dim, elements) = __collect_table_dims(buf, value, cfg, val_pos);
+ count_elements += elements;
+
+ total_width += dim.width;
+
+ index.max.width = max(index.max.width, dim.width);
+ index.max.height = max(index.max.height, dim.height);
+
+ let _ = buf.all.insert(val_pos, dim);
+
+ let _ = index.index.insert(i, val_pos);
+
+ val_pos += 1 + elements;
+ }
+
+ let max_height = index.max.height;
+
+ let _ = buf.arrays.insert(pos, index);
+
+ let has_vertical = cfg.get_borders().has_left();
+ total_width += has_vertical as usize * (list.len() - 1);
+
+ (Dim::new(total_width, max_height), count_elements)
+ }
+ TableValue::Column(list) => {
+ if list.is_empty() {
+ return (empty_dimension(cfg), 0);
+ }
+
+ let mut index = ArrayDimensions {
+ max: Dim::default(),
+ index: HashMap::with_capacity(list.len()),
+ };
+
+ let mut total_height = 0;
+
+ let mut count_elements = list.len();
+ let mut val_pos = pos + 1;
+ for (i, value) in list.iter().enumerate() {
+ let (dim, elements) = __collect_table_dims(buf, value, cfg, val_pos);
+ count_elements += elements;
+
+ total_height += dim.height;
+
+ index.max.width = max(index.max.width, dim.width);
+ index.max.height = max(index.max.height, dim.height);
+
+ let _ = buf.all.insert(val_pos, dim);
+
+ let _ = index.index.insert(i, val_pos);
+
+ val_pos += 1 + elements;
+ }
+
+ let max_width = index.max.width;
+
+ let _ = buf.arrays.insert(pos, index);
+
+ let has_horizontal = cfg.get_borders().has_top();
+ total_height += has_horizontal as usize * (list.len() - 1);
+
+ (Dim::new(max_width, total_height), count_elements)
+ }
+ }
+ }
+
+ fn empty_dimension(cfg: &CompactMultilineConfig) -> Dim {
+ Dim::new(get_padding_horizontal(cfg), 1 + get_padding_vertical(cfg))
+ }
+
+ fn str_dimension(text: &str, cfg: &CompactMultilineConfig) -> Dim {
+ let (count_lines, width) = string_dimension(text);
+ let w = width + get_padding_horizontal(cfg);
+ let h = count_lines + get_padding_vertical(cfg);
+ Dim::new(w, h)
+ }
+
+ fn get_padding_horizontal(cfg: &CompactMultilineConfig) -> usize {
+ let pad = cfg.get_padding();
+ pad.left.size + pad.right.size
+ }
+
+ fn get_padding_vertical(cfg: &CompactMultilineConfig) -> usize {
+ let pad = cfg.get_padding();
+ pad.top.size + pad.bottom.size
+ }
+
+ fn split_value(value: usize, by: usize) -> (usize, usize) {
+ let val = value / by;
+ let rest = value - val * by;
+ (val, rest)
+ }
+
+ fn indent_vertical(al: AlignmentVertical, available: usize, real: usize) -> (usize, usize) {
+ let top = indent_top(al, available, real);
+ let bottom = available - real - top;
+ (top, bottom)
+ }
+
+ fn indent_horizontal(al: AlignmentHorizontal, available: usize, real: usize) -> (usize, usize) {
+ let top = indent_left(al, available, real);
+ let right = available - real - top;
+ (top, right)
+ }
+
+ fn indent_top(al: AlignmentVertical, available: usize, real: usize) -> usize {
+ match al {
+ AlignmentVertical::Top => 0,
+ AlignmentVertical::Bottom => available - real,
+ AlignmentVertical::Center => (available - real) / 2,
+ }
+ }
+
+ fn indent_left(al: AlignmentHorizontal, available: usize, real: usize) -> usize {
+ match al {
+ AlignmentHorizontal::Left => 0,
+ AlignmentHorizontal::Right => available - real,
+ AlignmentHorizontal::Center => (available - real) / 2,
+ }
+ }
+
+ fn short_splits(splits: &mut Vec<usize>, width: usize) -> Vec<usize> {
+ if splits.is_empty() {
+ return Vec::new();
+ }
+
+ let mut out = Vec::new();
+ let mut pos = 0;
+ for &split in splits.iter() {
+ if pos + split >= width {
+ break;
+ }
+
+ pos += split;
+ out.push(pos);
+ }
+
+ let _ = splits.drain(..out.len());
+
+ if !splits.is_empty() && pos <= width {
+ let rest = width - pos;
+ splits[0] -= rest;
+ }
+
+ out
+ }
+
+ fn short_splits3(splits: &mut Vec<usize>, width: usize) -> (bool, Vec<usize>) {
+ if splits.is_empty() {
+ return (false, Vec::new());
+ }
+
+ let mut out = Vec::new();
+ let mut pos = 0;
+ for &split in splits.iter() {
+ if pos + split >= width {
+ break;
+ }
+
+ pos += split + 1;
+ out.push(split);
+ }
+
+ let _ = splits.drain(..out.len());
+
+ if splits.is_empty() {
+ return (false, out);
+ }
+
+ if pos <= width {
+ splits[0] -= width - pos;
+ if splits[0] > 0 {
+ splits[0] -= 1;
+ } else {
+ let _ = splits.remove(0);
+ return (true, out);
+ }
+ }
+
+ (false, out)
+ }
+
+ fn squash_splits(splits: &mut [usize]) {
+ splits.iter_mut().enumerate().for_each(|(i, s)| *s += i);
+ }
+
+ fn set_margin(table: &str, margin: Sides<Indent>, color: Sides<Option<StaticColor>>) -> String {
+ if table.is_empty() {
+ return String::new();
+ }
+
+ let mut buf = String::new();
+ let width = string_width_multiline(table);
+ let top_color = color.top;
+ let bottom_color = color.bottom;
+ let left_color = color.left;
+ let right_color = color.right;
+ for _ in 0..margin.top.size {
+ print_chars(&mut buf, margin.left.fill, left_color, margin.left.size);
+ print_chars(&mut buf, margin.top.fill, top_color, width);
+ print_chars(&mut buf, margin.right.fill, right_color, margin.right.size);
+ buf.push('\n');
+ }
+
+ for line in get_lines(table) {
+ print_chars(&mut buf, margin.left.fill, left_color, margin.left.size);
+ buf.push_str(&line);
+ print_chars(&mut buf, margin.right.fill, right_color, margin.right.size);
+ buf.push('\n');
+ }
+
+ for _ in 0..margin.bottom.size {
+ print_chars(&mut buf, margin.left.fill, left_color, margin.left.size);
+ print_chars(&mut buf, margin.bottom.fill, bottom_color, width);
+ print_chars(&mut buf, margin.right.fill, right_color, margin.right.size);
+ buf.push('\n');
+ }
+
+ let _ = buf.remove(buf.len() - 1);
+
+ buf
+ }
+
+ fn convert_border_colors(pad_color: Sides<StaticColor>) -> Sides<Option<StaticColor>> {
+ Sides::new(
+ (!pad_color.left.is_empty()).then(|| pad_color.left),
+ (!pad_color.right.is_empty()).then(|| pad_color.right),
+ (!pad_color.top.is_empty()).then(|| pad_color.top),
+ (!pad_color.bottom.is_empty()).then(|| pad_color.bottom),
+ )
+ }
+}
diff --git a/vendor/tabled/src/tables/util/mod.rs b/vendor/tabled/src/tables/util/mod.rs
new file mode 100644
index 000000000..ea5453b8f
--- /dev/null
+++ b/vendor/tabled/src/tables/util/mod.rs
@@ -0,0 +1,2 @@
+#[cfg(feature = "std")]
+pub(crate) mod utf8_writer;
diff --git a/vendor/tabled/src/tables/util/utf8_writer.rs b/vendor/tabled/src/tables/util/utf8_writer.rs
new file mode 100644
index 000000000..d4fc515df
--- /dev/null
+++ b/vendor/tabled/src/tables/util/utf8_writer.rs
@@ -0,0 +1,29 @@
+use std::fmt;
+use std::io;
+
+pub(crate) struct UTF8Writer<W>(W);
+
+impl<W> UTF8Writer<W> {
+ pub(crate) fn new(writer: W) -> Self {
+ Self(writer)
+ }
+}
+
+impl<W> fmt::Write for UTF8Writer<W>
+where
+ W: io::Write,
+{
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ let mut buf = s.as_bytes();
+ loop {
+ let n = self.0.write(buf).map_err(|_| fmt::Error)?;
+ if n == buf.len() {
+ break;
+ }
+
+ buf = &buf[n..];
+ }
+
+ Ok(())
+ }
+}