From 02e1eec81795b34efe90e9b87952827efb60722b Mon Sep 17 00:00:00 2001 From: Maxim Zhiburt Date: Tue, 19 Dec 2023 23:15:30 +0300 Subject: [PATCH 1/4] Update documentation --- README.md | 1404 ++++++++++-------- papergrid/src/config/entity.rs | 114 ++ tabled/src/lib.rs | 2 + tabled/src/settings/color/mod.rs | 5 + tabled/src/settings/concat/mod.rs | 3 + tabled/src/settings/height/mod.rs | 8 +- tabled/src/settings/panel/vertical_panel.rs | 69 + tabled/src/settings/split/mod.rs | 121 +- tabled/src/settings/style/builder.rs | 14 + tabled/src/settings/style/horizontal_line.rs | 2 +- tabled/src/settings/themes/theme.rs | 6 + tabled/src/settings/width/truncate.rs | 3 +- tabled/src/settings/width/util.rs | 283 ---- tabled/src/settings/width/wrap.rs | 8 +- tabled/src/tables/extended.rs | 42 +- tabled/src/util/mod.rs | 2 + tabled/src/util/string.rs | 320 ++++ tabled/tests/settings/panel_test.rs | 24 + 18 files changed, 1487 insertions(+), 943 deletions(-) create mode 100644 tabled/src/util/mod.rs create mode 100644 tabled/src/util/string.rs diff --git a/README.md b/README.md index 22fdd96f..6a291732 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,6 @@ you can find more examples in an **[examples](/tabled/examples/)** folder. ## Table of Contents - [Usage](#usage) - - [Dynamic table](#dynamic-table) - - [Build index](#build-index) - [Settings](#settings) - [Style](#style) - [Styles](#styles) @@ -40,19 +38,18 @@ you can find more examples in an **[examples](/tabled/examples/)** folder. - [ascii\_rounded](#ascii_rounded) - [blank](#blank) - [empty](#empty) - - [Customization](#customization) + - [Customization](#style-customization) - [Cell Border](#cell-border) - [Text on borders](#text-on-borders) - [Colorize borders](#colorize-borders) - - [Theme](#theme) - - [Colorize content](#colorize-content) - - [Column names](#colorize-content) + - [Theme](#theme) + - [Colorize content](#colorize-content) + - [Column names](#colorize-content) - [Alignment](#alignment) - [Format](#format) - [Padding](#padding) - - [Padding Color](#padding-color) - [Margin](#margin) - - [Margin Color](#margin-color) + - [Color](#margin-color) - [Shadow](#shadow) - [Width](#width) - [Truncate](#truncate) @@ -67,7 +64,6 @@ you can find more examples in an **[examples](/tabled/examples/)** folder. - [Rotate](#rotate) - [Disable](#disable) - [Extract](#extract) - - [Refinishing](#refinishing) - [Header and Footer and Panel](#header-and-footer-and-panel) - [Merge](#merge) - [Concat](#concat) @@ -76,9 +72,6 @@ you can find more examples in an **[examples](/tabled/examples/)** folder. - [Horizontal span](#horizontal-span) - [Vertical span](#vertical-span) - [Split](#split) - - [Directions](#directions) - - [Behaviors](#behaviors) - - [Displays](#displays) - [Duplicate](#duplicate) - [Derive](#derive) - [Override a column name](#override-a-column-name) @@ -87,19 +80,21 @@ you can find more examples in an **[examples](/tabled/examples/)** folder. - [Format fields](#format-fields) - [Format headers](#format-headers) - [Inline](#inline) -- [Features](#features) - - [Color](#color) - - [Tuple combination](#tuple-combination) - - [Object](#object) - - [Macros](#macros) - - [`col` and `row`](#col-and-row) - - [`static_table`](#static_table) - [Table types](#table-types) - [`Table`](#table) - [`IterTable`](#itertable) - [`CompactTable`](#compacttable) - [`PoolTable`](#pooltable) - [`ExpandedDisplay`](#expanded-display) +- [Tips & Tricks](#tips-&-tricks) + - [`std::fmt::*` options](#std::fmt::*-options) + - [Tuple combination](#tuple-combination) + - [Object](#object) + - [Builder](#builder) + - [Macros](#macros) + - [`col` and `row`](#col-and-row) + - [`static_table`](#static_table) +- [Features](#features) - [Formats](#formats) - [`json` format](#json-format) - [`ron` format](#ron-format) @@ -154,34 +149,21 @@ let expected = "+------+----------------+---------------+\n\ +------+----------------+---------------+\n\ | C | Dennis Ritchie | 1972 |\n\ +------+----------------+---------------+\n\ - | Rust | Graydon Hoare | 2010 |\n\ - +------+----------------+---------------+\n\ | Go | Rob Pike | 2009 |\n\ + +------+----------------+---------------+\n\ + | Rust | Graydon Hoare | 2010 |\n\ +------+----------------+---------------+"; assert_eq!(table, expected); ``` -You can also use some of the formatting(`std::fmt::*`) options. - -```rust -use tabled::Table; - -let numbers = [1, 2, 3]; -let table = Table::new(numbers); - -println!("{:#^10}", table); -``` - -### Dynamic table - Sometimes you can't say what type of data you are going to deal with (like parsing `csv`). In such cases it may be handy to build table dynamically (step by step). ```rust use tabled::{builder::Builder, settings::Style}; -let song = r#" +let lyrics = r#" And the cat's in the cradle and the silver spoon Little boy blue and the man on the moon When you comin' home dad? @@ -190,12 +172,13 @@ let song = r#" "#; let mut builder = Builder::default(); -for line in song.lines() { +for line in lyrics.lines() { + let line = line.trim(); if line.is_empty() { continue; } - let words: Vec<_> = line.trim().split_terminator(' ').collect(); + let words: Vec<_> = line.split_terminator(' ').collect(); builder.push_record(words); } @@ -205,81 +188,24 @@ builder.insert_record(0, columns); let mut table = builder.build(); table.with(Style::ascii_rounded()); -println!("{}", table); -``` - -```text -.------------------------------------------------------------------------------------. -| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -| And | the | cat's | in | the | cradle | and | the | silver | spoon | -| Little | boy | blue | and | the | man | on | the | moon | | -| When | you | comin' | home | dad? | | | | | | -| I | don't | know | when, | but | we'll | get | together | then | son | -| You | know | we'll | have | a | good | time | then | | | -'------------------------------------------------------------------------------------' -``` - -#### Build index - -You can change a table layout by `Builder`. - -```rust -// previous example -// ... - -let mut builder = builder.index(); -builder.transpose(); -``` - -```text -.-------------------------------------------------. -| | 0 | 1 | 2 | 3 | 4 | -| 0 | And | Little | When | I | You | -| 1 | the | boy | you | don't | know | -| 2 | cat's | blue | comin' | know | we'll | -| 3 | in | and | home | when, | have | -| 4 | the | the | dad? | but | a | -| 5 | cradle | man | | we'll | good | -| 6 | and | on | | get | time | -| 7 | the | the | | together | then | -| 8 | silver | moon | | then | | -| 9 | spoon | | | son | | -'-------------------------------------------------' -``` - -You can use `Builder::index` to make a particular column an index, which will stay on the left. - -```rust -use tabled::{builder::Builder, settings::Style}; - -let mut builder = Builder::default(); -builder.push_record(["Index", "Language", "Status"]); -builder.push_record(["1", "English", "In progress"]); -builder.push_record(["2", "Deutsch", "Not ready"]); - -let builder = builder.index().column(1).name(None); - -let mut table = builder.build(); -table.with(Style::rounded()); - -println!("{}", table); -``` +let expected = concat!( + ".------------------------------------------------------------------------------------.\n", + "| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |\n", + "| And | the | cat's | in | the | cradle | and | the | silver | spoon |\n", + "| Little | boy | blue | and | the | man | on | the | moon | |\n", + "| When | you | comin' | home | dad? | | | | | |\n", + "| I | don't | know | when, | but | we'll | get | together | then | son |\n", + "| You | know | we'll | have | a | good | time | then | | |\n", + "'------------------------------------------------------------------------------------'", +); -```text -╭─────────┬───────┬─────────────╮ -│ │ Index │ Status │ -├─────────┼───────┼─────────────┤ -│ English │ 1 │ In progress │ -│ Deutsch │ 2 │ Not ready │ -╰─────────┴───────┴─────────────╯ +assert_eq!(table, expected); ``` ## Settings This section lists the set of settings you can apply to your table. -Most of the settings are used by `.with` method of `Table`. - -You can find a list of show cases in **[examples folder](/tabled/examples/README.md)**. +Most of the settings are laveraged by `Table::with` and `Table::modify`. ### Style @@ -442,53 +368,61 @@ Rust Graydon Hoare 2010 Go Rob Pike 2009 ``` -#### Customization +#### Style customization You can modify existing styles to fit your needs. +Notice that all modifications are done at compile time. -```rust -let style = tabled::settings::Style::modern().remove_horizontal(); -``` - -The style will look like the following. - -```rust -┌──────┬────────────────┬───────────────┐ -│ name │ designed_by │ invented_year │ -│ C │ Dennis Ritchie │ 1972 │ -│ Rust │ Graydon Hoare │ 2010 │ -│ Go │ Rob Pike │ 2009 │ -└──────┴────────────────┴───────────────┘ -``` - -You can change the existing styles. +Check the [documentation](https://docs.rs/tabled/latest/tabled/settings/struct.Style.html) for +more customization options. ```rust -use tabled::settings::style::{HorizontalLine, Style, VerticalLine}; +use tabled::settings::{Style, HorizontalLine, VerticalLine}; let style = Style::modern() - .remove_horizontals() - .remove_verticals() - .horizontals([HorizontalLine::new(1, Style::modern().get_horizontal()) - .main(Some('═')) - .intersection(None)]) - .verticals([VerticalLine::new(1, Style::modern().get_vertical())]); + .horizontals([(1, HorizontalLine::inherit(Style::modern()).horizontal('═'))]) + .verticals([(1, VerticalLine::inherit(Style::modern()))]) + .remove_horizontal() + .remove_vertical(); ``` -The style will look like the following. +The style will look like the following for the first example. ```rust ┌──────┬───────────────────────────────┐ │ name │ designed_by invented_year │ ├══════┼═══════════════════════════════┤ │ C │ Dennis Ritchie 1972 │ -│ Rust │ Graydon Hoare 2010 │ │ Go │ Rob Pike 2009 │ +│ Rust │ Graydon Hoare 2010 │ └──────┴───────────────────────────────┘ ``` -Check the [documentation](https://docs.rs/tabled/latest/tabled/style/struct.Style.html) for -more customization options. +Also you can build your own one from scratch, and doing so not always possible at compile time, +so you may use `Theme` object to do that. + +Notice that the `Theme` is quite powerfull on itself, you can check it in the [documentation](https://docs.rs/tabled/latest/tabled/settings/struct.Theme.html). + +```rust +use tabled::grid::config::{Border, HorizontalLine}; +use tabled::settings::Theme; + +let mut style = Theme::default(); +style.set_lines_horizontal(HashMap::from_iter([(1, HorizontalLine::full('-', '-', '+', '+'))])); +style.set_border_frame(Border::filled('+')); +``` + +The style will look like the following. + +```rust ++++++++++++++++++++++++++++++++++++++++ ++ name designed_by invented_year + ++-------------------------------------+ ++ C Dennis Ritchie 1972 + ++ Go Rob Pike 2009 + ++ Rust Graydon Hoare 2010 + ++++++++++++++++++++++++++++++++++++++++ +``` #### Cell Border @@ -498,24 +432,25 @@ Sometimes it's nesessary to change a border of a particular cell. For this purpose you can use `Border`. ```rust -use tabled::{settings::{object::Rows, Border, Modify, Style}, Table}; +use tabled::{settings::{object::Rows, Border, Style}, Table}; let data = [["123", "456"], ["789", "000"]]; - + let table = Table::new(data) .with(Style::ascii()) - .with(Modify::new(Rows::first()).with(Border::default().top('x'))) + .modify(Rows::first(), Border::new().set_top('x')) .to_string(); - -let expected = "+xxxxx+xxxxx+\n\ - | 0 | 1 |\n\ - +-----+-----+\n\ - | 123 | 456 |\n\ - +-----+-----+\n\ - | 789 | 000 |\n\ - +-----+-----+"; - -assert_eq!(table, expected); + +assert_eq!( + table, + "+xxxxx+xxxxx+\n\ + | 0 | 1 |\n\ + +-----+-----+\n\ + | 123 | 456 |\n\ + +-----+-----+\n\ + | 789 | 000 |\n\ + +-----+-----+" +); ``` #### Text on borders @@ -523,10 +458,11 @@ assert_eq!(table, expected); You can set a string to a horizontal border line. ```rust -use tabled::{settings::style::BorderText, Table}; +use tabled::{settings::style::LineText, Table}; +use tabled::settings::object::Rows; let mut table = Table::new(["Hello World"]); -table.with(BorderText::new("+-.table").horizontal(0)); +table.with(LineText::new("+-.table", Rows::first())); assert_eq!( table.to_string(), @@ -541,13 +477,13 @@ assert_eq!( Sometimes though it's not convenient to set a string. But rather necessary to set a custom char. -You can use `BorderChar` to achieve this. +You can use `LineChar` to achieve this. ```rust use tabled::{ settings::{ object::Columns, - style::{BorderChar, Offset}, + style::{LineChar, Offset}, Modify, Style, }, Table, @@ -557,8 +493,8 @@ let table = Table::new([["Hello", "World", "!"]]) .with(Style::markdown()) .with( Modify::new(Columns::new(..)) - .with(BorderChar::horizontal(':', Offset::Begin(0))) - .with(BorderChar::horizontal(':', Offset::End(0))), + .with(LineChar::horizontal(':', Offset::Begin(0))) + .with(LineChar::horizontal(':', Offset::End(0))), ) .to_string(); @@ -575,57 +511,97 @@ assert_eq!( You can set a colors of all borders using `Color`. ```rust -use tabled::settings::{Color, style::BorderColor}; +use tabled::settings::{style::BorderColor, Color}; -table.with(BorderColor::default().top(Color::FG_GREEN))) +table.with(BorderColor::new().set_top(Color::FG_GREEN)); ``` You can also set a color border of intividial cell by using `BorderColored`. ```rust -use tabled::settings::{Modify, style::BorderColor, Color, object::Columns}; +use tabled::settings::{object::Columns, style::BorderColor, Color}; + +table.modify(Columns::single(2), BorderColor::new().set_top(Color::FG_GREEN)); +``` + +#### Theme + +It was considered that having a few atomic settings is better rather then have one but feature full. + +But `tabled::settings::themes::*` are a bit diverge from this idea. +It contains a list of settings which do bigger changes to the table. + +The first one is `Theme` itself. +You can change layout, set style, colorize, config borders and even revers table with it. + +```rust +use tabled::settings::{ + object::{Columns, Object}, + Alignment, Style, Theme, +}; -table.with(Modify::new(Columns::single(2)).with(BorderColor::default().top(Color::FG_GREEN))) +let mut style = Theme::from_style(Style::ascii_rounded()); +style.remove_border_horizontal(); +style.remove_border_vertical(); +style.align_columns(Alignment::left()); +style.set_footer(true); + +table.with(style); +table.modify(Columns::new(1..).not(Columns::last()), Alignment::center()); +table.modify(Columns::last(), Alignment::right()); ``` -### Theme +You'll must see the following output when run against the first example. + +```text +.---------------------------------------------------------------------------. +| name C Go Rust name | +| designed_by Dennis Ritchie Rob Pike Graydon Hoare designed_by | +| invented_year 1972 2009 2010 invented_year | +'---------------------------------------------------------------------------' +``` -#### Colorize content +##### Colorize content You can colorize the content by the pattern or a specific cell. ```rust +use std::iter::FromIterator; + use tabled::{ builder::Builder, - settings::{object::Rows, style::Style, themes::Colorization, Color, Modify}, + settings::{object::Rows, style::Style, themes::Colorization, Color}, }; let data = vec![ - vec![String::from("header 0"), String::from("header 1")], - vec![String::from("Hello"), String::from("World")], - vec![String::from("Bonjour"), String::from("le monde")], - vec![String::from("Hallo"), String::from("Welt")], + vec!["Word", "Translation", "Lang"], + vec!["World", "le monde", "FR"], + vec!["World", "Welt", "DE"], ]; -let color1 = Color::BG_WHITE | Color::FG_BLACK; -let color2 = Color::BG_GREEN | Color::FG_BLACK; -let color3 = Color::BG_MAGENTA | Color::FG_BLACK; -let color4 = Color::BG_BLUE | Color::FG_BLACK; +let color_col1 = Color::BG_GREEN | Color::FG_BLACK; +let color_col2 = Color::BG_MAGENTA | Color::FG_BLACK; +let color_col3 = Color::BG_YELLOW | Color::FG_BLACK; +let color_head = Color::BG_WHITE | Color::FG_BLACK; +let color_head_text = Color::BG_BLUE | Color::FG_BLACK; -let mut table = Builder::from(data).build(); +let mut table = Builder::from_iter(data).build(); table .with(Style::empty()) - .with(Colorization::columns([color2, color3])) - .with(Colorization::exact([color1], Rows::first())) - .with(Modify::new(Rows::first()).with(color4)); + .with(Colorization::columns([color_col1, color_col2, color_col3])) + .with(Colorization::exact([color_head], Rows::first())) + .modify(Rows::first(), color_head_text); + +println!("{table}"); ``` - - Preview + + Preview + -#### Column names +##### Column names You can move the header right to the borders. @@ -644,6 +620,8 @@ let data = vec![ let mut table = Builder::from(data).build(); table.with(Style::modern()).with(ColumnNames::default()); + +println!("{table}"); ``` ```text @@ -662,13 +640,31 @@ You can set a horizontal and vertical alignment for any `Object` (e.g `Columns`, ```rust use tabled::{ - settings::{Modify, Alignment, object::Segment} + settings::{object::Segment, Alignment, Settings}, Table, }; +let data = [("Text", "Multiline\ntext"), ("text", "text")]; let mut table = Table::new(data); -table - .with(Modify::new(Segment::all()).with(Alignment::left()).with(Alignment::top())); +table.modify( + Segment::all(), + Settings::new(Alignment::right(), Alignment::bottom()), +); + +println!("{table}"); +``` + +The output would be. + +```text ++------+-----------+ +| &str | &str | ++------+-----------+ +| | Multiline | +| Text | text | ++------+-----------+ +| text | text | ++------+-----------+ ``` ### Format @@ -677,95 +673,130 @@ The `Format` function provides an interface for a modification of cells. ```rust use tabled::{ + settings::{format::Format, object::Rows}, Table, - settings::{Modify, format::Format, object::{Rows, Columns}}, }; -let mut table = Table::new(&data); -table - .with(Modify::new(Rows::first()).with(Format::new(|s| format!("Head {}", s)))) - .with(Modify::new(Columns::new(1..=2)).with(Format::new(|s| format!("<< {} >>", s)))); +let data = vec![[0; 10]; 9]; +let mut table = Table::new(data); +table.modify( + Rows::new(..), + Format::positioned(|_, (row, col)| ((row + 1) * (col + 1)).to_string()), +); + +println!("{table}"); +``` + +The result you must get will be. + +```text ++----+----+----+----+----+----+----+----+----+-----+ +| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ++----+----+----+----+----+----+----+----+----+-----+ +| 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | ++----+----+----+----+----+----+----+----+----+-----+ +| 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | ++----+----+----+----+----+----+----+----+----+-----+ +| 4 | 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 | 40 | ++----+----+----+----+----+----+----+----+----+-----+ +| 5 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | ++----+----+----+----+----+----+----+----+----+-----+ +| 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48 | 54 | 60 | ++----+----+----+----+----+----+----+----+----+-----+ +| 7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63 | 70 | ++----+----+----+----+----+----+----+----+----+-----+ +| 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | ++----+----+----+----+----+----+----+----+----+-----+ +| 9 | 18 | 27 | 36 | 45 | 54 | 63 | 72 | 81 | 90 | ++----+----+----+----+----+----+----+----+----+-----+ +| 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100 | ++----+----+----+----+----+----+----+----+----+-----+ ``` ### Padding The `Padding` structure provides an interface for a left, right, top and bottom padding of cells. +You can set indent size, and color of the padding. ```rust -use tabled::{ - Table, - settings::{Modify, Padding, object::Cell} +use tabled::settings::{ + object::{Columns, Object, Rows}, + Color, Padding, }; -let mut table = Table::new(&data); -table.with(Modify::new(Cell(0, 3)).with(Padding::new(1, 1, 0, 2))); - -// It's possible to set a fill char for padding. -let mut table = Table::new(&data) -table.with(Modify::new(Cell(0, 3)).with(Padding::new(1, 1, 0, 2).fill('>', '<', '^', 'V'))); -``` - -#### Padding Color +// Set a padding size for first column +table.modify(Columns::first().not((0, 0)), Padding::new(0, 10, 0, 0)); -You can set a color for padding characters. +// Set a padding for a last column (except first row) +table.modify(Columns::last().not(Rows::first()), Padding::new(1, 1, 0, 0).fill('[', ']', ' ', ' ')); -BE AWARE: It only works with `color` feature. - -```rust -use tabled::{ - settings::{Color, Padding}, - Table, -}; +// Set a padding for a first row +table.modify( + Rows::first(), + Padding::new(2, 2, 0, 2).fill(' ', ' ', ' ', ' ').colorize( + Color::BG_BLUE, + Color::BG_BLUE, + Color::BG_BLUE, + Color::BG_BLUE, + ), +); +``` -let mut table = Table::new(&data); +Applying the last change to the first example will result in the following. -table.with(Padding::new(1, 1, 0, 2).colorize( - Color::FG_BLUE, - Color::FG_BLUE, - Color::FG_BLUE, - Color::FG_BLUE, -)); -``` + + + Preview + ### Margin -`Margin` sets extra space around the border (top, bottom, left, right). +`Margin` sets extra space around the table (top, bottom, left, right). +As for `Padding` you can set indent, size and color of the extra space. ```rust -use tabled::{Table, settings::Margin}; +use tabled::settings::Margin; -let mut table = Table::new(&data); table.with(Margin::new(3, 4, 1, 2).fill('>', '<', 'v', '^')); ``` -An output would depend on the `data`. But it could look like the following. +If you run it for a first example you'll get. ```text -vvvvvvvvvvvvvvvvvvvvvvvvvvvvv ->>>┌─────────┬──────────┐<<<< ->>>│ feature │ released │<<<< ->>>│ margin │ 0.6.0 │<<<< ->>>└─────────┴──────────┘<<<< -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +>>>+------+----------------+---------------+<<<< +>>>| name | designed_by | invented_year |<<<< +>>>+------+----------------+---------------+<<<< +>>>| C | Dennis Ritchie | 1972 |<<<< +>>>+------+----------------+---------------+<<<< +>>>| Go | Rob Pike | 2009 |<<<< +>>>+------+----------------+---------------+<<<< +>>>| Rust | Graydon Hoare | 2010 |<<<< +>>>+------+----------------+---------------+<<<< +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` #### Margin Color -You can set a color for padding characters. +You can set a color for the characters. ```rust -use tabled::{ - settings::{Color, Margin}, - Table, -}; +use tabled::settings::{Margin, Color}; -let mut table = Table::new(data); -table - .with(Margin::new(3, 4, 1, 2)) - .with(Margin::new(1, 1, 1, 1).colorize( Color::BG_RED, Color::BG_RED, Color::BG_RED, Color::BG_RED)); +table.with(Margin::new(3, 4, 1, 2).fill('>', '<', 'v', '^').colorize( + Color::BG_BRIGHT_BLUE, + Color::BG_BRIGHT_CYAN, + Color::BG_BLUE, + Color::BG_RED, +)); ``` + + + Preview + + ### Shadow `Shadow` can be used to set a 'shadow' like margin. @@ -796,34 +827,79 @@ An output could look like the following. ### Width Using the following structures you can configure a width of a table and a single cell. -But be aware that it doesn't often consider `Padding`. -The functions preserves the text color. +But be aware that it often DOES NOT consider `Padding` when adjusting the width. + +The functions preserves `ansi` color sequences (when `ansi` feature is on). + +Beware that we `Width` controls only content, so it can't make things smaller then a certain minimum. + +Bellow is an example of setting an exact table width. + +```rust +use tabled::{ + settings::{ + peaker::{PriorityMax, PriorityMin}, + Settings, Width, + }, + Table, +}; + +fn generate_table(string_size: usize, width: usize) -> String { + let data = vec![(string_size.to_string(), "x".repeat(string_size))]; + + let mut table = Table::new(data); + table.with(Settings::new( + Width::wrap(width).priority::(), + Width::increase(width).priority::(), + )); + + table.to_string() +} + +let table = generate_table(50, 40); +println!("{table}"); + +let table = generate_table(20, 40); +println!("{table}"); +``` + +The result must be seen as following. + +```text ++--------+-----------------------------+ +| String | String | ++--------+-----------------------------+ +| 50 | xxxxxxxxxxxxxxxxxxxxxxxxxxx | +| | xxxxxxxxxxxxxxxxxxxxxxx | ++--------+-----------------------------+ ++---------------+----------------------+ +| String | String | ++---------------+----------------------+ +| 20 | xxxxxxxxxxxxxxxxxxxx | ++---------------+----------------------+ +``` #### Truncate `Truncate` sets a maximum width of a cell by truncating its content. ```rust -use tabled::{Table, settings::{Modify, Width, object::Rows}}; - -let mut table = Table::new(data); +use tabled::settings::{Width, object::Rows}; // Truncating content to 10 chars in case it's bigger than that // in a first row. -table.with(Modify::new(Rows::first()).with(Width::truncate(10))); +table.modify(Rows::first(), Width::truncate(10)); // Truncating content to 7 chars and puts a suffix '...' after it // in all rows except a first. -table.with(Modify::new(Rows::new(1..)).with(Width::truncate(10).suffix("..."))); +table.modify(Rows::new(1..), Width::truncate(10).suffix("...")); ``` -`Truncate` also can be used to set a maximum width of a whole table. +`Truncate` can be used to set a maximum width of a whole table. ```rust -use tabled::{Table, settings::Width}; - -let mut table = Table::new(data); +use tabled::settings::Width; // Tries to set table width to 22, in case it's bigger than that. table.with(Width::truncate(22)); @@ -836,24 +912,20 @@ It can be used in combination with `MinWidth` to set an exact table size. `Wrap` sets a maximum width of a cell by wrapping its content to new lines. ```rust -use tabled::{Table, settings::{Modify, Width, object::Rows}}; - -let mut table = Table::new(data); +use tabled::settings::{Width, object::Rows}; // Wrap content to 10 chars in case it's bigger than that // in a first row. -table.with(Modify::new(Rows::first()).with(Width::wrap(10))); +table.modify(Rows::first().with(Width::wrap(10))); // Use a strategy where we try to keep words not splited (where possible). -table.with(Modify::new(Rows::new(1..)).with(Width::wrap(10).keep_words())); +table.modify(Rows::new(1..).with(Width::wrap(10).keep_words())); ``` -`Wrap` also can be used to set a maximum width of a whole table. +`Wrap` can be used to set a maximum width of a whole table. ```rust -use tabled::{Table, settings::Width}; - -let mut table = Table::new(data); +use tabled::settings::Width; // Tries to set table width to 22, in case it's bigger than that. table.with(Width::wrap(22)); @@ -866,20 +938,16 @@ It can be used in combination with `MinWidth` to set an exact table size. `MinWidth` sets a minimal width of an object. ```rust -use tabled::{Table, settings::{Modify, Width, object::Rows}}; - -let mut table = Table::new(data); +use tabled::settings::{Width, object::Rows}; // increase the space used by cells in all rows except the header to be at least 10 -table.with(Modify::new(Rows::new(1..)).with(Width::increase(10))); +table.modify(Rows::new(1..), Width::increase(10)); ``` `MinWidth` also can be used to set a minimum width of a whole table. ```rust -use tabled::{Table, settings::Width}; - -let mut table = Table::new(data); +use tabled::settings::Width; // increase width of a table in case it was lower than 10. table.with(Width::increase(10)); @@ -892,9 +960,8 @@ It can be used in combination with `Truncate` and `Wrap` to set an exact table s You can set a constant width for all columns using `Justify`. ```rust -use tabled::{Table, settings::Width}; +use tabled::settings::Width; -let mut table = Table::new(data); table.with(Width::justify(10)); ``` @@ -903,9 +970,8 @@ table.with(Width::justify(10)); You can tweak `Truncate`, `Wrap`, `MinWidth` logic by setting a priority by which a trim/inc be done. ```rust -use tabled::{Table, settings::{Width, peaker::PriorityMax}}; +use tabled::settings::{Width, peaker::PriorityMax}; -let mut table = Table::new(data); table.with(Width::truncate(10).priority::()); ``` @@ -915,42 +981,111 @@ By default you use `usize` int to set width settings, but you could do it also with `tabled::width::Percent`. ```rust -use tabled::width::{Table, settings::{measurement::Percent, Width}}; +use tabled::settings::{Width, measurement::Percent}; -let mut table = Table::new(data); table.with(Width::wrap(Percent(75))); ``` ### Height -You can increase a table or a specific cell height using `Height` motifier. +You can increase a table or a specific cell height using `Height` modifier. -#### Height increase +Beware that we `Height` controls only content, +so it can't make things smaller then a certain minimum. + +Bellow is an example of setting an exact table height and width. ```rust -use tabled::{Table, settings::{Height, Modify, object::Rows}}; +use std::iter::FromIterator; -let mut table = Table::new(data); +use tabled::{ + settings::{ + peaker::{PriorityMax, PriorityMin}, + Height, Settings, Width, + }, + Table, +}; + +fn generate_data(width: usize, height: usize) -> Vec> { + let dims = format!("{}x{}", width, height); + let string = vec!["x".repeat(width); height].join("\n"); + + vec![ + vec![String::from("N"), String::from("string")], + vec![dims, string], + ] +} + +fn generate_table(data: Vec>, width: usize, height: usize) -> String { + let mut table = Table::from_iter(data); + + table.with( + Settings::empty() + .with(Width::truncate(width).priority::()) + .with(Width::increase(width).priority::()) + .with(Height::increase(height)) + .with(Height::limit(height).priority::()), + ); + + table.to_string() +} + +let table = generate_table(generate_data(40, 10), 30, 8); +println!("{table}"); + +let table = generate_table(generate_data(40, 4), 80, 12); +println!("{table}"); +``` + +```text ++-------+--------------------+ +| N | string | +| | | +| | | ++-------+--------------------+ +| 40x10 | xxxxxxxxxxxxxxxxxx | +| | | ++-------+--------------------+ ++-----------------------------------+------------------------------------------+ +| N | string | +| | | +| | | ++-----------------------------------+------------------------------------------+ +| 40x4 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | +| | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | +| | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | +| | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | +| | | +| | | ++-----------------------------------+------------------------------------------+ +``` + +#### Height increase + +Increase a of a height of a cell of a whole table could be done by `Height::increase`. + +```rust +use tabled::settings::{Height, object::Rows}; // increase height of a table in case it was lower than 10. table.with(Height::increase(10)); // increase height of cells in the last row on a table in case if some of them has it lower than 10. -table.with(Modify::new(Rows::last()).with(Height::increase(10))); +table.modify(Rows::last(), Height::increase(10)); ``` #### Height limit -```rust -use tabled::{Table, settings::{Height, Modify, object::Rows}}; +Truncation a of a height of a cell of a whole table could be done by `Height::limit`. -let mut table = Table::new(data); +```rust +use tabled::settings::{Height, object::Rows}; // decrease height of a table to 10 in case it was bigger than that. table.with(Height::limit(10)); // decrease height of cells in the last row on a table to 10 in case if some of them has it bigger than that. -table.with(Modify::new(Rows::last()).with(Height::limit(10))); +table.modify(Rows::last(), Height::limit(10)); ``` ### Rotate @@ -971,12 +1106,12 @@ Imagine you have a table already which output may look like this. └────┴──────────────┴───────────────────────────┘ ``` -Now we will add the following modificator and the output will be; +Now we will add the following modificator and the output will be rotated; ```rust use tabled::settings::Rotate; -table.with(Rotate::Left) +table.with(Rotate::Left); ``` ```text @@ -991,25 +1126,38 @@ table.with(Rotate::Left) ### Disable -You can remove certain rows or columns from the table. +You can remove certain rows or columns from the table by `Disable`. ```rust -use tabled::{Table, settings::Disable}; +use tabled::settings::{ + object::{Columns, Rows}, + Disable, +}; -let mut table = Table::new(data); table - .with(Disable::Row(..1)) - .with(Disable::Column(3..4)); + .with(Disable::row(Rows::first())) + .with(Disable::column(Columns::single(2))); ``` -### Extract +If the above example be applied for a first example in a file it would look like this. -You can `Extract` segments of a table to focus on a reduced number of rows and columns. +```text ++------+----------------+ +| C | Dennis Ritchie | ++------+----------------+ +| Go | Rob Pike | ++------+----------------+ +| Rust | Graydon Hoare | ++------+----------------+ +``` + +### Extract + +You can `Extract` data segments of a table to focus on it closely. ```rust -use tabled::{Table, settings::Extract}; +use tabled::settings::Extract; -let mut table = Table::new(&data); table.with(Extract::segment(1..3, 1..)); ``` @@ -1027,70 +1175,46 @@ table.with(Extract::segment(1..3, 1..)); +-------+-------------+-----------+ ``` -#### Refinishing - -For styles with unique corner and edge textures it is possible to reapply a table style once a `Table` extract has been created. - -```rust -use tabled::{Table, settings::{Extract, Style}}; - -let mut table = Table::new(&data); -table - .with(Extract::segment(1..3, 1..)) - .with(Style::modern()); -``` - -```text -Raw extract -┼───────────────────────────┼──────────────────┼──────────────┤ -│ The Dark Side of the Moon │ 01 March 1973 │ Unparalleled │ -┼───────────────────────────┼──────────────────┼──────────────┤ -│ Rumours │ 04 February 1977 │ Outstanding │ -┼───────────────────────────┼──────────────────┼──────────────┤ - -Refinished extract -┌───────────────────────────┬──────────────────┬───────────────┐ -│ The Dark Side of the Moon │ 01 March 1973 │ Unparalleled │ -├───────────────────────────┼──────────────────┼───────────────┤ -│ Rumours │ 04 February 1977 │ Outstanding │ -└───────────────────────────┴──────────────────┴───────────────┘ -``` - ### Header and Footer and Panel You can add a `Header` and `Footer` to display some information. ```rust -use tabled::{Table, settings::Panel}; +use tabled::settings::Panel; + +let count_elements = table.count_rows(); -let mut table = Table::new(&data); table + .with(Panel::vertical(0, "A vertical panel").width(1)) .with(Panel::header("Tabled Name")) - .with(Panel::footer(format!("{} elements", data.len()))) + .with(Panel::footer(format!("{} elements", count_elements))); ``` -The look will depend on the style you choose -but it may look something like this: +When applied to the main example of this file it will result in the following output. ```text -┌────────────────────────────────────────────────────────────┐ -│ Tabled Name │ -├────────────────────────────────────────────────────────────┤ - ... -├───────┼──────────────┼─────────┼───────────────────────────┤ -│ 3 elements │ -└────────────────────────────────────────────────────────────┘ -``` - -You can also add a full row/column using `tabled::Panel`. - -```rust -use tabled::{Table, settings::Panel}; - -let mut table = Table::new(&data); -table - .with(Panel::vertical(2).text("A panel on 2nd row")) - .with(Panel::horizontal(0).text("A panel on 1st column")); ++---+------+----------------+---------------+ +| Tabled Name | ++---+------+----------------+---------------+ +| A | name | designed_by | invented_year | +| | | | | +| v | | | | +| e | | | | ++ r +------+----------------+---------------+ +| t | C | Dennis Ritchie | 1972 | +| i | | | | +| c | | | | ++ a +------+----------------+---------------+ +| l | Go | Rob Pike | 2009 | +| | | | | +| p | | | | ++ a +------+----------------+---------------+ +| n | Rust | Graydon Hoare | 2010 | +| e | | | | +| l | | | | ++---+------+----------------+---------------+ +| 4 elements | ++---+------+----------------+---------------+ ``` ### Merge @@ -1125,17 +1249,56 @@ println!("{}", table); You can concatanate 2 tables using `Concat`. It will stick 2 tables together either vertically or horizontally. +The example below show the result of horizontal concat of primarily table of this file. + +```rust +use tabled::settings::Concat; + +table.with(Concat::horizontal(table.clone())); +``` + +The result. + +```text ++------+----------------+---------------+------+----------------+---------------+ +| name | designed_by | invented_year | name | designed_by | invented_year | ++------+----------------+---------------+------+----------------+---------------+ +| C | Dennis Ritchie | 1972 | C | Dennis Ritchie | 1972 | ++------+----------------+---------------+------+----------------+---------------+ +| Go | Rob Pike | 2009 | Go | Rob Pike | 2009 | ++------+----------------+---------------+------+----------------+---------------+ +| Rust | Graydon Hoare | 2010 | Rust | Graydon Hoare | 2010 | ++------+----------------+---------------+------+----------------+---------------+ +``` + +The example below show the result of vertical concat of primarily table of this file. + ```rust use tabled::settings::Concat; -// let t1: Table = ...; -// let t2: Table = ...; +table.with(Concat::vertical(table.clone())); +``` -// vertical concat -t1.with(Concat::vertical(t2)); +The result. -// horizontal concat -t1.with(Concat::horizontal(t2)); +```text ++------+----------------+---------------+ +| name | designed_by | invented_year | ++------+----------------+---------------+ +| C | Dennis Ritchie | 1972 | ++------+----------------+---------------+ +| Go | Rob Pike | 2009 | ++------+----------------+---------------+ +| Rust | Graydon Hoare | 2010 | ++------+----------------+---------------+ +| name | designed_by | invented_year | ++------+----------------+---------------+ +| C | Dennis Ritchie | 1972 | ++------+----------------+---------------+ +| Go | Rob Pike | 2009 | ++------+----------------+---------------+ +| Rust | Graydon Hoare | 2010 | ++------+----------------+---------------+ ``` ### Highlight @@ -1156,7 +1319,7 @@ let data = vec![["A", "B", "C"], ["D", "E", "F"]]; let mut table = Table::new(data); table.with(Style::modern()); -table.with(Highlight::new( +table.with(Highlight::border( Rows::first().and(Columns::single(2).and((1, 1))), Border::filled('*'), )); @@ -1184,7 +1347,7 @@ It's possible to set a horizontal(column) span and vertical(row) span to a cell. ```rust use tabled::{ - settings::{Alignment, Modify, Span}, + settings::{Alignment, Span}, Table, }; @@ -1192,8 +1355,8 @@ let data = vec![["A", "B", "C"], ["D", "E", "F"]]; let mut table = Table::new(data); table - .with(Modify::new((0, 0)).with(Span::column(3))) - .with(Modify::new((1, 0)).with(Span::column(2))) + .modify((0, 0), Span::column(3)) + .modify((1, 0), Span::column(2)) .with(Alignment::center()); println!("{}", table); @@ -1213,7 +1376,7 @@ println!("{}", table); ```rust use tabled::{ - settings::{Alignment, Modify, Span}, + settings::{Alignment, Span}, Table, }; @@ -1221,7 +1384,7 @@ let data = vec![["A", "B", "C"], ["D", "E", "F"]]; let mut table = Table::new(data); table - .with(Modify::new((0, 1)).with(Span::row(3))) + .modify((0, 1), Span::row(3)) .with(Alignment::center()) .with(Alignment::center_vertical()); @@ -1243,123 +1406,35 @@ println!("{}", table); You can `Split` a table on a row or column to redistribute the cells beyond that point into a new shape with the provided point acting as the new, upper boundry in the direction selected. -#### Directions - -Direction functions are the entry point for the `Split` setting. - -There are two directions available: `column` and `row`. +Adding this to a first example will result in the next table. ```rust -use std::iter::FromIterator; -use tabled::{Table, settings::split::Split}; - -let mut table = Table::from_iter(['a'..='z']); -table.with(Split::column(12)); -table.with(Split::row(2)); -``` - -```text -┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ -│ a │ b │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ m │ n │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ y │ z │ -└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ -┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ -│ a │ b │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ -├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ -│ m │ n │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ -├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ -│ y │ z │ │ │ │ │ │ │ │ │ │ │<- y and z act as anchors to new empty cells -└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ to conform to the new shape -┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ -│ a │ y │ b │ z │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ -├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤<- Display::Clean removes empty cells that would be anchors otherwise -│ m │ │ n │ │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ -└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ - ^anchors^ -``` - - -#### Behaviors - -Behaviors determine how cells attempt to conform to the new tables shape. - -There are two behaviors available: `zip` and `concat`. - -`zip` is the default behavior. - -```rust -use tabled::{Table, settings::split::Split}; - -let mut table = Table::new(&data); +use tabled::settings::{Style, split::Split}; +table.with(Style::modern()); table.with(Split::column(2).concat()); -table.with(Split::column(2).zip()); ``` -```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 | | - +---+---+ - - sect 3 +---+---+ - sect 1 sect 2 (anchors) | a | b | - / \ / \ / \ +---+---+ -+---+---+---+---+---+ | c | d | -| a | b | c | d | e | Split::column(2).zip() +---+---+ -+---+---+---+---+---+ => | e | | -| f | g | h | i | j | +---+---+ -+---+---+---+---+---+ | f | g | - +---+---+ - | h | i | - +---+---+ - | j | | - +---+---+ -``` - -#### Displays - -Display functions give the user the choice to `retain` or `clean` empty sections in a `Split` table result. - -- `retain` does not filter any existing or newly added cells when conforming to a new shape. - -- `clean` filters out empty columns/rows from the output and prevents empty cells from acting as anchors to newly inserted cells. - -`clean` is the default `Display`. - -```rust -use std::iter::FromIterator; -use tabled::{ - settings::{split::Split, style::Style}, - Table, -}; -let mut table = Table::from_iter(['a'..='z']); - -table.with(Split::column(25)).with(Style::modern()); -table.clone().with(Split::column(1).concat().retain()); -table.clone().with(Split::column(1).concat()); // .clean() is not necessary as it is the default display property -``` +The result of the running example will be as follows. ```text -┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ -│ a │ b │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ m │ n │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ y │ -├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ -│ z │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │<- lots of extra cells generated -└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ -┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ -│ a │ b │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ m │ n │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ y │ z │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ -└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘ -┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ^ cells retained during concatenation -│ a │ b │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ m │ n │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ y │ z │ -└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘<- cells removed during concatenation +┌───────────────┬────────────────┐ +│ name │ designed_by │ +├───────────────┼────────────────┤ +│ C │ Dennis Ritchie │ +├───────────────┼────────────────┤ +│ Go │ Rob Pike │ +├───────────────┼────────────────┤ +│ Rust │ Graydon Hoare │ +├───────────────┼────────────────┤ +│ invented_year │ │ +├───────────────┼────────────────┤ +│ 1972 │ │ +├───────────────┼────────────────┤ +│ 2009 │ │ +├───────────────┼────────────────┤ +│ 2010 │ │ +└───────────────┴────────────────┘ ``` #### Duplicate @@ -1371,8 +1446,8 @@ use tabled::{Table, settings::{Dup, object::Rows}}; let mut table = Table::new(data); -// copy last line to the first line (first line gets erased). -table.with(Dup::new(Rows::first(), Rows::last())); +// copy lastfirst line to the last line (last line gets erased). +table.with(Dup::new(Rows::last(), Rows::first())); ``` ## Derive @@ -1392,7 +1467,7 @@ struct SomeType { struct SomeOtherType; ``` -The `Tabled` macros available when `derive` feature in turned on. +The `Tabled` macro available when `derive` feature in turned on. And it is by default. ### Override a column name @@ -1565,38 +1640,191 @@ struct Bike { } ``` -## Features +## Table types + +`tabled` has a few representations of tables some differs from it's view some from it's implememtation details. -### Color +There are situations when you might better use one but not another. +But sometimes some can be used interchangable. -The library doesn't bind you in usage of any color library but to be able to work correctly with color input, and avoid [miscalculation of string width](https://github.com/zhiburt/tabled/issues/26) -because of embedded ansi sequences, you should add the `color` feature of `tabled` to your `Cargo.toml`: +Bellow you'll find a short list of existing ones. You can find a descriptive information about each at the documentation. + +### `Table` + +Main table of the library. +It's implemenentation requires that all data be stored on heap. + +### `IterTable` + +It's simmilar to main `Table`, it's only difference is that it does not require a the whole buffer. +It only requires a buffer for 1 row at a time. + +It might be usefull when you can't fit all your data in memory. + +### `CompactTable` + +Simmular to `IterTable` but it might not require any buffer. +It also has capability for a sniffing logic, where we estimate data dimension on a small selection of data. + +It might be usefull in a very constrain environments. +It is the only table which supports `no-std`. + +### `PoolTable` + +Unlike `Table` it does not nessarily requires columns be aligned. +It provides capabilities for a completely uterly diverse table layout. + +Example + +```rust +use tabled::{ + settings::{Alignment, Style}, + tables::PoolTable, +}; + +fn main() { + let characters = [ + "Naruto Uzumaki", + "Kakashi Hatake", + "Minato Namikaze", + "Jiraiya", + "Orochimaru", + "Itachi Uchiha", + ]; + + let data = characters.chunks(2); + + let table = PoolTable::new(data) + .with(Style::dots()) + .with(Alignment::center()) + .to_string(); + + println!("{table}"); +} -```toml -tabled = { version = "*", features = ["ansi"] } ``` -Then you can use colored strings as values and table dimension will be properly estimated. +The output would look like the following. + +``` +................................... +: Naruto Uzumaki : Kakashi Hatake : +:................:................: +: Minato Namikaze : Jiraiya : +:....................:............: +: Orochimaru : Itachi Uchiha : +:...............:.................: +``` + +### `ExpandedDisplay` + +You can use `ExpandedDisplay` if your data structure has a lot of fields. + +Here's an example. ```rust -use owo_colors::OwoColorize; -// ... -let mut builder = tabled::builder::Builder::default(); -builder.push_record(vec!["green".green(), "red".red()]) -let mut table = builder.build(); +use tabled::{display::ExpandedDisplay, Tabled}; + +#[derive(Tabled)] +struct Distribution { + name: &'static str, + is_active: bool, + is_cool: bool, +} + +let data = [ + Distribution { + name: "Manjaro", + is_cool: true, + is_active: true, + }, + Distribution { + name: "Debian", + is_cool: true, + is_active: true, + }, + Distribution { + name: "Debian", + is_cool: true, + is_active: true, + }, +]; + +let table = ExpandedDisplay::new(&data); + +println!("{}", table); +``` + +You'll see the following. + +```text +-[ RECORD 0 ]------ +name | Manjaro +is_active | true +is_cool | true +-[ RECORD 1 ]------ +name | Debian +is_active | true +is_cool | true +-[ RECORD 2 ]------ +name | Debian +is_active | true +is_cool | true +``` + +## Tips & Tricks + +### `std::fmt::*` options + +You use formatting(`std::fmt::*`) options. +To apply certain settings. + +```rust +use tabled::Table; + +let numbers = [1, 2, 3]; +let table = Table::new(numbers); + +println!("{:#^10}", table); +``` + +The result will be as follows. + +```text +#+-----+## +#| i32 |## +#+-----+## +#| 1 |## +#+-----+## +#| 2 |## +#+-----+## +#| 3 |## +#+-----+## ``` -Another example: +### ANSI + +The library doesn't bind you in usage of any color library but to be able to work correctly with colored input (with ANSI sequences), and avoid [miscalculation of string width](https://github.com/zhiburt/tabled/issues/26) +because of embedded ansi sequences, you should add the `ansi` feature to your `Cargo.toml`: + +```toml +tabled = { version = "*", features = ["ansi"] } +``` + +Then you can use colored strings as values and table will be properly rendered. + +Tunning our favorite example will result in the following: ```rust -use tabled::{format::Format, object::Columns, Modify, Style, Table}; +use tabled::{format::Format, object::Columns, Style, Table}; +use owo_colors::OwoColorize; let mut table = Table::new(&data); table .with(Style::psql()) - .with(Modify::new(Columns::single(0)).with(Format::new(|s| s.red().to_string()))) - .with(Modify::new(Columns::single(1)).with(Format::new(|s| s.blue().to_string()))) - .with(Modify::new(Columns::new(2..)).with(Format::new(|s| s.green().to_string()))); + .modify(Columns::single(0), Format::new(|s| s.red().to_string())) + .modify(Columns::single(1), Format::new(|s| s.blue().to_string())) + .modify(Columns::new(2..), Format::new(|s| s.green().to_string())); ``` ![carbon-2](https://user-images.githubusercontent.com/20165848/120526301-b95efc80-c3e1-11eb-8779-0ec48894463b.png) @@ -1666,6 +1894,63 @@ use tabled::{locator::ByColumnName, Alignment, Modify}; table.with(Modify::new(ByColumnName::new("name")).with(Alignment::center())); ``` +### Builder + +`Builder` is a powerfull tool you shall be aware of. + +For example you can use `Builder::index` to make a particular column an index, +which will stay on the left. + +```rust +use tabled::{builder::Builder, settings::Style}; + +let mut builder = Builder::default(); +builder.push_record(["Index", "Language", "Status"]); +builder.push_record(["1", "English", "In progress"]); +builder.push_record(["2", "Deutsch", "Not ready"]); + +let builder = builder.index().column(1).name(None); + +let mut table = builder.build(); +table.with(Style::rounded()); + +println!("{}", table); +``` + +```text +╭─────────┬───────┬─────────────╮ +│ │ Index │ Status │ +├─────────┼───────┼─────────────┤ +│ English │ 1 │ In progress │ +│ Deutsch │ 2 │ Not ready │ +╰─────────┴───────┴─────────────╯ +``` + +For example you can use `transpose()` method to change the layout. + +```rust +// A dynamic table example +// ... + +let mut builder = builder.index().transpose(); +``` + +```text +.-------------------------------------------------. +| | 0 | 1 | 2 | 3 | 4 | +| 0 | And | Little | When | I | You | +| 1 | the | boy | you | don't | know | +| 2 | cat's | blue | comin' | know | we'll | +| 3 | in | and | home | when, | have | +| 4 | the | the | dad? | but | a | +| 5 | cradle | man | | we'll | good | +| 6 | and | on | | get | time | +| 7 | the | the | | together | then | +| 8 | silver | moon | | then | | +| 9 | spoon | | | son | | +'-------------------------------------------------' +``` + ### Macros Utilities for dynamic `Table` displays. @@ -1764,6 +2049,31 @@ col![ It's possible to construct a table at compile time, via [`static_table`](/static_table/README.md). You'd need to include a different crate to use it. +```toml +static_table = "*" +``` + +```rust +let table = static_table::static_table!( + [ + ["x", "y", "op", "result"], + ["1", '2', '*', '2'], + ["2", '2', '*', '4'] + ], + THEME = "ROUNDED", +); + +assert_eq!( + table, + "╭───┬───┬────┬────────╮\n\ + │ x │ y │ op │ result │\n\ + ├───┼───┼────┼────────┤\n\ + │ 1 │ 2 │ * │ 2 │\n\ + │ 2 │ 2 │ * │ 4 │\n\ + ╰───┴───┴────┴────────╯", +); +``` + Notice that you can even use it in documentation. ```rust @@ -1781,142 +2091,18 @@ pub fn mul(left: usize, right: usize) -> usize { } ``` +## Features -## Table types - -`tabled` has a few representations of tables some differs from it's view some from it's implememtation details. - -There are situations when you might better use one but not another. -But sometimes some can be used interchangable. - -Bellow you'll find a short list of existing ones. You can find a descriptive information about each at the documentation. - -### `Table` - -Main table of the library. -It's implemenentation requires that all data be stored on heap. - -### `IterTable` - -It's simmilar to main `Table`, it's only difference is that it does not require a the whole buffer. -It only requires a buffer for 1 row at a time. - -It might be usefull when you can't fit all your data in memory. - -### `CompactTable` - -Simmular to `IterTable` but it might not require any buffer. -It also has capability for a sniffing logic, where we estimate data dimension on a small selection of data. - -It might be usefull in a very constrain environments. -It is the only table which supports `no-std`. - -### `PoolTable` - -Unlike `Table` it does not nessarily requires columns be aligned. -It provides capabilities for a completely uterly diverse table layout. - -Example - -```rust -use tabled::{ - settings::{Alignment, Style}, - tables::PoolTable, -}; - -fn main() { - let characters = [ - "Naruto Uzumaki", - "Kakashi Hatake", - "Minato Namikaze", - "Jiraiya", - "Orochimaru", - "Itachi Uchiha", - ]; - - let data = characters.chunks(2); - - let table = PoolTable::new(data) - .with(Style::dots()) - .with(Alignment::center()) - .to_string(); - - println!("{table}"); -} - -``` - -The output would look like the following. - -``` -................................... -: Naruto Uzumaki : Kakashi Hatake : -:................:................: -: Minato Namikaze : Jiraiya : -:....................:............: -: Orochimaru : Itachi Uchiha : -:...............:.................: -``` - -### `ExpandedDisplay` - -You can use `ExpandedDisplay` if your data structure has a lot of fields. - -Here's an example. - -```rust -use tabled::{display::ExpandedDisplay, Tabled}; - -#[derive(Tabled)] -struct Distribution { - name: &'static str, - is_active: bool, - is_cool: bool, -} - -let data = [ - Distribution { - name: "Manjaro", - is_cool: true, - is_active: true, - }, - Distribution { - name: "Debian", - is_cool: true, - is_active: true, - }, - Distribution { - name: "Debian", - is_cool: true, - is_active: true, - }, -]; - -let table = ExpandedDisplay::new(&data); - -println!("{}", table); -``` - -You'll see the following. +The library has a list of features. -```text --[ RECORD 0 ]------ -name | Manjaro -is_active | true -is_cool | true --[ RECORD 1 ]------ -name | Debian -is_active | true -is_cool | true --[ RECORD 2 ]------ -name | Debian -is_active | true -is_cool | true -``` +- `std` - Used by default. If not its considered `no_std` with a limited set of functionality. +- `derive` - Used by default. A support for `Tabled` derive macro. +- `ansi` - A support for ANSI sequences. +- `macros` - A support for `row!`, `col!` macro. ## Formats -You can convert some formats to a `Table`. +You can convert some formats to a `Table` using an utility library. ### `json` format @@ -1994,7 +2180,7 @@ let languages = vec![ The resultant table will look like the following. As you can see Github tricks a bit a return table, but `GNOME terminal` and `Alacritty` terminal handles it correctly. - ```rust +```text +---------+----------------+---------------+ | name | designed_by | invented_year | +---------+----------------+---------------+ diff --git a/papergrid/src/config/entity.rs b/papergrid/src/config/entity.rs index 0a2d3ba9..bcdafcf6 100644 --- a/papergrid/src/config/entity.rs +++ b/papergrid/src/config/entity.rs @@ -118,3 +118,117 @@ impl Iterator for EntityIterator { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_entity_iter() { + assert_eq!( + Entity::Global.iter(10, 10).collect::>(), + vec![ + (0, 0), + (0, 1), + (0, 2), + (0, 3), + (0, 4), + (0, 5), + (0, 6), + (0, 7), + (0, 8), + (0, 9), + (1, 0), + (1, 1), + (1, 2), + (1, 3), + (1, 4), + (1, 5), + (1, 6), + (1, 7), + (1, 8), + (1, 9), + (2, 0), + (2, 1), + (2, 2), + (2, 3), + (2, 4), + (2, 5), + (2, 6), + (2, 7), + (2, 8), + (2, 9), + (3, 0), + (3, 1), + (3, 2), + (3, 3), + (3, 4), + (3, 5), + (3, 6), + (3, 7), + (3, 8), + (3, 9), + (4, 0), + (4, 1), + (4, 2), + (4, 3), + (4, 4), + (4, 5), + (4, 6), + (4, 7), + (4, 8), + (4, 9), + (5, 0), + (5, 1), + (5, 2), + (5, 3), + (5, 4), + (5, 5), + (5, 6), + (5, 7), + (5, 8), + (5, 9), + (6, 0), + (6, 1), + (6, 2), + (6, 3), + (6, 4), + (6, 5), + (6, 6), + (6, 7), + (6, 8), + (6, 9), + (7, 0), + (7, 1), + (7, 2), + (7, 3), + (7, 4), + (7, 5), + (7, 6), + (7, 7), + (7, 8), + (7, 9), + (8, 0), + (8, 1), + (8, 2), + (8, 3), + (8, 4), + (8, 5), + (8, 6), + (8, 7), + (8, 8), + (8, 9), + (9, 0), + (9, 1), + (9, 2), + (9, 3), + (9, 4), + (9, 5), + (9, 6), + (9, 7), + (9, 8), + (9, 9) + ] + ); + } +} diff --git a/tabled/src/lib.rs b/tabled/src/lib.rs index 68cfb710..1aa2d9a3 100644 --- a/tabled/src/lib.rs +++ b/tabled/src/lib.rs @@ -275,6 +275,8 @@ )] #![allow(clippy::uninlined_format_args)] +mod util; + #[cfg(feature = "std")] mod tabled; diff --git a/tabled/src/settings/color/mod.rs b/tabled/src/settings/color/mod.rs index c793b31d..5e7b8dbb 100644 --- a/tabled/src/settings/color/mod.rs +++ b/tabled/src/settings/color/mod.rs @@ -197,6 +197,11 @@ impl Color { Self { inner } } + /// Creates a new empty [`Color`]`. + pub fn empty() -> Self { + Self::new_static("", "") + } + const fn new_static(prefix: &'static str, suffix: &'static str) -> Self { let color = StaticColor::new(prefix, suffix); let inner = ColorInner::Static(color); diff --git a/tabled/src/settings/concat/mod.rs b/tabled/src/settings/concat/mod.rs index 9f6fe1e4..18284bab 100644 --- a/tabled/src/settings/concat/mod.rs +++ b/tabled/src/settings/concat/mod.rs @@ -27,6 +27,9 @@ use crate::{ /// [`Concat`] in horizontal mode has similar behaviour to tuples `(a, b)`. /// But it behaves on tables rather than on an actual data. /// +/// [`Concat`] DOES NOT handle style merge and other configuration of 2nd table, +/// it just uses 1st one as a bases. +/// /// # Example /// /// diff --git a/tabled/src/settings/height/mod.rs b/tabled/src/settings/height/mod.rs index ad9caff7..0c84d657 100644 --- a/tabled/src/settings/height/mod.rs +++ b/tabled/src/settings/height/mod.rs @@ -119,8 +119,8 @@ impl Height { /// +---------------------+------+------------+", /// ) /// ``` - pub fn increase>(width: W) -> CellHeightIncrease { - CellHeightIncrease::new(width) + pub fn increase>(height: W) -> CellHeightIncrease { + CellHeightIncrease::new(height) } /// Create [`CellHeightLimit`] to set a table/cell height. @@ -189,8 +189,8 @@ impl Height { /// +--+--+--+", /// ); /// ``` - pub fn limit>(width: W) -> CellHeightLimit { - CellHeightLimit::new(width) + pub fn limit>(height: W) -> CellHeightLimit { + CellHeightLimit::new(height) } /// Create [`HeightList`] to set a table height to a constant list of row heights. diff --git a/tabled/src/settings/panel/vertical_panel.rs b/tabled/src/settings/panel/vertical_panel.rs index dee2405f..b24459db 100644 --- a/tabled/src/settings/panel/vertical_panel.rs +++ b/tabled/src/settings/panel/vertical_panel.rs @@ -19,6 +19,23 @@ impl VerticalPanel { { Self { text, col } } + + /// Split the set text to a certain width, so it fits within it. + pub fn width(self, width: usize) -> VerticalPanel + where + S: AsRef, + { + let mut text = String::new(); + + if width > 0 { + text = split_string_by_width(self.text.as_ref(), width); + } + + VerticalPanel { + text, + col: self.col, + } + } } impl TableOption for VerticalPanel @@ -81,3 +98,55 @@ fn move_column_spans(cfg: &mut SpannedConfig, target_column: usize) { cfg.set_row_span((row, col + 1), span); } } + +fn split_string_by_width(str: &str, width: usize) -> String { + if width == 0 { + return String::new(); + } + + let (lhs, rhs) = crate::util::string::split_str(str, width); + if rhs.is_empty() { + return lhs.into_owned(); + } + + let mut buf = lhs.into_owned(); + let mut next = rhs.into_owned(); + while !next.is_empty() { + let (lhs, rhs) = crate::util::string::split_str(&next, width); + buf.push('\n'); + buf.push_str(&lhs); + next = rhs.into_owned(); + } + + buf +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_split_string_by_width() { + assert_eq!(split_string_by_width("123456789", 3), "123\n456\n789"); + assert_eq!(split_string_by_width("123456789", 2), "12\n34\n56\n78\n9"); + assert_eq!( + split_string_by_width("123456789", 1), + "1\n2\n3\n4\n5\n6\n7\n8\n9" + ); + assert_eq!(split_string_by_width("123456789", 0), ""); + + assert_eq!( + split_string_by_width("\u{1b}[31;100m😳😳🏳️\u{1b}[39m\u{1b}[49m😳🏳️", 3), + { + #[cfg(feature = "ansi")] + { + "\u{1b}[31m\u{1b}[100m😳\u{1b}[39m\u{1b}[49m�\n\u{1b}[31m\u{1b}[100m🏳\u{fe0f}\u{1b}[39m\u{1b}[49m😳\n🏳\u{fe0f}" + } + #[cfg(not(feature = "ansi"))] + { + "\u{1b}[31\n;10\n0m�\n😳🏳\n\u{fe0f}\u{1b}[39\nm\u{1b}[4\n9m�\n🏳\u{fe0f}" + } + } + ); + } +} diff --git a/tabled/src/settings/split/mod.rs b/tabled/src/settings/split/mod.rs index becb3c17..097cc964 100644 --- a/tabled/src/settings/split/mod.rs +++ b/tabled/src/settings/split/mod.rs @@ -39,9 +39,128 @@ enum Display { /// - behavior /// - display /// +/// #### Directions +/// +/// Direction functions are the entry point for the `Split` setting. +/// +/// There are two directions available: `column` and `row`. +/// +/// ```rust +/// use std::iter::FromIterator; +/// use tabled::{Table, settings::split::Split}; +/// +/// let mut table = Table::from_iter(['a'..='z']); +/// table.with(Split::column(12)); +/// table.with(Split::row(2)); +/// ``` +/// +/// ```text +/// ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ +/// │ a │ b │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ m │ n │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ y │ z │ +/// └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ +/// ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ +/// │ a │ b │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ +/// ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ +/// │ m │ n │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ +/// ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ +/// │ y │ z │ │ │ │ │ │ │ │ │ │ │<- y and z act as anchors to new empty cells +/// └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ to conform to the new shape +/// ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ +/// │ a │ y │ b │ z │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ +/// ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤<- Display::Clean removes empty cells that would be anchors otherwise +/// │ m │ │ n │ │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ +/// └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ +/// ^anchors^ +/// ``` +/// +/// +/// #### Behaviors +/// +/// Behaviors determine how cells attempt to conform to the new tables shape. +/// +/// There are two behaviors available: `zip` and `concat`. +/// +/// `zip` is the default behavior. +/// +/// ```rust +/// use std::iter::FromIterator; +/// use tabled::{Table, settings::split::Split}; +/// +/// let mut table = Table::from_iter(['a'..='z']); +/// table.with(Split::column(2).concat()); +/// table.with(Split::column(2).zip()); +/// ``` +/// +/// ```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 | | +/// +---+---+ +/// +/// sect 3 +---+---+ +/// sect 1 sect 2 (anchors) | a | b | +/// / \ / \ / \ +---+---+ +/// +---+---+---+---+---+ | c | d | +/// | a | b | c | d | e | Split::column(2).zip() +---+---+ +/// +---+---+---+---+---+ => | e | | +/// | f | g | h | i | j | +---+---+ +/// +---+---+---+---+---+ | f | g | +/// +---+---+ +/// | h | i | +/// +---+---+ +/// | j | | +/// +---+---+ +/// ``` +/// +/// #### Displays +/// +/// Display functions give the user the choice to `retain` or `clean` empty sections in a `Split` table result. +/// +/// - `retain` does not filter any existing or newly added cells when conforming to a new shape. +/// +/// - `clean` filters out empty columns/rows from the output and prevents empty cells from acting as anchors to newly inserted cells. +/// +/// `clean` is the default `Display`. +/// +/// ```rust +/// use std::iter::FromIterator; +/// use tabled::{ +/// settings::{split::Split, style::Style}, +/// Table, +/// }; +/// let mut table = Table::from_iter(['a'..='z']); +/// table.with(Split::column(25)).with(Style::modern()); +/// table.clone().with(Split::column(1).concat().retain()); +/// table.clone().with(Split::column(1).concat()); // .clean() is not necessary as it is the default display property +/// ``` +/// +/// ```text +/// ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ +/// │ a │ b │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ m │ n │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ y │ +/// ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ +/// │ z │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │<- lots of extra cells generated +/// └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ +/// ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ +/// │ a │ b │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ m │ n │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ y │ z │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ +/// └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘ +/// ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ^ cells retained during concatenation +/// │ a │ b │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ m │ n │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ y │ z │ +/// └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘<- cells removed during concatenation +/// ``` +/// +/// /// # Example /// -/// ```rust,no_run +/// ```rust /// use std::iter::FromIterator; /// use tabled::{ /// settings::split::Split, diff --git a/tabled/src/settings/style/builder.rs b/tabled/src/settings/style/builder.rs index eaa60c87..562a350b 100644 --- a/tabled/src/settings/style/builder.rs +++ b/tabled/src/settings/style/builder.rs @@ -1424,6 +1424,20 @@ impl Style Style { + /// Removes frame. + pub const fn remove_frame(self) -> Style<(), (), (), (), H, V, HN, VN> + where + V: Copy, + H: Copy, + { + self.remove_bottom() + .remove_top() + .remove_left() + .remove_right() + } +} + #[cfg(feature = "std")] impl TableOption for Style diff --git a/tabled/src/settings/style/horizontal_line.rs b/tabled/src/settings/style/horizontal_line.rs index cebf9d37..f3b2a852 100644 --- a/tabled/src/settings/style/horizontal_line.rs +++ b/tabled/src/settings/style/horizontal_line.rs @@ -29,7 +29,7 @@ impl HorizontalLine { /// Fetches vertical line from a style. pub const fn inherit( - style: Style, + style: Style, ) -> Self { let borders = style.get_borders(); let line = Line::new( diff --git a/tabled/src/settings/themes/theme.rs b/tabled/src/settings/themes/theme.rs index ef60195e..61232d06 100644 --- a/tabled/src/settings/themes/theme.rs +++ b/tabled/src/settings/themes/theme.rs @@ -96,6 +96,12 @@ impl Theme { } } +impl Default for Theme { + fn default() -> Self { + Self::new() + } +} + macro_rules! func_set_chars { ($name:ident, $arg:ident, $desc:expr) => { #[doc = concat!("Set a border character", " ", "<", $desc, ">", " ", ".")] diff --git a/tabled/src/settings/width/truncate.rs b/tabled/src/settings/width/truncate.rs index 83a05baa..516ebba6 100644 --- a/tabled/src/settings/width/truncate.rs +++ b/tabled/src/settings/width/truncate.rs @@ -18,7 +18,8 @@ use crate::{ }, }; -use super::util::{cut_str, get_table_widths, get_table_widths_with_total}; +use super::util::{get_table_widths, get_table_widths_with_total}; +use crate::util::string::cut_str; /// Truncate cut the string to a given width if its length exceeds it. /// Otherwise keeps the content of a cell untouched. diff --git a/tabled/src/settings/width/util.rs b/tabled/src/settings/width/util.rs index 4cdf0491..18394dff 100644 --- a/tabled/src/settings/width/util.rs +++ b/tabled/src/settings/width/util.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use crate::{ grid::config::SpannedConfig, grid::dimension::SpannedGridDimension, @@ -31,284 +29,3 @@ fn get_table_total_width(list: &[usize], cfg: &SpannedConfig) -> usize { + 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 = "ansi")] - { - 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 = "ansi"))] - { - 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 = "ansi"))] -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 -/// -/// -/// The function is based on Dan Davison delta ansi library. -#[cfg(feature = "ansi")] -pub(crate) fn strip_osc(text: &str) -> (String, Option) { - #[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 = "ansi")] - 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 = "ansi")] - #[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 = "ansi")] - fn test_color_strip() { - let s = "Collored string" - .fg::() - .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" - ) - } - - #[test] - #[cfg(feature = "ansi")] - fn test_srip_osc() { - assert_eq!( - strip_osc("just a string here"), - (String::from("just a string here"), None) - ); - assert_eq!( - strip_osc("/etc/rc.conf"), - (String::from("/etc/rc.conf"), None) - ); - assert_eq!( - strip_osc( - "https://gitlab.com/finestructure/swiftpackageindex-builder/-/pipelines/1054655982" - ), - (String::from("https://gitlab.com/finestructure/swiftpackageindex-builder/-/pipelines/1054655982"), None) - ); - - assert_eq!( - strip_osc(&build_link_prefix_suffix("just a string here")), - (String::default(), Some(String::from("just a string here"))) - ); - assert_eq!( - strip_osc(&build_link_prefix_suffix("/etc/rc.conf")), - (String::default(), Some(String::from("/etc/rc.conf"))) - ); - assert_eq!( - strip_osc( - &build_link_prefix_suffix("https://gitlab.com/finestructure/swiftpackageindex-builder/-/pipelines/1054655982") - ), - (String::default(), Some(String::from("https://gitlab.com/finestructure/swiftpackageindex-builder/-/pipelines/1054655982"))) - ); - - #[cfg(feature = "ansi")] - fn build_link_prefix_suffix(url: &str) -> String { - // https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda - let osc8 = "\x1b]8;;"; - let st = "\x1b\\"; - format!("{osc8}{url}{st}") - } - } -} diff --git a/tabled/src/settings/width/wrap.rs b/tabled/src/settings/width/wrap.rs index 8146edd8..eb21870f 100644 --- a/tabled/src/settings/width/wrap.rs +++ b/tabled/src/settings/width/wrap.rs @@ -21,7 +21,8 @@ use crate::{ }, }; -use super::util::{get_table_widths, get_table_widths_with_total, split_at_pos}; +use super::util::{get_table_widths, get_table_widths_with_total}; +use crate::util::string::split_at_width; /// Wrap wraps a string to a new line in case it exceeds the provided max boundary. /// Otherwise keeps the content of a cell untouched. @@ -209,7 +210,7 @@ pub(crate) fn wrap_text(text: &str, width: usize, keep_words: bool) -> String { #[cfg(feature = "ansi")] pub(crate) fn wrap_text(text: &str, width: usize, keep_words: bool) -> String { - use super::util::strip_osc; + use crate::util::string::strip_osc; if width == 0 { return String::new(); @@ -783,7 +784,8 @@ mod parsing { } 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 (length, width, split_char_size) = split_at_width(text, at); + let count_unknowns = if split_char_size > 0 { at - width } else { 0 }; let (lhs, rhs) = text.split_at(length); (lhs, rhs, (count_unknowns, split_char_size)) diff --git a/tabled/src/tables/extended.rs b/tabled/src/tables/extended.rs index 478505c4..60af682e 100644 --- a/tabled/src/tables/extended.rs +++ b/tabled/src/tables/extended.rs @@ -48,7 +48,6 @@ //! assert_eq!(table, expected); //! ``` -use std::borrow::Cow; use std::fmt::{self, Display}; use crate::grid::util::string::string_width; @@ -289,7 +288,7 @@ fn truncate(text: &mut String, max: usize, suffix: &str) { if max == 0 || text.is_empty() { *text = String::new(); } else { - *text = cut_str_basic(text, max).into_owned(); + *text = crate::util::string::cut_str2(text, max).into_owned(); } let cut_was_done = text.len() < original_len; @@ -297,42 +296,3 @@ fn truncate(text: &mut String, max: usize, suffix: &str) { 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/tabled/src/util/mod.rs b/tabled/src/util/mod.rs new file mode 100644 index 00000000..47ed4f33 --- /dev/null +++ b/tabled/src/util/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "std")] +pub(crate) mod string; diff --git a/tabled/src/util/string.rs b/tabled/src/util/string.rs new file mode 100644 index 00000000..5f159029 --- /dev/null +++ b/tabled/src/util/string.rs @@ -0,0 +1,320 @@ +use std::borrow::Cow; + +/// The function cuts the string to a specific width. +/// Preserving colors with `ansi` feature on. +pub(crate) fn split_str(s: &str, width: usize) -> (Cow<'_, str>, Cow<'_, str>) { + #[cfg(feature = "ansi")] + { + const REPLACEMENT: char = '\u{FFFD}'; + + let stripped = ansi_str::AnsiStr::ansi_strip(s); + let (length, cutwidth, csize) = split_at_width(&stripped, width); + let (mut lhs, mut rhs) = ansi_str::AnsiStr::ansi_split_at(s, length); + + if csize > 0 { + let mut buf = lhs.into_owned(); + let count_unknowns = width - cutwidth; + buf.extend(std::iter::repeat(REPLACEMENT).take(count_unknowns)); + lhs = Cow::Owned(buf); + rhs = Cow::Owned(ansi_str::AnsiStr::ansi_cut(rhs.as_ref(), csize..).into_owned()); + } + + (lhs, rhs) + } + + #[cfg(not(feature = "ansi"))] + { + const REPLACEMENT: char = '\u{FFFD}'; + + let (length, cutwidth, csize) = split_at_width(s, width); + let (lhs, rhs) = s.split_at(length); + + if csize == 0 { + return (Cow::Borrowed(lhs), Cow::Borrowed(rhs)); + } + + let count_unknowns = width - cutwidth; + let mut buf = lhs.to_owned(); + buf.extend(std::iter::repeat(REPLACEMENT).take(count_unknowns)); + + (Cow::Owned(buf), Cow::Borrowed(&rhs[csize..])) + } +} + +/// The function cuts the string to a specific width. +/// Preserving colors with `ansi` feature on. +pub(crate) fn cut_str(s: &str, width: usize) -> Cow<'_, str> { + #[cfg(feature = "ansi")] + { + const REPLACEMENT: char = '\u{FFFD}'; + + let stripped = ansi_str::AnsiStr::ansi_strip(s); + let (length, cutwidth, csize) = split_at_width(&stripped, width); + let mut buf = ansi_str::AnsiStr::ansi_cut(s, ..length); + if csize == 0 { + let mut b = buf.into_owned(); + let count_unknowns = width - cutwidth; + b.extend(std::iter::repeat(REPLACEMENT).take(count_unknowns)); + buf = Cow::Owned(b); + } + + buf + } + + #[cfg(not(feature = "ansi"))] + { + cut_str2(s, width) + } +} + +/// The function cuts the string to a specific width. +/// While not preserving ansi sequences. +pub(crate) fn cut_str2(text: &str, width: usize) -> Cow<'_, str> { + const REPLACEMENT: char = '\u{FFFD}'; + + let (length, cutwidth, csize) = split_at_width(text, width); + if csize == 0 { + let buf = &text[..length]; + return Cow::Borrowed(buf); + } + + let buf = &text[..length]; + let mut buf = buf.to_owned(); + let count_unknowns = width - cutwidth; + 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. +pub(crate) fn split_at_width(s: &str, at_width: usize) -> (usize, usize, usize) { + let mut length = 0; + let mut width = 0; + for c in s.chars() { + if width == at_width { + break; + }; + + let c_width = unicode_width::UnicodeWidthChar::width(c).unwrap_or_default(); + let c_length = c.len_utf8(); + + // We cut the chars which takes more then 1 symbol to display, + // in order to archive the necessary width. + if width + c_width > at_width { + return (length, width, c_length); + } + + width += c_width; + length += c_length; + } + + (length, width, 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 +/// +/// +/// The function is based on Dan Davison delta ansi library. +#[cfg(feature = "ansi")] +pub(crate) fn strip_osc(text: &str) -> (String, Option) { + #[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 = "ansi")] + 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 = "ansi")] + #[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 = "ansi")] + fn test_color_strip() { + let s = "Collored string" + .fg::() + .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" + ) + } + + #[test] + #[cfg(feature = "ansi")] + fn test_srip_osc() { + assert_eq!( + strip_osc("just a string here"), + (String::from("just a string here"), None) + ); + assert_eq!( + strip_osc("/etc/rc.conf"), + (String::from("/etc/rc.conf"), None) + ); + assert_eq!( + strip_osc( + "https://gitlab.com/finestructure/swiftpackageindex-builder/-/pipelines/1054655982" + ), + (String::from("https://gitlab.com/finestructure/swiftpackageindex-builder/-/pipelines/1054655982"), None) + ); + + assert_eq!( + strip_osc(&build_link_prefix_suffix("just a string here")), + (String::default(), Some(String::from("just a string here"))) + ); + assert_eq!( + strip_osc(&build_link_prefix_suffix("/etc/rc.conf")), + (String::default(), Some(String::from("/etc/rc.conf"))) + ); + assert_eq!( + strip_osc( + &build_link_prefix_suffix("https://gitlab.com/finestructure/swiftpackageindex-builder/-/pipelines/1054655982") + ), + (String::default(), Some(String::from("https://gitlab.com/finestructure/swiftpackageindex-builder/-/pipelines/1054655982"))) + ); + + #[cfg(feature = "ansi")] + fn build_link_prefix_suffix(url: &str) -> String { + // https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda + let osc8 = "\x1b]8;;"; + let st = "\x1b\\"; + format!("{osc8}{url}{st}") + } + } +} diff --git a/tabled/tests/settings/panel_test.rs b/tabled/tests/settings/panel_test.rs index 67b59965..d5cc0294 100644 --- a/tabled/tests/settings/panel_test.rs +++ b/tabled/tests/settings/panel_test.rs @@ -345,3 +345,27 @@ test_table!( " | 1 | 1-0 | 1-1 | 1-2 | " " | 2 | 2-0 | 2-1 | 2-2 | " ); + +test_table!( + panel_vertical_split, + Matrix::new(3, 3).with(Style::psql()).with(Panel::vertical(0, "Linux Distributions").width(1)), + " L | N | column 0 | column 1 | column 2 " + " i | | | | " + " n | | | | " + " u | | | | " + " x | | | | " + " | | | | " + " D +---+----------+----------+----------" + " i | 0 | 0-0 | 0-1 | 0-2 " + " s | | | | " + " t | | | | " + " r | | | | " + " i | 1 | 1-0 | 1-1 | 1-2 " + " b | | | | " + " u | | | | " + " t | | | | " + " i | 2 | 2-0 | 2-1 | 2-2 " + " o | | | | " + " n | | | | " + " s | | | | " +); From 66dd17f84f9fc02f5e58eb569b92b83f9aa97660 Mon Sep 17 00:00:00 2001 From: Maxim Zhiburt Date: Wed, 20 Dec 2023 00:00:49 +0300 Subject: [PATCH 2/4] Update doc comments --- papergrid/src/ansi/mod.rs | 3 +- .../grid/records/into_records/buf_records.rs | 11 +--- tabled/src/lib.rs | 65 +++++++++++-------- tabled/src/settings/style/border_color.rs | 2 +- tabled/src/settings/style/builder.rs | 4 +- tabled/src/settings/style/mod.rs | 6 +- tabled/src/settings/themes/colorization.rs | 4 +- tabled/src/settings/themes/theme.rs | 12 ++-- tabled/src/util/string.rs | 2 +- 9 files changed, 59 insertions(+), 50 deletions(-) diff --git a/papergrid/src/ansi/mod.rs b/papergrid/src/ansi/mod.rs index 01e78433..33b19314 100644 --- a/papergrid/src/ansi/mod.rs +++ b/papergrid/src/ansi/mod.rs @@ -1,4 +1,5 @@ -//! A module which contains [`Color`] trait and its implementation [`AnsiColor`]. +//! A module which contains [`ANSIFmt`] trait and its implementation [`ANSIStr`] +#[cfg_attr(feature = "std", doc = "and [`ANSIBuf`].")] #[cfg(feature = "std")] mod ansi_buf; diff --git a/tabled/src/grid/records/into_records/buf_records.rs b/tabled/src/grid/records/into_records/buf_records.rs index d277fd5d..158647f7 100644 --- a/tabled/src/grid/records/into_records/buf_records.rs +++ b/tabled/src/grid/records/into_records/buf_records.rs @@ -1,14 +1,9 @@ -//! 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. +//! A module contains [`BufRecords`] iterator. use crate::grid::records::IntoRecords; /// 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 BufRecords { iter: I, @@ -16,7 +11,7 @@ pub struct BufRecords { } impl BufRecords<(), ()> { - /// Creates new [`BufColumns`] structure, filling the buffer. + /// Creates new [`BufRecords`] structure, filling the buffer. pub fn new( records: I, sniff: usize, @@ -60,7 +55,7 @@ where } } -/// A row iterator for [`BufColumns`] +/// A row iterator for [`BufRecords`] #[derive(Debug)] pub struct BufRecordsIter { iter: I, diff --git a/tabled/src/lib.rs b/tabled/src/lib.rs index 1aa2d9a3..0bb0f060 100644 --- a/tabled/src/lib.rs +++ b/tabled/src/lib.rs @@ -4,7 +4,7 @@ //! You can use [`Tabled`] trait if the data type is known. //! Or you can use [`Builder`] to construct the table from scratch. //! -//! ## Usage +//! ## Derive //! //! If you want to build a table for your custom type. //! A starting point is to a anotate your type with `#[derive(Tabled)]`. @@ -55,9 +55,12 @@ //! assert_eq!(table, expected); //! ``` //! -//! Not all types can derive [`Tabled`] trait though. +//! BEWARE not all types can derive [`Tabled`] trait. //! The example below can't be compiled. //! +//! Because `tabled` must know what we're up to print as a field, so +//! each field must implement [`std::fmt::Display`]. +//! //! ```rust,compile_fail //! # use tabled::Tabled; //! #[derive(Tabled)] @@ -68,8 +71,7 @@ //! struct SomeOtherType; //! ``` //! -//! Because `tabled` must know what we're up to print as a field, so -//! each (almost) field must implement [`std::fmt::Display`]. +//! You can tweak it by derive options. //! //! ### Default implementations //! @@ -92,7 +94,7 @@ //! # assert_eq!(table.to_string(), expected); //! ``` //! -//! ### Dynamic table +//! ### Builder //! //! When you data scheme is not known at compile time. //! You most likely will not able to relay on [`Tabled`] trait. @@ -171,14 +173,21 @@ //! //! You can use many settings which is found in [`tabled::settings`] module. //! +//! # Features +//! +//! - `std` - Used by default. If not its considered `no_std` with a limited set of functionality. +//! - `derive` - Used by default. A support for `Tabled` derive macro. +//! - `ansi` - A support for ANSI sequences. +//! - `macros` - A support for `row!`, `col!` macro. +//! //! # Advanced //! -//! ## Alloc +//! ## Table types //! //! [`Table`] keeps data buffered, which sometimes not ideal choise. //! For such reason there is [`IterTable`] and [`CompactTable`]. //! -//! ### Less allocations +//! ### [`IterTable`] //! //! [`IterTable`] stands on a middle ground between [`Table`] and [`CompactTable`]. //! @@ -205,9 +214,9 @@ //! ); //! ``` //! -//! ## Alloc free (`#nostd`) +//! ### [`CompactTable`] //! -//! [`CompactTable`] can be configured ('1) to not make any allocations. +//! Alloc free 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. @@ -237,6 +246,10 @@ //! table.fmt(StubWriter); //! ``` //! +//! ## `no_std` +//! +//! [`CompactTable`] can be used in `no_std` context. +//! //! ## More information //! //! You can find more examples of settings and attributes in @@ -296,12 +309,12 @@ pub mod grid; #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub use crate::{tabled::Tabled, tables::Table}; -/// A derive to implement a [`Tabled`] trait. +/// A derive macro to implement a [`Tabled`] trait. /// -/// The macros available only when `derive` feature in turned on (and it is by default). +/// The macro 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. +/// The following example will cause an error because of that. /// /// ```rust,compile_fail /// use tabled::Tabled; @@ -325,9 +338,9 @@ pub use crate::{tabled::Tabled, tables::Table}; /// #[derive(Tabled)] /// struct Person { /// #[tabled(rename = "Name")] -/// first_name: &'static str, +/// first_name: String, /// #[tabled(rename = "Surname")] -/// last_name: &'static str, +/// last_name: String, /// } /// ``` /// @@ -344,8 +357,8 @@ pub use crate::{tabled::Tabled, tables::Table}; /// struct Person { /// id: u8, /// #[tabled(skip)] -/// number: &'static str, -/// name: &'static str, +/// number: String, +/// name: String, /// } /// ``` /// @@ -360,9 +373,9 @@ pub use crate::{tabled::Tabled, tables::Table}; /// struct Person { /// id: u8, /// #[tabled(order = 0)] -/// number: &'static str, +/// number: String, /// #[tabled(order = 1)] -/// name: &'static str, +/// name: String, /// } /// ``` /// @@ -429,10 +442,10 @@ pub use crate::{tabled::Tabled, tables::Table}; /// #[tabled(rename_all = "CamelCase")] /// struct Person { /// id: u8, -/// number: &'static str, -/// name: &'static str, +/// number: String, +/// name: String, /// #[tabled(rename_all = "snake_case")] -/// middle_name: &'static str, +/// middle_name: String, /// } /// ``` /// @@ -447,14 +460,14 @@ pub use crate::{tabled::Tabled, tables::Table}; /// #[derive(Tabled)] /// struct Person { /// id: u8, -/// name: &'static str, +/// name: String, /// #[tabled(inline)] /// ed: Education, /// } /// /// #[derive(Tabled)] /// struct Education { -/// uni: &'static str, +/// uni: String, /// graduated: bool, /// } /// ``` @@ -468,12 +481,12 @@ pub use crate::{tabled::Tabled, tables::Table}; /// enum Vehicle { /// #[tabled(inline("Auto::"))] /// Auto { -/// model: &'static str, -/// engine: &'static str, +/// model: String, +/// engine: String, /// }, /// #[tabled(inline)] /// Bikecycle( -/// &'static str, +/// String, /// #[tabled(inline)] Bike, /// ), /// } diff --git a/tabled/src/settings/style/border_color.rs b/tabled/src/settings/style/border_color.rs index 89ce2167..bf3b3d49 100644 --- a/tabled/src/settings/style/border_color.rs +++ b/tabled/src/settings/style/border_color.rs @@ -89,7 +89,7 @@ impl BorderColor { } /// This function constructs a cell borders with all sides's char set to a given color. - /// It behaves like [`Border::full`] with the same color set to each side. + /// It behaves like [`BorderColor::full`] with the same color set to each side. pub fn filled(c: Color) -> Self { Self::full( c.clone(), diff --git a/tabled/src/settings/style/builder.rs b/tabled/src/settings/style/builder.rs index 562a350b..38b43319 100644 --- a/tabled/src/settings/style/builder.rs +++ b/tabled/src/settings/style/builder.rs @@ -51,7 +51,7 @@ use crate::grid::config::ColoredConfig; /// 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. +/// You can turn [`Style`] into [`Theme`] to have a precise control using [`Into`] implementation. /// /// # Example /// @@ -69,7 +69,7 @@ use crate::grid::config::ColoredConfig; /// ``` /// /// [`Table`]: crate::Table -/// [`RawStyle`]: crate::settings::Style::RawStyle +/// [`Theme`]: crate::settings::themes::Theme /// [`Style::corner_top_left`]: Style::corner_top_left /// [`Style::left`]: Style.left /// [`Style::top`]: Style.function.top diff --git a/tabled/src/settings/style/mod.rs b/tabled/src/settings/style/mod.rs index f8fec995..233c5779 100644 --- a/tabled/src/settings/style/mod.rs +++ b/tabled/src/settings/style/mod.rs @@ -85,16 +85,16 @@ //! ) //! ``` //! -//! ## [`RawStyle`] +//! ## [`Theme`] //! -//! A different representation of [`Style`]. +//! A different representation of [`Theme`]. //! 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 +//! [`Theme`]: crate::settings::themes::Theme mod border; mod builder; diff --git a/tabled/src/settings/themes/colorization.rs b/tabled/src/settings/themes/colorization.rs index c0461a94..be7a754f 100644 --- a/tabled/src/settings/themes/colorization.rs +++ b/tabled/src/settings/themes/colorization.rs @@ -9,7 +9,7 @@ use crate::{ /// [`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`] +/// You can colorize borders in a different round using [`BorderColor`] or [`Theme`] /// /// # Examples /// @@ -34,7 +34,7 @@ use crate::{ /// println!("{table}"); /// ``` /// -/// [`RawStyle`]: crate::settings::style::RawStyle +/// [`Theme`]: crate::settings::themes::Theme /// [`BorderColor`]: crate::settings::style::BorderColor #[derive(Debug, Clone, PartialEq, Eq)] pub struct Colorization { diff --git a/tabled/src/settings/themes/theme.rs b/tabled/src/settings/themes/theme.rs index 61232d06..1457f62c 100644 --- a/tabled/src/settings/themes/theme.rs +++ b/tabled/src/settings/themes/theme.rs @@ -104,7 +104,7 @@ impl Default for Theme { macro_rules! func_set_chars { ($name:ident, $arg:ident, $desc:expr) => { - #[doc = concat!("Set a border character", " ", "<", $desc, ">", " ", ".")] + #[doc = concat!("Set a border character", " ", "", $desc, "", " ", ".")] pub fn $name(&mut self, c: char) { self.border.chars.$arg = Some(c); } @@ -113,7 +113,7 @@ macro_rules! func_set_chars { macro_rules! func_remove_chars { ($name:ident, $arg:ident, $desc:expr) => { - #[doc = concat!("Remove a border character", " ", "<", $desc, ">", " ", ".")] + #[doc = concat!("Remove a border character", " ", "", $desc, "", " ", ".")] pub fn $name(&mut self) { self.border.chars.$arg = None; } @@ -122,7 +122,7 @@ macro_rules! func_remove_chars { macro_rules! func_get_chars { ($name:ident, $arg:ident, $desc:expr) => { - #[doc = concat!("Get a border character", " ", "<", $desc, ">", " ", ".")] + #[doc = concat!("Get a border character", " ", "", $desc, "", " ", ".")] pub const fn $name(&self) -> Option { self.border.chars.$arg } @@ -131,7 +131,7 @@ macro_rules! func_get_chars { macro_rules! func_set_colors { ($name:ident, $arg:ident, $desc:expr) => { - #[doc = concat!("Set a border color", " ", "<", $desc, ">", " ", ".")] + #[doc = concat!("Set a border color", " ", "", $desc, "", " ", ".")] pub fn $name(&mut self, color: Color) { self.border.colors.$arg = Some(color); } @@ -140,7 +140,7 @@ macro_rules! func_set_colors { macro_rules! func_remove_colors { ($name:ident, $arg:ident, $desc:expr) => { - #[doc = concat!("Remove a border color", " ", "<", $desc, ">", " ", ".")] + #[doc = concat!("Remove a border color", " ", "", $desc, "", " ", ".")] pub fn $name(&mut self) { self.border.colors.$arg = None; } @@ -149,7 +149,7 @@ macro_rules! func_remove_colors { macro_rules! func_get_colors { ($name:ident, $arg:ident, $desc:expr) => { - #[doc = concat!("Set a border color", " ", "<", $desc, ">", " ", ".")] + #[doc = concat!("Get a border color", " ", "", $desc, "", " ", ".")] pub fn $name(&self) -> Option<&Color> { self.border.colors.$arg.as_ref() } diff --git a/tabled/src/util/string.rs b/tabled/src/util/string.rs index 5f159029..63e0ad82 100644 --- a/tabled/src/util/string.rs +++ b/tabled/src/util/string.rs @@ -51,7 +51,7 @@ pub(crate) fn cut_str(s: &str, width: usize) -> Cow<'_, str> { let stripped = ansi_str::AnsiStr::ansi_strip(s); let (length, cutwidth, csize) = split_at_width(&stripped, width); let mut buf = ansi_str::AnsiStr::ansi_cut(s, ..length); - if csize == 0 { + if csize != 0 { let mut b = buf.into_owned(); let count_unknowns = width - cutwidth; b.extend(std::iter::repeat(REPLACEMENT).take(count_unknowns)); From d5ee197f58e619446d4d247fd3a8ca6e09ae3f4f Mon Sep 17 00:00:00 2001 From: Maxim Zhiburt Date: Wed, 20 Dec 2023 00:10:40 +0300 Subject: [PATCH 3/4] Fix cargo fmt --- papergrid/src/ansi/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/papergrid/src/ansi/mod.rs b/papergrid/src/ansi/mod.rs index 33b19314..cee7c389 100644 --- a/papergrid/src/ansi/mod.rs +++ b/papergrid/src/ansi/mod.rs @@ -1,6 +1,5 @@ //! A module which contains [`ANSIFmt`] trait and its implementation [`ANSIStr`] #[cfg_attr(feature = "std", doc = "and [`ANSIBuf`].")] - #[cfg(feature = "std")] mod ansi_buf; mod ansi_str; From a0d1d3e40cabf308767e4e5b6b8622f8d58ee73c Mon Sep 17 00:00:00 2001 From: Maxim Zhiburt Date: Wed, 20 Dec 2023 02:45:08 +0300 Subject: [PATCH 4/4] Update docs --- README.md | 113 ++++++++++++++++-------------------------------------- 1 file changed, 33 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index ea9c4d4d..7aea57a9 100644 --- a/README.md +++ b/README.md @@ -1957,91 +1957,38 @@ Utilities for dynamic `Table` displays. #### Col and Row -Combine `col!` and `row!` to create flexible table visualizations. - -```rust -row![table1, table2]; -``` +`col!` creates a single column table, with a given set of cells. +`row!` creates a single row table, with a given set of cells. -```text -+-------------------------------------------+---------------------------------------------+ -| .---------------------------------------. | ┌────────────────────┬─────┬──────────────┐ | -| | name | age | is_validated | | │ name │ age │ is_validated │ | -| | Jon Doe | 255 | false | | ├────────────────────┼─────┼──────────────┤ | -| | Mark Nelson | 13 | true | | │ Jack Black │ 51 │ false │ | -| | Terminal Monitor | 0 | false | | ├────────────────────┼─────┼──────────────┤ | -| | Adam Blend | 17 | true | | │ Michelle Goldstein │ 44 │ true │ | -| '---------------------------------------' | └────────────────────┴─────┴──────────────┘ | -+-------------------------------------------+---------------------------------------------+ -``` +Combine `col!` and `row!` to create flexible table visualizations. ```rust -col![table1, table2]; -``` - -```text -+---------------------------------------------+ -| .---------------------------------------. | -| | name | age | is_validated | | -| | Jon Doe | 255 | false | | -| | Mark Nelson | 13 | true | | -| | Terminal Monitor | 0 | false | | -| | Adam Blend | 17 | true | | -| '---------------------------------------' | -+---------------------------------------------+ -| ┌────────────────────┬─────┬──────────────┐ | -| │ name │ age │ is_validated │ | -| ├────────────────────┼─────┼──────────────┤ | -| │ Jack Black │ 51 │ false │ | -| ├────────────────────┼─────┼──────────────┤ | -| │ Michelle Goldstein │ 44 │ true │ | -| └────────────────────┴─────┴──────────────┘ | -+---------------------------------------------+ -``` +use tabled::{col, row, settings::Style}; -```rust -row![table1; 3]; -``` +let mut table = row![ + col!["table 0", "0", "1", "2"], + col!["table 1", "world"], + col!["table 2"], +]; +table.with(Style::modern_rounded()); -```text -+-------------------------------------------+-------------------------------------------+-------------------------------------------+ -| .---------------------------------------. | .---------------------------------------. | .---------------------------------------. | -| | name | age | is_validated | | | name | age | is_validated | | | name | age | is_validated | | -| | Jon Doe | 255 | false | | | Jon Doe | 255 | false | | | Jon Doe | 255 | false | | -| | Mark Nelson | 13 | true | | | Mark Nelson | 13 | true | | | Mark Nelson | 13 | true | | -| | Terminal Monitor | 0 | false | | | Terminal Monitor | 0 | false | | | Terminal Monitor | 0 | false | | -| | Adam Blend | 17 | true | | | Adam Blend | 17 | true | | | Adam Blend | 17 | true | | -| '---------------------------------------' | '---------------------------------------' | '---------------------------------------' | -+-------------------------------------------+-------------------------------------------+-------------------------------------------+ +println!("{table}"); ``` -```rust -col![ - row![table_a, table_b], - table_c -] -``` +The output you're goint to see running it. ```text -+----------------------------------------------------------------------------------+ -| +--------------------------------+---------------------------------------------+ | -| | +-------+-----+--------------+ | ┌────────────────────┬─────┬──────────────┐ | | -| | | name | age | is_validated | | │ name │ age │ is_validated │ | | -| | +-------+-----+--------------+ | ├────────────────────┼─────┼──────────────┤ | | -| | | Sam | 31 | true | | │ Jack Black │ 51 │ false │ | | -| | +-------+-----+--------------+ | ├────────────────────┼─────┼──────────────┤ | | -| | | Sarah | 26 | true | | │ Michelle Goldstein │ 44 │ true │ | | -| | +-------+-----+--------------+ | └────────────────────┴─────┴──────────────┘ | | -| +--------------------------------+---------------------------------------------+ | -+----------------------------------------------------------------------------------+ -| .---------------------------------------. | -| | name | age | is_validated | | -| | Jon Doe | 255 | false | | -| | Mark Nelson | 13 | true | | -| | Terminal Monitor | 0 | false | | -| | Adam Blend | 17 | true | | -| '---------------------------------------' | -+----------------------------------------------------------------------------------+ +╭─────────────┬─────────────┬─────────────╮ +│ +---------+ │ +---------+ │ +---------+ │ +│ | table 0 | │ | table 1 | │ | table 2 | │ +│ +---------+ │ +---------+ │ +---------+ │ +│ | 0 | │ | world | │ │ +│ +---------+ │ +---------+ │ │ +│ | 1 | │ │ │ +│ +---------+ │ │ │ +│ | 2 | │ │ │ +│ +---------+ │ │ │ +╰─────────────┴─────────────┴─────────────╯ ``` #### `static_table` @@ -2079,18 +2026,24 @@ Notice that you can even use it in documentation. ```rust /// Multiply 2 integers together. /// -/// ``` #[doc = static_table::static_table!([ - ["a", "b", "result"], - ["1", '2', '3'], + ["x", "y", "result"], + ["1", '0', '0'], + ["1", '2', '2'], ["2", '2', '4'] ])] -/// ``` pub fn mul(left: usize, right: usize) -> usize { left + right } ``` +It will be looking as foolows. + + + + Preview + + ## Features The library has a list of features.