Refactroings

This commit is contained in:
Maxim Zhiburt 2024-12-17 23:47:10 +03:00
parent 25a5fca41d
commit 91130104f8
13 changed files with 301 additions and 273 deletions

View file

@ -18,8 +18,8 @@ use nu_protocol::{
ByteStream, Config, DataSource, ListStream, PipelineMetadata, Signals, TableMode, ValueIterator,
};
use nu_table::{
common::create_nu_table_config, CollapsedTable, ExpandedTable, JustTable, NuRecordsValue,
NuTable, StringResult, TableOpts, TableOutput,
common::configure_table, CollapsedTable, ExpandedTable, JustTable, NuRecordsValue, NuTable,
StringResult, TableOpts, TableOutput,
};
use nu_utils::{get_ls_colors, terminal_size};
@ -1070,13 +1070,13 @@ fn create_empty_placeholder(
let data = vec![vec![cell]];
let mut table = NuTable::from(data);
table.set_data_style(TextStyle::default().dimmed());
let out = TableOutput::from_table(table, false, false);
let mut out = TableOutput::from_table(table, false, false);
let style_computer = &StyleComputer::from_config(engine_state, stack);
let config = create_nu_table_config(&config, style_computer, &out, false, TableMode::default());
configure_table(&mut out, &config, style_computer, TableMode::default());
out.table
.draw(config, termwidth)
.draw(termwidth)
.expect("Could not create empty table placeholder")
}

View file

@ -1,6 +1,6 @@
use nu_ansi_term::{Color, Style};
use nu_color_config::TextStyle;
use nu_table::{NuTable, NuTableConfig, TableTheme};
use nu_table::{NuTable, TableTheme};
use tabled::grid::records::vec_records::Text;
fn main() {
@ -28,15 +28,11 @@ fn main() {
table.set_data_style(TextStyle::basic_left());
table.set_header_style(TextStyle::basic_center().style(Style::new().on(Color::Blue)));
let table_cfg = NuTableConfig {
theme: TableTheme::rounded(),
with_header: true,
..Default::default()
};
table.set_theme(TableTheme::rounded());
table.set_structure(false, true, false);
let output_table = table
.draw(table_cfg, width)
.draw(width)
.unwrap_or_else(|| format!("Couldn't fit table into {width} columns!"));
println!("{output_table}")

View file

@ -3,9 +3,7 @@ use nu_protocol::{Config, FooterMode, ShellError, Span, TableMode, TrimStrategy,
use terminal_size::{terminal_size, Height, Width};
use crate::{
clean_charset, colorize_space_str, string_wrap, NuTableConfig, TableOutput, TableTheme,
};
use crate::{clean_charset, colorize_space_str, string_wrap, TableOutput, TableTheme};
pub type NuText = (String, TextStyle);
pub type TableResult = Result<Option<TableOutput>, ShellError>;
@ -13,30 +11,31 @@ pub type StringResult = Result<Option<String>, ShellError>;
pub const INDEX_COLUMN_NAME: &str = "index";
pub fn create_nu_table_config(
pub fn configure_table(
out: &mut TableOutput,
config: &Config,
comp: &StyleComputer,
out: &TableOutput,
expand: bool,
mode: TableMode,
) -> NuTableConfig {
) {
let with_footer = is_footer_needed(config, out);
let theme = load_theme(mode);
out.table.set_theme(theme);
out.table
.set_structure(out.with_index, out.with_header, with_footer);
out.table.set_trim(config.table.trim.clone());
out.table
.set_border_header(config.table.header_on_separator);
out.table.set_border_color(lookup_separator_color(comp));
}
fn is_footer_needed(config: &Config, out: &TableOutput) -> bool {
let mut count_rows = out.table.count_rows();
if config.table.footer_inheritance {
count_rows = out.count_rows;
}
let with_footer = with_footer(config, out.with_header, count_rows);
NuTableConfig {
theme: load_theme(mode),
with_footer,
with_index: out.with_index,
with_header: out.with_header,
split_color: Some(lookup_separator_color(comp)),
trim: config.table.trim.clone(),
header_on_border: config.table.header_on_separator,
expand,
}
with_footer(config, out.with_header, count_rows)
}
pub fn nu_value_to_string_colored(val: &Value, cfg: &Config, comp: &StyleComputer) -> String {

View file

@ -10,7 +10,7 @@ pub mod common;
pub use common::{StringResult, TableResult};
pub use nu_color_config::TextStyle;
pub use table::{NuRecordsValue, NuTable, NuTableConfig};
pub use table::{NuRecordsValue, NuTable};
pub use table_theme::TableTheme;
pub use types::{CollapsedTable, ExpandedTable, JustTable, TableOpts, TableOutput};
pub use unstructured_table::UnstructuredTable;

View file

@ -2,7 +2,7 @@ use std::{cmp::min, collections::HashMap};
use nu_ansi_term::Style;
use nu_color_config::TextStyle;
use nu_protocol::TrimStrategy;
use nu_protocol::{TableIndent, TrimStrategy};
use nu_utils::strip_ansi_unlikely;
use tabled::{
@ -40,29 +40,15 @@ pub struct NuTable {
data: NuRecords,
styles: Styles,
alignments: Alignments,
indent: (usize, usize),
config: TableConfig,
}
#[derive(Debug, Default, Clone)]
struct TableConfig<Value> {
data: Value,
index: Value,
header: Value,
columns: HashMap<usize, Value>,
cells: HashMap<Position, Value>,
}
type Alignments = TableConfig<AlignmentHorizontal>;
type Styles = TableConfig<Color>;
impl NuTable {
/// Creates an empty [`NuTable`] instance.
pub fn new(count_rows: usize, count_columns: usize) -> Self {
Self {
data: VecRecords::new(vec![vec![Text::default(); count_columns]; count_rows]),
styles: Styles::default(),
indent: (1, 1),
alignments: Alignments {
data: AlignmentHorizontal::Left,
index: AlignmentHorizontal::Right,
@ -70,6 +56,15 @@ impl NuTable {
columns: HashMap::default(),
cells: HashMap::default(),
},
config: TableConfig {
theme: TableTheme::basic(),
trim: TrimStrategy::truncate(None),
structure: TableStructure::new(false, false, false),
indent: TableIndent::new(1, 1),
header_on_border: false,
expand: false,
border_color: None,
},
}
}
@ -151,8 +146,32 @@ impl NuTable {
self.alignments.data = convert_alignment(style.alignment);
}
pub fn set_indent(&mut self, left: usize, right: usize) {
self.indent = (left, right);
pub fn set_indent(&mut self, indent: TableIndent) {
self.config.indent = indent;
}
pub fn set_theme(&mut self, theme: TableTheme) {
self.config.theme = theme;
}
pub fn set_structure(&mut self, index: bool, header: bool, footer: bool) {
self.config.structure = TableStructure::new(index, header, footer);
}
pub fn set_border_header(&mut self, on: bool) {
self.config.header_on_border = on;
}
pub fn set_trim(&mut self, strategy: TrimStrategy) {
self.config.trim = strategy;
}
pub fn set_strategy(&mut self, expand: bool) {
self.config.expand = expand;
}
pub fn set_border_color(&mut self, color: Style) {
self.config.border_color = (!color.is_plain()).then_some(color);
}
pub fn get_records_mut(&mut self) -> &mut NuRecords {
@ -162,21 +181,15 @@ impl NuTable {
/// 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: NuTableConfig, termwidth: usize) -> Option<String> {
build_table(
self.data,
config,
self.alignments,
self.styles,
termwidth,
self.indent,
)
pub fn draw(self, termwidth: usize) -> Option<String> {
build_table(self, termwidth)
}
/// Return a total table width.
pub fn total_width(&self, config: &NuTableConfig) -> usize {
let config = get_config(&config.theme, false, None);
let widths = build_width(&self.data, self.indent.0 + self.indent.1);
pub fn total_width(&self) -> usize {
let config = create_config(&self.config.theme, false, None);
let pad = indent_sum(self.config.indent);
let widths = build_width(&self.data, pad);
get_total_width2(&widths, &config)
}
}
@ -190,33 +203,31 @@ impl From<Vec<Vec<Text<String>>>> for NuTable {
}
}
type Alignments = CellConfiguration<AlignmentHorizontal>;
type Styles = CellConfiguration<Color>;
#[derive(Debug, Default, Clone)]
struct CellConfiguration<Value> {
data: Value,
index: Value,
header: Value,
columns: HashMap<usize, Value>,
cells: HashMap<Position, Value>,
}
#[derive(Debug, Clone)]
pub struct NuTableConfig {
pub theme: TableTheme,
pub trim: TrimStrategy,
pub split_color: Option<Style>,
pub expand: bool,
pub with_index: bool,
pub with_header: bool,
pub with_footer: bool,
pub header_on_border: bool,
}
impl Default for NuTableConfig {
fn default() -> Self {
Self {
theme: TableTheme::basic(),
trim: TrimStrategy::truncate(None),
with_header: false,
with_index: false,
with_footer: false,
expand: false,
split_color: None,
header_on_border: false,
}
}
pub struct TableConfig {
theme: TableTheme,
trim: TrimStrategy,
border_color: Option<Style>,
expand: bool,
structure: TableStructure,
header_on_border: bool,
indent: TableIndent,
}
#[derive(Debug, Clone)]
struct TableStructure {
with_index: bool,
with_header: bool,
@ -233,64 +244,62 @@ impl TableStructure {
}
}
fn build_table(
mut data: NuRecords,
cfg: NuTableConfig,
alignments: Alignments,
styles: Styles,
termwidth: usize,
indent: (usize, usize),
) -> Option<String> {
if data.count_columns() == 0 || data.count_rows() == 0 {
fn build_table(mut t: NuTable, termwidth: usize) -> Option<String> {
if t.count_columns() == 0 || t.count_rows() == 0 {
return Some(String::new());
}
let pad = indent.0 + indent.1;
let widths = maybe_truncate_columns(&mut data, &cfg, termwidth, pad);
let widths = table_truncate(&mut t, termwidth)?;
table_insert_footer(&mut t);
draw_table(t, widths, termwidth)
}
fn table_insert_footer(t: &mut NuTable) {
if t.config.structure.with_header && t.config.structure.with_footer {
duplicate_row(&mut t.data, 0);
}
}
fn table_truncate(t: &mut NuTable, termwidth: usize) -> Option<Vec<usize>> {
let pad = t.config.indent.left + t.config.indent.right;
let widths = maybe_truncate_columns(&mut t.data, &t.config, termwidth, pad);
if widths.is_empty() {
return None;
}
if cfg.with_header && cfg.with_footer {
duplicate_row(&mut data, 0);
}
draw_table(data, alignments, styles, widths, cfg, termwidth, indent)
Some(widths)
}
fn draw_table(
data: NuRecords,
alignments: Alignments,
styles: Styles,
widths: Vec<usize>,
cfg: NuTableConfig,
termwidth: usize,
indent: (usize, usize),
) -> Option<String> {
let structure = get_table_structure(&data, &cfg);
let sep_color = cfg.split_color;
let border_header = structure.with_header && cfg.header_on_border;
fn draw_table(t: NuTable, widths: Vec<usize>, termwidth: usize) -> Option<String> {
let structure = get_table_structure(&t.data, &t.config);
let sep_color = t.config.border_color;
let border_header = structure.with_header && t.config.header_on_border;
let data: Vec<Vec<_>> = data.into();
// TODO: Optimize in tabled
let data: Vec<Vec<_>> = t.data.into();
let mut table = Builder::from(data).build();
set_indent(&mut table, indent.0, indent.1);
load_theme(&mut table, &cfg.theme, &structure, sep_color);
align_table(&mut table, alignments, &structure);
colorize_table(&mut table, styles, &structure);
set_indent(&mut table, t.config.indent);
load_theme(&mut table, &t.config.theme, &structure, sep_color);
align_table(&mut table, t.alignments, &structure);
colorize_table(&mut table, t.styles, &structure);
let pad = indent.0 + indent.1;
let width_ctrl = WidthCtrl::new(widths, cfg, termwidth, pad);
let pad = indent_sum(t.config.indent);
let width_ctrl = WidthCtrl::new(widths, t.config, termwidth, pad);
adjust_table(&mut table, width_ctrl, border_header, structure.with_footer);
table_to_string(table, termwidth)
}
fn get_table_structure(data: &VecRecords<Text<String>>, cfg: &NuTableConfig) -> TableStructure {
let with_index = cfg.with_index;
let with_header = cfg.with_header && data.count_rows() > 1;
let with_footer = with_header && cfg.with_footer;
fn indent_sum(indent: TableIndent) -> usize {
indent.left + indent.right
}
fn get_table_structure(data: &VecRecords<Text<String>>, cfg: &TableConfig) -> TableStructure {
let with_index = cfg.structure.with_index;
let with_header = cfg.structure.with_header && data.count_rows() > 1;
let with_footer = with_header && cfg.structure.with_footer;
TableStructure::new(with_index, with_header, with_footer)
}
@ -307,8 +316,8 @@ fn adjust_table(table: &mut Table, width_ctrl: WidthCtrl, border_header: bool, w
}
}
fn set_indent(table: &mut Table, left: usize, right: usize) {
table.with(Padding::new(left, right, 0, 0));
fn set_indent(table: &mut Table, indent: TableIndent) {
table.with(Padding::new(indent.left, indent.right, 0, 0));
}
fn set_border_head(table: &mut Table, wctrl: WidthCtrl) {
@ -377,13 +386,13 @@ fn table_to_string(table: Table, termwidth: usize) -> Option<String> {
struct WidthCtrl {
width: Vec<usize>,
cfg: NuTableConfig,
cfg: TableConfig,
width_max: usize,
pad: usize,
}
impl WidthCtrl {
fn new(width: Vec<usize>, cfg: NuTableConfig, max: usize, pad: usize) -> Self {
fn new(width: Vec<usize>, cfg: TableConfig, max: usize, pad: usize) -> Self {
Self {
width,
cfg,
@ -404,7 +413,7 @@ impl TableOption<NuRecords, ColoredConfig, CompleteDimensionVecRecords<'_>> for
let need_truncation = total_width > self.width_max;
if need_truncation {
let has_header = self.cfg.with_header && rec.count_rows() > 1;
let has_header = self.cfg.structure.with_header && rec.count_rows() > 1;
let as_head = has_header && self.cfg.header_on_border;
let trim = TableTrim::new(self.width, self.width_max, self.cfg.trim, as_head, self.pad);
@ -654,14 +663,14 @@ fn load_theme(
fn maybe_truncate_columns(
data: &mut NuRecords,
cfg: &NuTableConfig,
cfg: &TableConfig,
termwidth: usize,
pad: usize,
) -> Vec<usize> {
const TERMWIDTH_THRESHOLD: usize = 120;
let preserve_content = termwidth > TERMWIDTH_THRESHOLD;
let has_header = cfg.with_header && data.count_rows() > 1;
let has_header = cfg.structure.with_header && data.count_rows() > 1;
let is_header_on_border = has_header && cfg.header_on_border;
let truncate = if is_header_on_border {
@ -685,7 +694,7 @@ fn truncate_columns_by_content(
const MIN_ACCEPTABLE_WIDTH: usize = 3;
const TRAILING_COLUMN_WIDTH: usize = 5;
let config = get_config(theme, false, None);
let config = create_config(theme, false, None);
let mut widths = build_width(&*data, pad);
let total_width = get_total_width2(&widths, &config);
if total_width <= termwidth {
@ -765,7 +774,7 @@ fn truncate_columns_by_columns(
let acceptable_width = 10 + pad;
let trailing_column_width = 3 + pad;
let config = get_config(theme, false, None);
let config = create_config(theme, false, None);
let mut widths = build_width(&*data, pad);
let total_width = get_total_width2(&widths, &config);
if total_width <= termwidth {
@ -836,7 +845,7 @@ fn truncate_columns_by_head(
) -> Vec<usize> {
const TRAILING_COLUMN_WIDTH: usize = 5;
let config = get_config(theme, false, None);
let config = create_config(theme, false, None);
let mut widths = build_width(&*data, pad);
let total_width = get_total_width2(&widths, &config);
if total_width <= termwidth {
@ -912,7 +921,7 @@ fn get_total_width2(widths: &[usize], cfg: &ColoredConfig) -> usize {
total + countv + margin.left.size + margin.right.size
}
fn get_config(theme: &TableTheme, with_header: bool, color: Option<Style>) -> ColoredConfig {
fn create_config(theme: &TableTheme, with_header: bool, color: Option<Style>) -> ColoredConfig {
let structure = TableStructure::new(false, with_header, false);
let mut table = Table::new([[""]]);
load_theme(&mut table, theme, &structure, color);

View file

@ -8,9 +8,9 @@ use tabled::grid::config::Position;
use crate::{
common::{
check_value, create_nu_table_config, error_sign, get_header_style, get_index_style,
load_theme, nu_value_to_string, nu_value_to_string_clean, nu_value_to_string_colored,
wrap_text, NuText, StringResult, TableResult, INDEX_COLUMN_NAME,
check_value, configure_table, error_sign, get_header_style, get_index_style, load_theme,
nu_value_to_string, nu_value_to_string_clean, nu_value_to_string_colored, wrap_text,
NuText, StringResult, TableResult, INDEX_COLUMN_NAME,
},
string_width,
types::has_index,
@ -47,12 +47,19 @@ impl ExpandedTable {
pub fn build_list(self, vals: &[Value], opts: TableOpts<'_>) -> StringResult {
let cfg = Cfg { opts, format: self };
let output = expand_list(vals, cfg.clone())?;
let output = match output {
let mut output = match output {
Some(out) => out,
None => return Ok(None),
};
maybe_expand_table(output, cfg.opts.width, &cfg.opts)
configure_table(
&mut output,
cfg.opts.config,
&cfg.opts.style_computer,
cfg.opts.mode,
);
maybe_expand_table(output, cfg.opts.width)
}
}
@ -200,10 +207,7 @@ fn expand_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
}
let mut table = NuTable::from(data);
table.set_indent(
cfg.opts.config.table.padding.left,
cfg.opts.config.table.padding.right,
);
table.set_indent(cfg.opts.config.table.padding);
table.set_index_style(get_index_style(&cfg.opts.style_computer));
set_data_styles(&mut table, data_styles);
@ -363,10 +367,7 @@ fn expand_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
let mut table = NuTable::from(data);
table.set_index_style(get_index_style(&cfg.opts.style_computer));
table.set_header_style(get_header_style(&cfg.opts.style_computer));
table.set_indent(
cfg.opts.config.table.padding.left,
cfg.opts.config.table.padding.right,
);
table.set_indent(cfg.opts.config.table.padding);
set_data_styles(&mut table, data_styles);
Ok(Some(TableOutput::new(table, true, with_index, rows_count)))
@ -421,14 +422,11 @@ fn expanded_table_kv(record: &Record, cfg: Cfg<'_>) -> CellResult {
let mut table = NuTable::from(data);
table.set_index_style(get_key_style(&cfg));
table.set_indent(
cfg.opts.config.table.padding.left,
cfg.opts.config.table.padding.right,
);
table.set_indent(cfg.opts.config.table.padding);
let out = TableOutput::new(table, false, true, count_rows);
maybe_expand_table(out, cfg.opts.width, &cfg.opts)
maybe_expand_table(out, cfg.opts.width)
.map(|value| value.map(|value| CellOutput::clean(value, count_rows, false)))
}
@ -446,9 +444,9 @@ fn expand_value(value: &Value, width: usize, cfg: &Cfg<'_>) -> CellResult {
let table = expand_list(vals, inner_cfg)?;
match table {
Some(out) => {
let cfg = create_table_cfg(cfg, &out);
let value = out.table.draw(cfg, width);
Some(mut out) => {
table_apply_config(&mut out, cfg);
let value = out.table.draw(width);
match value {
Some(value) => Ok(Some(CellOutput::clean(value, out.count_rows, true))),
None => Ok(None),
@ -539,7 +537,7 @@ fn expand_entry(item: &Value, cfg: Cfg<'_>) -> CellOutput {
let inner_cfg = cfg_expand_next_level(cfg.clone(), span);
let table = expand_list(vals, inner_cfg);
let out = match table {
let mut out = match table {
Ok(Some(out)) => out,
_ => {
let value = nu_value_to_string(item, cfg.opts.config, &cfg.opts.style_computer);
@ -547,8 +545,9 @@ fn expand_entry(item: &Value, cfg: Cfg<'_>) -> CellOutput {
}
};
let table_config = create_table_cfg(&cfg, &out);
let table = out.table.draw(table_config, usize::MAX);
table_apply_config(&mut out, &cfg);
let table = out.table.draw(usize::MAX);
match table {
Some(table) => CellOutput::clean(table, out.count_rows, false),
None => {
@ -592,20 +591,18 @@ fn list_to_string(
buf
}
fn maybe_expand_table(out: TableOutput, term_width: usize, opts: &TableOpts<'_>) -> StringResult {
let mut table_config =
create_nu_table_config(opts.config, &opts.style_computer, &out, false, opts.mode);
let total_width = out.table.total_width(&table_config);
fn maybe_expand_table(mut out: TableOutput, term_width: usize) -> StringResult {
let total_width = out.table.total_width();
if total_width < term_width {
const EXPAND_THRESHOLD: f32 = 0.80;
let used_percent = total_width as f32 / term_width as f32;
let need_expansion = total_width < term_width && used_percent > EXPAND_THRESHOLD;
if need_expansion {
table_config.expand = true;
out.table.set_strategy(true);
}
}
let table = out.table.draw(table_config, term_width);
let table = out.table.draw(term_width);
Ok(table)
}
@ -616,12 +613,11 @@ fn set_data_styles(table: &mut NuTable, styles: HashMap<Position, TextStyle>) {
}
}
fn create_table_cfg(cfg: &Cfg<'_>, out: &TableOutput) -> crate::NuTableConfig {
create_nu_table_config(
fn table_apply_config(out: &mut TableOutput, cfg: &Cfg<'_>) {
configure_table(
out,
cfg.opts.config,
&cfg.opts.style_computer,
out,
false,
cfg.opts.mode,
)
}

View file

@ -5,7 +5,7 @@ use nu_protocol::{Config, Record, ShellError, Value};
use crate::{
clean_charset, colorize_space,
common::{
check_value, create_nu_table_config, get_empty_style, get_header_style, get_index_style,
check_value, configure_table, get_empty_style, get_header_style, get_index_style,
get_value_style, nu_value_to_string_colored, NuText, INDEX_COLUMN_NAME,
},
types::has_index,
@ -30,15 +30,12 @@ fn list_table(input: &[Value], opts: TableOpts<'_>) -> Result<Option<String>, Sh
None => return Ok(None),
};
out.table.set_indent(
opts.config.table.padding.left,
opts.config.table.padding.right,
);
out.table.set_indent(opts.config.table.padding);
colorize_space(out.table.get_records_mut(), &opts.style_computer);
let config = create_nu_table_config(opts.config, &opts.style_computer, &out, false, opts.mode);
let table = out.table.draw(config, opts.width);
configure_table(&mut out, opts.config, &opts.style_computer, opts.mode);
let table = out.table.draw(opts.width);
Ok(table)
}
@ -60,15 +57,11 @@ fn kv_table(record: &Record, opts: TableOpts<'_>) -> StringResult {
let mut table = NuTable::from(data);
table.set_index_style(TextStyle::default_field());
table.set_indent(
opts.config.table.padding.left,
opts.config.table.padding.right,
);
table.set_indent(opts.config.table.padding);
let out = TableOutput::from_table(table, false, true);
let table_config =
create_nu_table_config(opts.config, &opts.style_computer, &out, false, opts.mode);
let table = out.table.draw(table_config, opts.width);
let mut out = TableOutput::from_table(table, false, true);
configure_table(&mut out, opts.config, &opts.style_computer, opts.mode);
let table = out.table.draw(opts.width);
Ok(table)
}

View file

@ -12,8 +12,11 @@ pub use expanded::ExpandedTable;
pub use general::JustTable;
pub struct TableOutput {
/// A table structure.
pub table: NuTable,
/// A flag whether a header was present in the table.
pub with_header: bool,
/// A flag whether a index was present in the table.
pub with_index: bool,
/// The value may be bigger then table.count_rows(),
/// Specifically in case of expanded table we collect the whole structure size here.

View file

@ -69,9 +69,11 @@ pub fn clean_charset(text: &str) -> String {
continue;
}
if c < ' ' {
continue;
}
// note: Overall maybe we shall delete this check?
// it was made in order to cope with emojie issue.
// if c < ' ' && c != '\u{1b}' {
// continue;
// }
buf.push(c);
}

View file

@ -1,29 +1,79 @@
#![allow(dead_code)]
use nu_table::{string_width, NuTable, NuTableConfig};
use nu_protocol::TrimStrategy;
use nu_table::{string_width, NuTable, TableTheme};
use tabled::grid::records::vec_records::Text;
#[derive(Debug, Clone)]
pub struct TestCase {
cfg: NuTableConfig,
theme: TableTheme,
with_header: bool,
with_footer: bool,
with_index: bool,
expand: bool,
strategy: TrimStrategy,
termwidth: usize,
expected: Option<String>,
}
impl TestCase {
pub fn new(cfg: NuTableConfig, termwidth: usize, expected: Option<String>) -> Self {
pub fn new(termwidth: usize) -> Self {
Self {
cfg,
termwidth,
expected,
expected: None,
theme: TableTheme::basic(),
with_header: false,
with_footer: false,
with_index: false,
expand: false,
strategy: TrimStrategy::truncate(None),
}
}
pub fn expected(mut self, value: Option<String>) -> Self {
self.expected = value;
self
}
pub fn theme(mut self, theme: TableTheme) -> Self {
self.theme = theme;
self
}
pub fn expand(mut self) -> Self {
self.expand = true;
self
}
pub fn header(mut self) -> Self {
self.with_header = true;
self
}
pub fn footer(mut self) -> Self {
self.with_footer = true;
self
}
pub fn index(mut self) -> Self {
self.with_index = true;
self
}
pub fn trim(mut self, trim: TrimStrategy) -> Self {
self.strategy = trim;
self
}
}
type Data = Vec<Vec<Text<String>>>;
pub fn test_table<I: IntoIterator<Item = TestCase>>(data: Data, tests: I) {
pub fn test_table<I>(data: Data, tests: I)
where
I: IntoIterator<Item = TestCase>,
{
for (i, test) in tests.into_iter().enumerate() {
let actual = create_table(data.clone(), test.cfg.clone(), test.termwidth);
let actual = create_table(data.clone(), test.clone());
assert_eq!(
actual, test.expected,
@ -37,9 +87,14 @@ pub fn test_table<I: IntoIterator<Item = TestCase>>(data: Data, tests: I) {
}
}
pub fn create_table(data: Data, config: NuTableConfig, termwidth: usize) -> Option<String> {
let table = NuTable::from(data);
table.draw(config, termwidth)
pub fn create_table(data: Data, case: TestCase) -> Option<String> {
let mut table = NuTable::from(data);
table.set_theme(case.theme);
table.set_structure(case.with_index, case.with_header, case.with_footer);
table.set_trim(case.strategy);
table.set_strategy(case.expand);
table.draw(case.termwidth)
}
pub fn create_row(count_columns: usize) -> Vec<Text<String>> {

View file

@ -1,20 +1,19 @@
mod common;
use common::{create_row, test_table, TestCase};
use nu_protocol::TrimStrategy;
use nu_table::{NuTable, NuTableConfig, TableTheme as theme};
use nu_table::{NuTable, TableTheme as theme};
use common::{create_row, test_table, TestCase};
use tabled::grid::records::vec_records::Text;
#[test]
fn data_and_header_has_different_size_doesnt_work() {
let table = NuTable::from(vec![create_row(5), create_row(5), create_row(5)]);
let cfg = NuTableConfig {
theme: theme::heavy(),
with_header: true,
..Default::default()
};
let mut table = NuTable::from(vec![create_row(5), create_row(5), create_row(5)]);
table.set_theme(theme::heavy());
table.set_structure(false, true, false);
let table = table.draw(cfg.clone(), usize::MAX);
let table = table.draw(usize::MAX);
assert_eq!(
table.as_deref(),
@ -30,7 +29,7 @@ fn data_and_header_has_different_size_doesnt_work() {
let table = NuTable::from(vec![create_row(5), create_row(5), create_row(5)]);
let table = table.draw(cfg, usize::MAX);
let table = table.draw(usize::MAX);
assert_eq!(
table.as_deref(),
@ -47,30 +46,27 @@ fn data_and_header_has_different_size_doesnt_work() {
#[test]
fn termwidth_too_small() {
let test_loop = |config: NuTableConfig| {
for i in 0..10 {
let table = NuTable::from(vec![create_row(5), create_row(5), create_row(5)]);
let table = table.draw(config.clone(), i);
assert!(table.is_none());
}
};
let mut cfg = NuTableConfig {
theme: theme::heavy(),
with_header: true,
..Default::default()
};
for case in [
let tests = [
TrimStrategy::truncate(None),
TrimStrategy::truncate(Some(String::from("**"))),
TrimStrategy::truncate(Some(String::from(""))),
TrimStrategy::wrap(false),
TrimStrategy::wrap(true),
] {
cfg.trim = case;
test_loop(cfg.clone());
];
let data = vec![create_row(5), create_row(5), create_row(5)];
for case in tests {
for i in 0..10 {
let mut table = NuTable::from(data.clone());
table.set_theme(theme::heavy());
table.set_structure(false, true, false);
table.set_trim(case.clone());
let table = table.draw(i);
assert!(table.is_none());
}
}
}
@ -195,30 +191,24 @@ fn width_control_test_0() {
}
fn test_width(data: Vec<Vec<Text<String>>>, tests: &[(usize, &str)]) {
let config = NuTableConfig {
theme: theme::heavy(),
trim: TrimStrategy::truncate(Some(String::from("..."))),
with_header: true,
..Default::default()
};
let tests = tests.iter().map(|&(termwidth, expected)| {
TestCase::new(config.clone(), termwidth, Some(expected.to_owned()))
TestCase::new(termwidth)
.theme(theme::heavy())
.trim(TrimStrategy::truncate(Some(String::from("..."))))
.header()
.expected(Some(expected.to_owned()))
});
test_table(data, tests);
}
fn test_trim(tests: &[(usize, Option<&str>)], trim: TrimStrategy) {
let config = NuTableConfig {
theme: theme::heavy(),
with_header: true,
trim,
..Default::default()
};
let tests = tests.iter().map(|&(termwidth, expected)| {
TestCase::new(config.clone(), termwidth, expected.map(|s| s.to_string()))
TestCase::new(termwidth)
.theme(theme::heavy())
.trim(trim.clone())
.header()
.expected(expected.map(|s| s.to_string()))
});
let data = vec![

View file

@ -1,20 +1,14 @@
mod common;
use common::{create_row, create_table};
use common::{create_row, create_table, TestCase};
use nu_table::{NuTableConfig, TableTheme as theme};
use nu_table::TableTheme as theme;
#[test]
fn test_expand() {
let table = create_table(
vec![create_row(4); 3],
NuTableConfig {
theme: theme::rounded(),
with_header: true,
expand: true,
..Default::default()
},
50,
TestCase::new(50).theme(theme::rounded()).header().expand(),
);
assert_eq!(

View file

@ -1,7 +1,7 @@
mod common;
use common::create_row as row;
use nu_table::{NuTable, NuTableConfig, TableTheme as theme};
use common::{create_row as row, TestCase};
use nu_table::{NuTable, TableTheme as theme};
use tabled::grid::records::vec_records::Text;
#[test]
@ -452,27 +452,18 @@ fn test_with_love() {
}
fn create_table(data: Vec<Vec<Text<String>>>, with_header: bool, theme: theme) -> String {
let config = NuTableConfig {
theme,
with_header,
..Default::default()
};
let mut case = TestCase::new(usize::MAX).theme(theme);
if with_header {
case = case.header();
}
let out = common::create_table(data, config, usize::MAX);
out.expect("not expected to get None")
common::create_table(data, case).expect("not expected to get None")
}
fn create_table_with_size(data: Vec<Vec<Text<String>>>, with_header: bool, theme: theme) -> String {
let config = NuTableConfig {
theme,
with_header,
..Default::default()
};
let mut table = NuTable::from(data);
table.set_theme(theme);
table.set_structure(false, with_header, false);
let table = NuTable::from(data);
table
.draw(config, usize::MAX)
.expect("not expected to get None")
table.draw(usize::MAX).expect("not expected to get None")
}