use crate::table_theme::TableTheme; use nu_ansi_term::Style; use nu_color_config::TextStyle; use nu_protocol::TrimStrategy; use std::cmp::min; use std::collections::HashMap; use tabled::{ builder::Builder, grid::{ color::AnsiColor, config::{AlignmentHorizontal, ColoredConfig, Entity, EntityMap, Position}, dimension::CompleteDimensionVecRecords, records::{ vec_records::{CellInfo, VecRecords}, ExactRecords, Records, }, }, settings::{ formatting::AlignmentStrategy, object::Segment, peaker::Peaker, Color, Modify, Settings, TableOption, Width, }, Table, }; /// Table represent a table view. #[derive(Debug, Clone)] pub struct NuTable { data: Data, styles: Styles, alignments: Alignments, size: (usize, usize), } #[derive(Debug, Default, Clone)] struct Styles { index: AnsiColor<'static>, header: AnsiColor<'static>, data: EntityMap>, data_is_set: bool, } type Data = VecRecords; pub type Cell = CellInfo; impl NuTable { /// Creates an empty [Table] instance. pub fn new(count_rows: usize, count_columns: usize) -> Self { let data = VecRecords::new(vec![vec![CellInfo::default(); count_columns]; count_rows]); Self { data, size: (count_rows, count_columns), styles: Styles::default(), alignments: Alignments::default(), } } pub fn count_rows(&self) -> usize { self.size.0 } pub fn count_columns(&self) -> usize { self.size.1 } pub fn insert(&mut self, pos: Position, text: String) { self.data[pos.0][pos.1] = CellInfo::new(text); } pub fn set_column_style(&mut self, column: usize, style: TextStyle) { if let Some(style) = style.color_style { let style = AnsiColor::from(convert_style(style)); self.styles.data.insert(Entity::Column(column), style); self.styles.data_is_set = true; } let alignment = convert_alignment(style.alignment); if alignment != self.alignments.data { self.alignments.columns.insert(column, alignment); } } pub fn set_cell_style(&mut self, pos: Position, style: TextStyle) { if let Some(style) = style.color_style { let style = AnsiColor::from(convert_style(style)); self.styles.data.insert(Entity::Cell(pos.0, pos.1), style); self.styles.data_is_set = true; } let alignment = convert_alignment(style.alignment); if alignment != self.alignments.data { self.alignments.cells.insert(pos, alignment); } } pub fn set_header_style(&mut self, style: TextStyle) { if let Some(style) = style.color_style { let style = AnsiColor::from(convert_style(style)); self.styles.header = style; } self.alignments.header = convert_alignment(style.alignment); } pub fn set_index_style(&mut self, style: TextStyle) { if let Some(style) = style.color_style { let style = AnsiColor::from(convert_style(style)); self.styles.index = style; } self.alignments.index = convert_alignment(style.alignment); } pub fn set_data_style(&mut self, style: TextStyle) { if let Some(style) = style.color_style { let style = AnsiColor::from(convert_style(style)); self.styles.data.insert(Entity::Global, style); self.styles.data_is_set = true; } self.alignments.data = convert_alignment(style.alignment); } /// Converts a table to a String. /// /// It returns None in case where table cannot be fit to a terminal width. pub fn draw(self, config: TableConfig, termwidth: usize) -> Option { build_table(self.data, config, self.alignments, self.styles, termwidth) } /// Return a total table width. pub fn total_width(&self, config: &TableConfig) -> usize { let config = get_config(&config.theme, false, None); let widths = build_width(&self.data); get_total_width2(&widths, &config) } } impl From>>> for NuTable { fn from(value: Vec>>) -> Self { let data = VecRecords::new(value); let size = (data.count_rows(), data.count_columns()); Self { data, size, alignments: Alignments::default(), styles: Styles::default(), } } } #[derive(Debug, Clone)] pub struct TableConfig { theme: TableTheme, trim: TrimStrategy, split_color: Option