summaryrefslogtreecommitdiffstats
path: root/vendor/tabled
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:48 +0000
commitef24de24a82fe681581cc130f342363c47c0969a (patch)
tree0d494f7e1a38b95c92426f58fe6eaa877303a86c /vendor/tabled
parentReleasing progress-linux version 1.74.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-ef24de24a82fe681581cc130f342363c47c0969a.tar.xz
rustc-ef24de24a82fe681581cc130f342363c47c0969a.zip
Merging upstream version 1.75.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/tabled')
-rw-r--r--vendor/tabled/.cargo-checksum.json1
-rw-r--r--vendor/tabled/Cargo.lock210
-rw-r--r--vendor/tabled/Cargo.toml367
-rw-r--r--vendor/tabled/LICENSE-MIT21
-rw-r--r--vendor/tabled/README.md92
-rw-r--r--vendor/tabled/examples/README.md831
-rw-r--r--vendor/tabled/examples/alphabet.rs13
-rw-r--r--vendor/tabled/examples/border_text.rs44
-rw-r--r--vendor/tabled/examples/builder.rs38
-rw-r--r--vendor/tabled/examples/builder_index.rs50
-rw-r--r--vendor/tabled/examples/chess.rs37
-rw-r--r--vendor/tabled/examples/col_row_macros.rs62
-rw-r--r--vendor/tabled/examples/color.rs74
-rw-r--r--vendor/tabled/examples/colored_borders.rs65
-rw-r--r--vendor/tabled/examples/colored_padding.rs173
-rw-r--r--vendor/tabled/examples/colorization.rs69
-rw-r--r--vendor/tabled/examples/column_names.rs63
-rw-r--r--vendor/tabled/examples/compact_table.rs23
-rw-r--r--vendor/tabled/examples/compact_table_2.rs20
-rw-r--r--vendor/tabled/examples/compact_table_3.rs19
-rw-r--r--vendor/tabled/examples/concat.rs58
-rw-r--r--vendor/tabled/examples/custom_style.rs54
-rw-r--r--vendor/tabled/examples/derive/display_with.rs65
-rw-r--r--vendor/tabled/examples/derive/inline.rs55
-rw-r--r--vendor/tabled/examples/derive/inline_enum.rs50
-rw-r--r--vendor/tabled/examples/derive/order.rs48
-rw-r--r--vendor/tabled/examples/derive/rename.rs45
-rw-r--r--vendor/tabled/examples/derive/rename_all.rs56
-rw-r--r--vendor/tabled/examples/derive/skip.rs49
-rw-r--r--vendor/tabled/examples/disable.rs49
-rw-r--r--vendor/tabled/examples/extended_display.rs35
-rw-r--r--vendor/tabled/examples/extract.rs106
-rw-r--r--vendor/tabled/examples/format.rs56
-rw-r--r--vendor/tabled/examples/formatting_settings.rs47
-rw-r--r--vendor/tabled/examples/grid_colors.rs46
-rw-r--r--vendor/tabled/examples/height.rs49
-rw-r--r--vendor/tabled/examples/highlight.rs28
-rw-r--r--vendor/tabled/examples/highlight_color.rs27
-rw-r--r--vendor/tabled/examples/hyperlink.rs83
-rw-r--r--vendor/tabled/examples/iter_table.rs31
-rw-r--r--vendor/tabled/examples/margin.rs21
-rw-r--r--vendor/tabled/examples/matrix.rs27
-rw-r--r--vendor/tabled/examples/merge_duplicates.rs58
-rw-r--r--vendor/tabled/examples/merge_duplicates_2.rs78
-rw-r--r--vendor/tabled/examples/nested_table.rs117
-rw-r--r--vendor/tabled/examples/nested_table_2.rs90
-rw-r--r--vendor/tabled/examples/nested_table_3.rs56
-rw-r--r--vendor/tabled/examples/panel.rs63
-rw-r--r--vendor/tabled/examples/pool_table.rs33
-rw-r--r--vendor/tabled/examples/pool_table2.rs26
-rw-r--r--vendor/tabled/examples/rotate.rs38
-rw-r--r--vendor/tabled/examples/settings_list.rs57
-rw-r--r--vendor/tabled/examples/shadow.rs160
-rw-r--r--vendor/tabled/examples/span.rs40
-rw-r--r--vendor/tabled/examples/split.rs43
-rw-r--r--vendor/tabled/examples/table.rs56
-rw-r--r--vendor/tabled/examples/table_width.rs40
-rw-r--r--vendor/tabled/examples/table_width_2.rs23
-rw-r--r--vendor/tabled/src/builder/index_builder.rs318
-rw-r--r--vendor/tabled/src/builder/mod.rs118
-rw-r--r--vendor/tabled/src/builder/table_builder.rs506
-rw-r--r--vendor/tabled/src/grid/colored_config.rs112
-rw-r--r--vendor/tabled/src/grid/compact_multiline_config.rs211
-rw-r--r--vendor/tabled/src/grid/dimension/complete_dimension.rs136
-rw-r--r--vendor/tabled/src/grid/dimension/complete_dimension_vec_records.rs128
-rw-r--r--vendor/tabled/src/grid/dimension/const_dimension.rs70
-rw-r--r--vendor/tabled/src/grid/dimension/mod.rs32
-rw-r--r--vendor/tabled/src/grid/dimension/peekable_dimension.rs335
-rw-r--r--vendor/tabled/src/grid/dimension/pool_table_dimension.rs36
-rw-r--r--vendor/tabled/src/grid/dimension/static_dimension.rs63
-rw-r--r--vendor/tabled/src/grid/mod.rs48
-rw-r--r--vendor/tabled/src/grid/records/empty_records.rs41
-rw-r--r--vendor/tabled/src/grid/records/into_records/buf_records.rs213
-rw-r--r--vendor/tabled/src/grid/records/into_records/either_string.rs22
-rw-r--r--vendor/tabled/src/grid/records/into_records/limit_column_records.rs82
-rw-r--r--vendor/tabled/src/grid/records/into_records/limit_row_records.rs59
-rw-r--r--vendor/tabled/src/grid/records/into_records/mod.rs26
-rw-r--r--vendor/tabled/src/grid/records/into_records/truncate_records.rs136
-rw-r--r--vendor/tabled/src/grid/records/mod.rs19
-rw-r--r--vendor/tabled/src/grid/records/records_mut.rs34
-rw-r--r--vendor/tabled/src/grid/records/resizable.rs217
-rw-r--r--vendor/tabled/src/lib.rs490
-rw-r--r--vendor/tabled/src/macros/col.rs50
-rw-r--r--vendor/tabled/src/macros/mod.rs6
-rw-r--r--vendor/tabled/src/macros/row.rs44
-rw-r--r--vendor/tabled/src/settings/alignment/mod.rs189
-rw-r--r--vendor/tabled/src/settings/cell_option.rs55
-rw-r--r--vendor/tabled/src/settings/color/mod.rs346
-rw-r--r--vendor/tabled/src/settings/concat/mod.rs171
-rw-r--r--vendor/tabled/src/settings/disable/mod.rs196
-rw-r--r--vendor/tabled/src/settings/duplicate/mod.rs150
-rw-r--r--vendor/tabled/src/settings/extract/mod.rs257
-rw-r--r--vendor/tabled/src/settings/format/format_config.rs14
-rw-r--r--vendor/tabled/src/settings/format/format_content.rs86
-rw-r--r--vendor/tabled/src/settings/format/format_positioned.rs51
-rw-r--r--vendor/tabled/src/settings/format/mod.rs144
-rw-r--r--vendor/tabled/src/settings/formatting/alignment_strategy.rs172
-rw-r--r--vendor/tabled/src/settings/formatting/charset.rs108
-rw-r--r--vendor/tabled/src/settings/formatting/justification.rs127
-rw-r--r--vendor/tabled/src/settings/formatting/mod.rs20
-rw-r--r--vendor/tabled/src/settings/formatting/tab_size.rs62
-rw-r--r--vendor/tabled/src/settings/formatting/trim_strategy.rs118
-rw-r--r--vendor/tabled/src/settings/height/cell_height_increase.rs98
-rw-r--r--vendor/tabled/src/settings/height/cell_height_limit.rs103
-rw-r--r--vendor/tabled/src/settings/height/height_list.rs47
-rw-r--r--vendor/tabled/src/settings/height/mod.rs228
-rw-r--r--vendor/tabled/src/settings/height/table_height_increase.rs90
-rw-r--r--vendor/tabled/src/settings/height/table_height_limit.rs123
-rw-r--r--vendor/tabled/src/settings/height/util.rs22
-rw-r--r--vendor/tabled/src/settings/highlight/mod.rs452
-rw-r--r--vendor/tabled/src/settings/locator/mod.rs202
-rw-r--r--vendor/tabled/src/settings/margin/mod.rs137
-rw-r--r--vendor/tabled/src/settings/measurement/mod.rs161
-rw-r--r--vendor/tabled/src/settings/merge/mod.rs196
-rw-r--r--vendor/tabled/src/settings/mod.rs135
-rw-r--r--vendor/tabled/src/settings/modify.rs81
-rw-r--r--vendor/tabled/src/settings/object/cell.rs70
-rw-r--r--vendor/tabled/src/settings/object/columns.rs209
-rw-r--r--vendor/tabled/src/settings/object/frame.rs69
-rw-r--r--vendor/tabled/src/settings/object/mod.rs765
-rw-r--r--vendor/tabled/src/settings/object/rows.rs213
-rw-r--r--vendor/tabled/src/settings/object/segment.rs151
-rw-r--r--vendor/tabled/src/settings/object/util.rs22
-rw-r--r--vendor/tabled/src/settings/padding/mod.rs164
-rw-r--r--vendor/tabled/src/settings/panel/footer.rs32
-rw-r--r--vendor/tabled/src/settings/panel/header.rs32
-rw-r--r--vendor/tabled/src/settings/panel/horizontal_panel.rs80
-rw-r--r--vendor/tabled/src/settings/panel/mod.rs127
-rw-r--r--vendor/tabled/src/settings/panel/vertical_panel.rs83
-rw-r--r--vendor/tabled/src/settings/peaker/mod.rs94
-rw-r--r--vendor/tabled/src/settings/rotate/mod.rs155
-rw-r--r--vendor/tabled/src/settings/settings_list.rs71
-rw-r--r--vendor/tabled/src/settings/shadow/mod.rs195
-rw-r--r--vendor/tabled/src/settings/span/column.rs125
-rw-r--r--vendor/tabled/src/settings/span/mod.rs68
-rw-r--r--vendor/tabled/src/settings/span/row.rs126
-rw-r--r--vendor/tabled/src/settings/split/mod.rs420
-rw-r--r--vendor/tabled/src/settings/style/border.rs159
-rw-r--r--vendor/tabled/src/settings/style/border_char.rs99
-rw-r--r--vendor/tabled/src/settings/style/border_color.rs155
-rw-r--r--vendor/tabled/src/settings/style/border_text.rs253
-rw-r--r--vendor/tabled/src/settings/style/builder.rs1208
-rw-r--r--vendor/tabled/src/settings/style/horizontal_line.rs69
-rw-r--r--vendor/tabled/src/settings/style/line.rs91
-rw-r--r--vendor/tabled/src/settings/style/mod.rs127
-rw-r--r--vendor/tabled/src/settings/style/offset.rs19
-rw-r--r--vendor/tabled/src/settings/style/raw_style.rs438
-rw-r--r--vendor/tabled/src/settings/style/span_border_correction.rs216
-rw-r--r--vendor/tabled/src/settings/style/vertical_line.rs50
-rw-r--r--vendor/tabled/src/settings/table_option.rs30
-rw-r--r--vendor/tabled/src/settings/themes/colorization.rs388
-rw-r--r--vendor/tabled/src/settings/themes/column_names.rs355
-rw-r--r--vendor/tabled/src/settings/themes/mod.rs9
-rw-r--r--vendor/tabled/src/settings/width/justify.rs96
-rw-r--r--vendor/tabled/src/settings/width/min_width.rs205
-rw-r--r--vendor/tabled/src/settings/width/mod.rs163
-rw-r--r--vendor/tabled/src/settings/width/truncate.rs505
-rw-r--r--vendor/tabled/src/settings/width/util.rs265
-rw-r--r--vendor/tabled/src/settings/width/width_list.rs50
-rw-r--r--vendor/tabled/src/settings/width/wrap.rs1468
-rw-r--r--vendor/tabled/src/tabled.rs150
-rw-r--r--vendor/tabled/src/tables/compact.rs309
-rw-r--r--vendor/tabled/src/tables/extended.rs338
-rw-r--r--vendor/tabled/src/tables/iter.rs344
-rw-r--r--vendor/tabled/src/tables/mod.rs48
-rw-r--r--vendor/tabled/src/tables/table.rs464
-rw-r--r--vendor/tabled/src/tables/table_pool.rs1607
-rw-r--r--vendor/tabled/src/tables/util/mod.rs2
-rw-r--r--vendor/tabled/src/tables/util/utf8_writer.rs29
-rw-r--r--vendor/tabled/tests/core/builder_test.rs748
-rw-r--r--vendor/tabled/tests/core/compact_table.rs119
-rw-r--r--vendor/tabled/tests/core/extended_table_test.rs472
-rw-r--r--vendor/tabled/tests/core/index_test.rs194
-rw-r--r--vendor/tabled/tests/core/iter_table.rs214
-rw-r--r--vendor/tabled/tests/core/mod.rs7
-rw-r--r--vendor/tabled/tests/core/pool_table.rs634
-rw-r--r--vendor/tabled/tests/core/table_test.rs847
-rw-r--r--vendor/tabled/tests/derive/derive_test.rs1090
-rw-r--r--vendor/tabled/tests/derive/mod.rs1
-rw-r--r--vendor/tabled/tests/macros/col_row_test.rs264
-rw-r--r--vendor/tabled/tests/macros/mod.rs1
-rw-r--r--vendor/tabled/tests/main.rs7
-rw-r--r--vendor/tabled/tests/matrix/matrix.rs161
-rw-r--r--vendor/tabled/tests/matrix/matrix_list.rs58
-rw-r--r--vendor/tabled/tests/matrix/mod.rs6
-rw-r--r--vendor/tabled/tests/settings/alignment_test.rs138
-rw-r--r--vendor/tabled/tests/settings/color_test.rs34
-rw-r--r--vendor/tabled/tests/settings/colorization.rs64
-rw-r--r--vendor/tabled/tests/settings/column_names_test.rs270
-rw-r--r--vendor/tabled/tests/settings/concat_test.rs140
-rw-r--r--vendor/tabled/tests/settings/disable_test.rs83
-rw-r--r--vendor/tabled/tests/settings/duplicate_test.rs189
-rw-r--r--vendor/tabled/tests/settings/extract_test.rs242
-rw-r--r--vendor/tabled/tests/settings/format_test.rs269
-rw-r--r--vendor/tabled/tests/settings/formatting_test.rs68
-rw-r--r--vendor/tabled/tests/settings/height_test.rs263
-rw-r--r--vendor/tabled/tests/settings/highlingt_test.rs419
-rw-r--r--vendor/tabled/tests/settings/margin_test.rs195
-rw-r--r--vendor/tabled/tests/settings/merge_test.rs196
-rw-r--r--vendor/tabled/tests/settings/mod.rs23
-rw-r--r--vendor/tabled/tests/settings/padding_test.rs138
-rw-r--r--vendor/tabled/tests/settings/panel_test.rs337
-rw-r--r--vendor/tabled/tests/settings/render_settings.rs292
-rw-r--r--vendor/tabled/tests/settings/rotate_test.rs214
-rw-r--r--vendor/tabled/tests/settings/shadow_test.rs105
-rw-r--r--vendor/tabled/tests/settings/span_test.rs1234
-rw-r--r--vendor/tabled/tests/settings/split_test.rs277
-rw-r--r--vendor/tabled/tests/settings/style_test.rs2606
-rw-r--r--vendor/tabled/tests/settings/width_test.rs2836
209 files changed, 40663 insertions, 0 deletions
diff --git a/vendor/tabled/.cargo-checksum.json b/vendor/tabled/.cargo-checksum.json
new file mode 100644
index 000000000..24deb3153
--- /dev/null
+++ b/vendor/tabled/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.lock":"8acd50c40b8eb734ac267ab12360fbabe188c05aa93a5b68c60f36d42cc0ac59","Cargo.toml":"bac040c9f0a02d93211a8bbdc0fa312113bc9835ede425ae6320d24f487aa990","LICENSE-MIT":"49ad3fe9bc9d4f0798a481e1c73ac833c52b4525c72fb2c4b0cd1a510e88a177","README.md":"04b277bd697992dfef40b34a8631bda6eccb91787884b97ae8cdb7257396d7a3","examples/README.md":"c35447fb5c215769ceb80b9279d9f02df9250fbd12ecc2598ee9ab8f1a082b28","examples/alphabet.rs":"469835de88d8da6e1a7b5b162ec28116da0480cd03a1a3a68df5a7a9416da467","examples/border_text.rs":"87ec5c3726d78aaba8a095b3b0018c3d523373288399b60bf8490d98660a814e","examples/builder.rs":"68bd0a303ace09ec695b4ccc5e024ddb9ae85fceb5e5f601d6589651c319f3d1","examples/builder_index.rs":"42448b10779d751ac0e853c77f46df14b51d169ebb1934dca24ae6f39c105ddf","examples/chess.rs":"8d54501d7286d0a0ff2725859824da2a2879e60e4873ccaa8fde1456ac407898","examples/col_row_macros.rs":"66bfb4bec2ecaebd0aeb1bc5fcc534aa091686eb48d7b0ce886ab1218692eb09","examples/color.rs":"6c6b4522976da5775b5501e26eb8df3f56298bcc1dd48bce6fee6f1b7d869b52","examples/colored_borders.rs":"ba36f5c5deb13be80a6bfd10e58666c9350ea06128a8f9b0b5cf0120351adb91","examples/colored_padding.rs":"5b17a748c48d5630f18cbb394ed41df5ff69e8a1f5b4b1dfbe0ce15041e44e8b","examples/colorization.rs":"e3508840fd8b479219d07395ca283f372574a77c8125d593d974eeb8810493f9","examples/column_names.rs":"c00c1feb459ca3549af13d31351c15e7a6bc671eefc8065597f831e164c620af","examples/compact_table.rs":"2564f8b2b14872e743bc9b240047fb32d07d2ed950d9fceace5f3b2f59c4748d","examples/compact_table_2.rs":"6c384217d99a0a0c603e6992878f8881ebe1c4bb3060535d0213d9fcb2c57e7c","examples/compact_table_3.rs":"d017100154a8952677ccb1dd4b89473f0359c9eacd58ff5de7859ed2b37efd65","examples/concat.rs":"77ea7c4332baf7d10b7c3e99efa5a1fd3fd3ee7e3f1c73737d10422a330cc778","examples/custom_style.rs":"4815e7830b8a3826c35ebdc6dc8c4e52239e11c78f03ad01fa4117ce45d263c5","examples/derive/display_with.rs":"a4c5b6400c3236811d6239225706f59934c72b978d02547f83ef6581e6a04877","examples/derive/inline.rs":"4037cce3819a0c93d78f514005956b95550eb9dcc51b371d1a43af4e582ed0f6","examples/derive/inline_enum.rs":"17cd68807d247269de6cb4e596bc8af0353149b1ce76c7723f8ba2aa573ad034","examples/derive/order.rs":"5f8a7157e7195f6bfc02644fc609a020d07371d74cc5b9288c0616f325a2b237","examples/derive/rename.rs":"9f06397edef8ed7ec0691328ea2053f426fe32bf59a921db0d5312ae6b1893ec","examples/derive/rename_all.rs":"3d1479a0d303f8816f7f7d4b04946a5c2dc16176eb6855bf17b3a201dd43e303","examples/derive/skip.rs":"baed27513011a2369d37fe69de86a5de0b3593dc5279a5b799d6ed06584eafd5","examples/disable.rs":"c7a9801e3fc3c2f2bac4c06fef587c1c06f025d00b4a549ad2d1f9e7a7072431","examples/extended_display.rs":"e58b83b61c937bbd2a4563bfddaa971464e04f5ecc8edef6111693d6855f2e1a","examples/extract.rs":"aafb4706d2a5017284ec1657dd4376feb182291c428319d258a15f3903d60d57","examples/format.rs":"acf136a6bbb9035568a00c664df289ad5ae84a6adf089fb339d4b2fac83667a9","examples/formatting_settings.rs":"fe7ae39ed108c04b760799070c2ec1a76b06e43cf9f78bccfe44cb5a9ae47d1c","examples/grid_colors.rs":"a1a5f72dc4bbee5e1fbe6b318decd0992a3e875ca3a3fb64f1f775816546c3be","examples/height.rs":"5489041a299a02d437a4656ca60446571418eec5c4db12717f59373582d04bda","examples/highlight.rs":"5839e6ef8e338717c21c9a4af452ee76c589337cc7de59eb554e8326a5351464","examples/highlight_color.rs":"f19b723b727ccafa99aba2f4d2449c0289d623f268a9b64c980794da25e4202e","examples/hyperlink.rs":"417f2b0e98046d6b9782542b61539d8ad5bfdcc48d52fb4aa4d1e44ce2a4795d","examples/iter_table.rs":"c06fc593fcaa3d4185bbab9da30bebc655865105ccbc8d2803e0b9e4a791b95c","examples/margin.rs":"6bc26aa50d49218e86331844da30da8b177b038d95444c4e86028b7275eb0577","examples/matrix.rs":"f9729badafbee5b9a86dc66459d19ac79d0b5048a2a819e596b4eb2095ae0ef5","examples/merge_duplicates.rs":"4239dc1d6c64416f2a0af97995b34238c3f52b4a1b785d46e6824eb2b57d6449","examples/merge_duplicates_2.rs":"ffb76058b1138540aec19923f36a1954db57f3bfe1450e4f140af992ab4baee9","examples/nested_table.rs":"eb7f9446ada602a202a146c45241165bc3b4d782c9530c4b201f9af8f609a697","examples/nested_table_2.rs":"786ae17ee8fdf78b2bff47b9ed9c08ab700b866cf07c134b80364bca637cb290","examples/nested_table_3.rs":"59d387bcdaaf61dfb2e2ceaf9242b52b4638c9223a077b63a1805f77732d521b","examples/panel.rs":"bda9781934adf26bc72a0269228fc609be8b84f3af5332f151794639630b032d","examples/pool_table.rs":"92f236d52503f153a6cd524673be0423c9f5860510e057a578015d4343e4929f","examples/pool_table2.rs":"a1fb1f5f25c9652c5cb7ccd0a6cc91e5434cd47ad64bf7bdc0eda6cd3b37edd3","examples/rotate.rs":"d200fc2d93a54c3e5e486768551ff5b0e7e233b24a57c05b2b079617d50849dd","examples/settings_list.rs":"b32d18b4bc475d45e1023522bc257a6af25cc9c6bddda3e651666da183d8f690","examples/shadow.rs":"c983465cad4f5b5d562cd27a4f4e7d7e981b2511a3f8385ce324fdb53a5fa4c7","examples/span.rs":"67a3212f606b1bbc7e19cd134bb94830055ec8c43970d4004783018d7e34801a","examples/split.rs":"2bdb3054e7f1467b47a32becfc287a01773bf00aad1458a84fd2ddb7191c1caa","examples/table.rs":"4321c82f4a5267f7e117ab8aaaab351db0bba5de94f2a99aa1460a7b08d77a54","examples/table_width.rs":"25af1d10b736124d904db71dd9c0797c70862551461556ff4a8796f8a364bf20","examples/table_width_2.rs":"90a713b6cac3f6a9038790ffdb2475d587af20954689733e1b7c5d724a30b95d","src/builder/index_builder.rs":"e679f927c0fb6646c7c5588ed2f0821348cb4e746d7783fcd40ca542d8eb618d","src/builder/mod.rs":"c7dccafb2ff869ddc79710f21dba0dcb80e3e9c01f2954b36de23a13cd68e88c","src/builder/table_builder.rs":"a4b4eb726b04b4ebd1632c25cc3dc04ba757bcfb0f4f53b9432c7c94942252ac","src/grid/colored_config.rs":"71f589246b7ddc446537461edc0fdea8a5a1896caac8e27082eb4b228b19c463","src/grid/compact_multiline_config.rs":"40f5894fc0a4841edf989868b2e59a85a64080a98998fb56e10a8c5bffdb414e","src/grid/dimension/complete_dimension.rs":"af04cac77366095d7a56fd0d5c315ec3263b5602bf608a43bd50508798629c97","src/grid/dimension/complete_dimension_vec_records.rs":"46d305799377420f06a34d4451aefd176dd793048339687d8210d7f590ce927c","src/grid/dimension/const_dimension.rs":"1015fd7d26e3d1cf0530dfd191f90f2da32183761825d8c3b60b99a977a5f14b","src/grid/dimension/mod.rs":"5f462f052904ebae923dee6cd69afede677b117274c55a99acc847440f204450","src/grid/dimension/peekable_dimension.rs":"f2611401c5e13f20fb7d23c50b853443d8cc19f7e497a77e566a317688b1e214","src/grid/dimension/pool_table_dimension.rs":"6dc53142a973bea0d5e6a8f12c0dbcf0b2bd0e2be85e905a32b3f96f0889a3a6","src/grid/dimension/static_dimension.rs":"a16b657f72a5795a949528ea7444e9157aeb5e4e3f08734db8ea68789ffa4136","src/grid/mod.rs":"c4c65a0ad651a4fa39c3e43f22a579d18df9150e04d1a9d6aa1b3ff1271f19a1","src/grid/records/empty_records.rs":"5e6498bfee974ac55c9ff0da30d8d11b9b220d825854aeba9a516f87b884f5c5","src/grid/records/into_records/buf_records.rs":"3bb7179051e87ffc158f6622a6a22b630be4021a7da69e8483fbcecce3c90d6a","src/grid/records/into_records/either_string.rs":"d7707681a12f24dcfbe48db7efaa6394d18bb73735630773563a48ab936213e6","src/grid/records/into_records/limit_column_records.rs":"83fd2c5bef9a0e9411467db91990e75eafee7e4f700ed8319254f88a33156eb7","src/grid/records/into_records/limit_row_records.rs":"93aa0eb4443656cd95c90c7ed990722b5944845d1d458fccc8f560abdadf67d6","src/grid/records/into_records/mod.rs":"569338db61df7059748bd346a18f42258d2b3da63bb047825b470af76c6e4e3e","src/grid/records/into_records/truncate_records.rs":"5809a7d721d8f25213459c7e5cd04c2a1a3d19138d437b812f83eee7aa13dd12","src/grid/records/mod.rs":"6177fb0ed037ee9616d93fb1f721c5c579c9f748ff9be1dbc4b28b5eb4f5be5d","src/grid/records/records_mut.rs":"08ed1e637d4725995ed89adc35337a09988b5b35e3f6456668b57cf548bd5520","src/grid/records/resizable.rs":"21b9ad1ccb9cb7906180408620a24ee86aebe9715dc51336979cce0114e5c010","src/lib.rs":"18ab09730e26d3800070eb77c08c17996c999d95337c84455c3bc31356e0f4de","src/macros/col.rs":"54bfe1e8d498397f78266358004aa03c6b1c6308cc3e40909640124cbf474c50","src/macros/mod.rs":"188dfe6bfc1737165ac8dc2aecef6c3785aff67c850e7f5e8a4b4635a56c0af7","src/macros/row.rs":"61354760659ccde947ae3ade6e1b62a592fbd88b20a6b5e49efe158298d13046","src/settings/alignment/mod.rs":"559bf427cd4d75cb4a1efce40354c8139715320e369d133504a1dff297df00d4","src/settings/cell_option.rs":"e00ade2f6031af0e9e2166da21d6fdd3910df7ac09351ec5900f4444a444126a","src/settings/color/mod.rs":"f9aed796f6455db2aaa5178934b8b39c6e20f497007a488c57f3500d29b1d244","src/settings/concat/mod.rs":"1c6ceb585335d00a197e47694aeadf8868a71d6b386ab43bd76bee30a713a317","src/settings/disable/mod.rs":"e825c646f93ce79b582efa0aa7a2c1b20fb53b54a32cad9230edea231193f21b","src/settings/duplicate/mod.rs":"706e6ce779efcd4df18100b4dda9cec12e1d841348fab758596e85dcf6e6e514","src/settings/extract/mod.rs":"1186ddd4fb75cfe1ff05db1ec1eb777ccebd77e656ffaa1bdb8e2dc2177e70b4","src/settings/format/format_config.rs":"af71c41ff5a0a4bce0f2c4afc4d70f73222d528e7fece8acd918119c0ced02f9","src/settings/format/format_content.rs":"5800cde98baf7e0bc9a9fc685c0f29fa4ce558e7848f0d9038f120c36c627295","src/settings/format/format_positioned.rs":"e00d49ecb32095e56263cc70d4cfe318bfd54a934c0fca194fb91c4fc1c116a2","src/settings/format/mod.rs":"eaa8aebb72978abeddd38e28f208007cb299df6bb0adbd1fbcd8562c0b7578a6","src/settings/formatting/alignment_strategy.rs":"f3f6977bfd691422be5ede98ca45c0c61f70a592b2408d5af668edacadb686c7","src/settings/formatting/charset.rs":"7eff1283911eb10f687112a6ad1a00bead66397bcf03a4d80cc5e87866076b81","src/settings/formatting/justification.rs":"a4034e8dfa12c6e0b80caf627b0d7c266f52cae8701241601dafc7455ec235f7","src/settings/formatting/mod.rs":"19e144bde5dda9b02e56b82702f151ee5b02ed9ccbb1902264c652171e89074b","src/settings/formatting/tab_size.rs":"04f00810468950e3c4e8a94fee918601d98f7bc641577c99aff796b6ebf4bcb2","src/settings/formatting/trim_strategy.rs":"c9b02b07563a5c51451a305e1e4211ec3538838f8a998ebec686b33db7d6430d","src/settings/height/cell_height_increase.rs":"42934beb31f8e5b2343a46338c922ba626d64190ae257f901119ae60b825de12","src/settings/height/cell_height_limit.rs":"34ccb01f94816f5bdf7d03be23c241ef6d96c1a93d7525470ee028e0861ed3f9","src/settings/height/height_list.rs":"ac9c4f92639b0499814adb3b8b6e1f09c0445c2585a8f07d30dc65028115228c","src/settings/height/mod.rs":"6f0396b36581c33407db24fa01d45159b34dcf24de681cbbcbc14149686a7fbd","src/settings/height/table_height_increase.rs":"117f3f3feeca3250873c0d7fb87645ec8f7c6cf4d40a367226e36b8789a3c449","src/settings/height/table_height_limit.rs":"bd7d6bfe4c8be8610bd41d1c8c0d4f8325df57fc2c1fd3e5597ab330a4ebeb8c","src/settings/height/util.rs":"0b3fbdbef24785b26f1da3b14f6ea64e57d78162ef0bb324a67a96b3e0c7346a","src/settings/highlight/mod.rs":"f8849979f10e9c35bdcad394c3a9b377d6083d4c45a4e496ecbd8374edfc0256","src/settings/locator/mod.rs":"e9b60346fedfccddbfba6b4d4271acd1659327af8fd78ae042ad21d37c820c65","src/settings/margin/mod.rs":"b11066ee2ecd81f3804d7ff9c75eebfcad3538fbd4d3669c6b54da84c66abe4d","src/settings/measurement/mod.rs":"0893a1e5693ab819b4ecbb1e898302c1a4f40e52d59a120efdc54a2de6704ea5","src/settings/merge/mod.rs":"1b0f62f323623daecd9865e1b46316363423b4cfe8ced45b0ec3815e59f6a4cd","src/settings/mod.rs":"dc25f44a241b1bf5d975d401f23f3e8c2f94360c13642db82e586a215b2e362c","src/settings/modify.rs":"b757545c21823a6b14a4e5382c538d5a2b70592cee2ede1ad378c1735e12252a","src/settings/object/cell.rs":"34c33393afcbc1109939f3947ef351248c0fb0a88788812e59a3743176183f47","src/settings/object/columns.rs":"0684b9f0f5366a3a1e4b9ce0d6ba4464c1e3f0a4b6f787794c77811da9623828","src/settings/object/frame.rs":"887027d12b36744474c0665677a9ce7cbe4349d9a5bdee36c792607f6ba52e5c","src/settings/object/mod.rs":"8322b812c2b8dd3473cf53a9f7c8bd49b8b4e5cf1c25ce62dd5a22447bebb587","src/settings/object/rows.rs":"e026319a33572c76f05dd6de9164bca233f2b354852dc6d0be269e8da0142a4d","src/settings/object/segment.rs":"551d476c27de4d6626e8496d41275c5a4de5a71e25bff65c9ed0201bcbd36303","src/settings/object/util.rs":"2570c1f89a8a1642ec5d530515b0363cbae9ded0f318a66082ed868b4ff01c38","src/settings/padding/mod.rs":"052c8a00c04476b8d4752b099b0f702e5c6954632f6685c8461bd381663a73a7","src/settings/panel/footer.rs":"b1e022930a2eb41faef837a1e5dd7afa5e28a384c8910d3697b65d27a887b386","src/settings/panel/header.rs":"f0ba5b0c898d7fecba6b4c162d7a39411bf3b484a5e66de96fc886caecd1b44c","src/settings/panel/horizontal_panel.rs":"7af24d240b271df2da4aae7800efc2b1b50d228b4d8d9c6be027bb5ef0186590","src/settings/panel/mod.rs":"8db8ea0659ee44ff6aabb384b341c701074edcc866d191eb43db359ebd37bcc2","src/settings/panel/vertical_panel.rs":"081ed754dfc990475ec6fb6bafa7b6882718c1ff0a9764bd2fad4ebb14da117f","src/settings/peaker/mod.rs":"5d7f82e4dc4d27abd7adcdc3f0f898dd439927ed8dd3ac75d4417cf43b601baa","src/settings/rotate/mod.rs":"0ec15820a2d61c411331899098d83b092af0f232a1354864b3a2cdad259f28d3","src/settings/settings_list.rs":"8b4a5efc821ac1ec01c28e164b7f5540d7c87073b3f139ab125349bfd1010aed","src/settings/shadow/mod.rs":"31addebc3df7cfd33365cd15ba71e3cdccbcc03f986181a91dca5afb71dd05ff","src/settings/span/column.rs":"e9e2a2c7be1bc7862411f494a154c1b701a25a3b18ce916773e90699c7e7966d","src/settings/span/mod.rs":"5acf7cf724546179fc2a80b64207159915e506bdca3e3a229998fbea2728483c","src/settings/span/row.rs":"80fdf9d8bc73896be1d2cede82f071350ac79122ddb342dbfeff9a7189642cdc","src/settings/split/mod.rs":"4f613518d564065862a729edbe2a7cae841d1f67848dcbc7dc540bdacf9ea096","src/settings/style/border.rs":"ec1beac0c5ddbd989a8cf18023254ff903627df5ce56aa116c7f8e2277d0f884","src/settings/style/border_char.rs":"20792f88c06ef8e46164274aecf66fd874bed9730cb01222fb78d2a0687316cd","src/settings/style/border_color.rs":"7c234c08cc9e9e14522a7bcfc767880271e5fb07e659e7329dc7e56aa3b2d298","src/settings/style/border_text.rs":"d9b951e9c894e50a57c50d6b6a2e832cabae27681b26cd2d842eef99888d60c8","src/settings/style/builder.rs":"3c074039dbf2ae429f5eeb43da05ceeaee20383d3f67a02b50c8a917904adb52","src/settings/style/horizontal_line.rs":"3695a779a1296d7b595b19fbebe1e5df631d49a8a6e9b915c191571235cca157","src/settings/style/line.rs":"8d7be44ba415b093481e4f41364edb180cee23f0082b7e1b425f69f7459161a5","src/settings/style/mod.rs":"e0e09789e0fb52e82d11e066183ffcba4645384210b13d3a6c61f13dfac01603","src/settings/style/offset.rs":"de5f6b9159716f70d84361401a37118e154de719c5d02168282a5873a9f94b51","src/settings/style/raw_style.rs":"d16821bc0a107adecf66e9ef210ddadd892281510956b5a1de7c9aed5806d17f","src/settings/style/span_border_correction.rs":"70450272ecb057d1720019c2debe060ee51216027dde3a8c0c132b2bb30beb98","src/settings/style/vertical_line.rs":"f4addb95eec3b4b3984e6b86d83ec74912eec49775284a7154006a345e120380","src/settings/table_option.rs":"1d2ca1f99edf4b2780224ee46283e98f8244dc9eede949c612ccd118e811316b","src/settings/themes/colorization.rs":"6de2c67164e222589b44a9f5cb6332be6c519e0553c1dc35d2bcb9783c4af284","src/settings/themes/column_names.rs":"aeecde8f6d606a3aeff7f9b558af86fed0bd57f2308410d42cd6426f298ce38d","src/settings/themes/mod.rs":"57a1b0e79ad5148f74654bfd9c74c78a8d904fb2398482c95735b4d2f71ccec7","src/settings/width/justify.rs":"f4e5d8c33193b241fee01e20e3df696cb90c3e2c727ed15e1f96aba0d393f7bf","src/settings/width/min_width.rs":"bafaef872844f2c8026c862b1cf3e6842f965ebf004728c7334821a3448cce69","src/settings/width/mod.rs":"2c6d5eb71887fe041bc83c543fad75b39d7d430aae0397cf7453bd0281196c05","src/settings/width/truncate.rs":"a03496dbba6ba6ef330438a6a4ad70ea58e326b5aa0a61ca1f04ed88b6312f73","src/settings/width/util.rs":"8c750305ea2840dbe5810583c998d1b5e55cfd12c58fbc9c0ebc9b8850ee914e","src/settings/width/width_list.rs":"83eafd51cfdd98c532065fb6419c8d7ee0c2e9722496d5b4fd20707da6634bcd","src/settings/width/wrap.rs":"408f50024550f2e501b511ebabfb93a8047940426fd9938e28189a28898e52df","src/tabled.rs":"8004c03fd69452e6c0de42c87bbe387a96d19e7d250d98ac48ebeb6a2057c823","src/tables/compact.rs":"8e5c0c96b3b78f53f2345d1451492c6eec544fcb5db94cb8152fcdf7a31a5b39","src/tables/extended.rs":"8887b125e41083cece14b46fba441a3db1eb3bb9b80f2867db834defcfb0360e","src/tables/iter.rs":"aa68749d6cf9dc92a56c837898f7c5f2f33fc1d5c90592bd9607bddbeaecda1b","src/tables/mod.rs":"5a8bd5e46cacc644e5a8fbcc3b5f02a12e87a6d0fbe19d767228ce3b40b3eec0","src/tables/table.rs":"5443e543b0f94e14a1a0ba3d7ed86bebdd6cc432be19173fac952ecc9a86bdb6","src/tables/table_pool.rs":"7eba1a99d44e256149c9171b1aff767d344cad897f459e6ce86648fec46b977f","src/tables/util/mod.rs":"74e2f0a1e3f6107f61fd6182b70864d87ead2aa964220bbc26ed0d9ad44d4694","src/tables/util/utf8_writer.rs":"93b121191b26d2429c800a95c7860668c32252f5372a9ee2483925226d245fbf","tests/core/builder_test.rs":"899d29e769c5b7a5402a733fe9c594b5b785c005e06e7f692774b7ec3a299a0a","tests/core/compact_table.rs":"282ce7a642eeb78642753494eff5b23efac657e74972554b63a31e7fdad41a1b","tests/core/extended_table_test.rs":"619f2d502a227080e8c36960f95027394b0e501907f8746f533f06aa0d5497cd","tests/core/index_test.rs":"f07633ca9469517d5526600b670d7b5aa71a2060d673f24c5886327a32422bd3","tests/core/iter_table.rs":"4c949c834bc0d821cb7a79f7ba3b44c7d232fd8db88871650e11c5403e62f624","tests/core/mod.rs":"c3a136c3f400bad4fd55d72c50c79fd9f8fe4f542572281c0be35e915d1c34ad","tests/core/pool_table.rs":"43ebeaeede9ea3cba5d8120af2e74292a4acc8400b1d97134ca2b09374f7b8c6","tests/core/table_test.rs":"b80e2ed089338591e58215c09186287c6b76ab424f1479dc487a183886ec0a07","tests/derive/derive_test.rs":"4e75c885dac6542f3f18a30ad341c1892f206f7c35dbe5174030f01c5c558f03","tests/derive/mod.rs":"8b059e0716d6e84fd5b6381c0c05fcea51a1ccffcf6addfb7ec4fc737dff602d","tests/macros/col_row_test.rs":"7c98c49354c1f506a5b5f4c878d0a6cd9085ef86363f86278677c038b99c4405","tests/macros/mod.rs":"e11259b057ec7558a625b5eef5efabb06596af50443134ca54415b2bf5794218","tests/main.rs":"c472107cafaee31edd7379c99542e6e89a1f00af4bc0ac3e052e34bce7813786","tests/matrix/matrix.rs":"3093a8cbd7100eddf472489363e40ef7913589795917fcc194b84591a592074b","tests/matrix/matrix_list.rs":"3ee3e7e98a16f57c6db29e964ec3d6ed6972c186106b51dc554e5b0c06c8076b","tests/matrix/mod.rs":"628e26c50c75ba755c9e5f1bcbf13d06b6b7be427f485647904b74af934b0164","tests/settings/alignment_test.rs":"46442422f73673517c530ddddbf35b99e4e38eca4efb4e59ea56fc0eaa6eaaf1","tests/settings/color_test.rs":"7133eba1b7544e04914f80cab22fc71bb864d6c545ac6e215f7f44b177e25619","tests/settings/colorization.rs":"313296177cb22d8b2b96838c36abd09f5667e5faa85114f52753f5e94afa6311","tests/settings/column_names_test.rs":"36766e2bf07f292f1ce22439ec7e09613a818697e197fce3335a51b64b8f6b31","tests/settings/concat_test.rs":"b6ef3a5699cdd5b02b7c8ce243eac81f1a721cd15741d35ce0a9c2319983e14a","tests/settings/disable_test.rs":"c64a08ae99ae0e6ad8fb3b74afbb4d6fda2898bd4504cf8df63d32b33eda5880","tests/settings/duplicate_test.rs":"c22637f808340e59d8a6e199ba895d8c205e1ab0084349760f3efb7f8a465bd0","tests/settings/extract_test.rs":"d675a66ddec3366a3dccacf9e825d9ca3af2656d67e8c7ef3745f3663effb4de","tests/settings/format_test.rs":"06f3297f6a1a54a33d4b399a66bd0a18df2de96f6be0a84bd7a8fe7abb8db817","tests/settings/formatting_test.rs":"f7028d21403f6b3a40fa5eadc9b9aa61b528b6c131e2269affa2c79552d9e18c","tests/settings/height_test.rs":"d5cb6f3f637c59f2ad516773b45e2971a103287e412700a7232b656f61e383e9","tests/settings/highlingt_test.rs":"f45847c75aa66e44f97d7a1eb3d59e659d58df4ca1abe66e4c42caaf9d3810ce","tests/settings/margin_test.rs":"849a4a1bae77e8b4043668a43b7286b45040836cb884f7943c23a4ec46d886be","tests/settings/merge_test.rs":"6da97efe49db8a609bbbc04321363a5fb6aa38b8142d2537f72697e706c819e7","tests/settings/mod.rs":"ad3c772baf4f36dfb6a0951a9daa342ab964aae7744e8a5f66f07a8f0817f864","tests/settings/padding_test.rs":"ab87e0a319242d35439b12f018eafd683cf6017d2b5e9987c7e0fbd6b306feb4","tests/settings/panel_test.rs":"40e93f7e6f79391e49c5856a8554722ae0fb202907c87394b273c1efccc7a37c","tests/settings/render_settings.rs":"57ca840b94a9c5f42d492831a236592d1b1551b65166d6f88382c8ff1e80cd1e","tests/settings/rotate_test.rs":"83a34095d961618b433979454ba92bd0a00e41777326d278f9ee703f0ea77dcb","tests/settings/shadow_test.rs":"a5c4f13be24f0bc051f46520b7316a5670c5ccac77933267577068708ee7334e","tests/settings/span_test.rs":"9c4a2dea81d437327f85f5721654c415fbd705bc9d4fcf277d846ff941081729","tests/settings/split_test.rs":"a953c5e8393e061e07be4c3ba88cf8761f695d84272ddb00cb5288d7ffdfa798","tests/settings/style_test.rs":"50d629536919603410c20a7da1682956923372da34d38c7f54f5a8c0fbce85d7","tests/settings/width_test.rs":"613840392eeaddd897fac8607fdeb442c2fe2b2026b34316c84662ee1b1eb404"},"package":"4d38d39c754ae037a9bc3ca1580a985db7371cd14f1229172d1db9093feb6739"} \ No newline at end of file
diff --git a/vendor/tabled/Cargo.lock b/vendor/tabled/Cargo.lock
new file mode 100644
index 000000000..987b995c3
--- /dev/null
+++ b/vendor/tabled/Cargo.lock
@@ -0,0 +1,210 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "ansi-str"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cf4578926a981ab0ca955dc023541d19de37112bc24c1a197bd806d3d86ad1d"
+dependencies = [
+ "ansitok",
+]
+
+[[package]]
+name = "ansitok"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "220044e6a1bb31ddee4e3db724d29767f352de47445a6cd75e1a173142136c83"
+dependencies = [
+ "nom",
+ "vte",
+]
+
+[[package]]
+name = "arrayvec"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+
+[[package]]
+name = "bytecount"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "owo-colors"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
+
+[[package]]
+name = "papergrid"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2ccbe15f2b6db62f9a9871642746427e297b0ceb85f9a7f1ee5ff47d184d0c8"
+dependencies = [
+ "ansi-str",
+ "ansitok",
+ "bytecount",
+ "fnv",
+ "unicode-width",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tabled"
+version = "0.13.0"
+dependencies = [
+ "ansi-str",
+ "ansitok",
+ "owo-colors",
+ "papergrid",
+ "tabled_derive",
+ "unicode-width",
+]
+
+[[package]]
+name = "tabled_derive"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99f688a08b54f4f02f0a3c382aefdb7884d3d69609f785bd253dc033243e3fe4"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "vte"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983"
+dependencies = [
+ "arrayvec",
+ "utf8parse",
+ "vte_generate_state_changes",
+]
+
+[[package]]
+name = "vte_generate_state_changes"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
diff --git a/vendor/tabled/Cargo.toml b/vendor/tabled/Cargo.toml
new file mode 100644
index 000000000..33b699cd6
--- /dev/null
+++ b/vendor/tabled/Cargo.toml
@@ -0,0 +1,367 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+rust-version = "1.61.0"
+name = "tabled"
+version = "0.13.0"
+authors = ["Maxim Zhiburt <zhiburt@gmail.com>"]
+description = "An easy to use library for pretty print tables of Rust `struct`s and `enum`s."
+homepage = "https://github.com/zhiburt/tabled"
+documentation = "https://docs.rs/tabled"
+readme = "README.md"
+keywords = [
+ "table",
+ "print",
+ "pretty-table",
+ "format",
+ "terminal",
+]
+categories = [
+ "text-processing",
+ "visualization",
+]
+license = "MIT"
+repository = "https://github.com/zhiburt/tabled"
+
+[package.metadata.docs.rs]
+all-features = true
+rustc-args = [
+ "--cfg",
+ "docsrs",
+]
+rustdoc-args = [
+ "--cfg",
+ "docsrs",
+]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[[example]]
+name = "color"
+required-features = [
+ "color",
+ "derive",
+]
+
+[[example]]
+name = "colored_borders"
+required-features = ["derive"]
+
+[[example]]
+name = "colored_padding"
+path = "examples/colored_padding.rs"
+required-features = [
+ "color",
+ "derive",
+]
+
+[[example]]
+name = "disable"
+required-features = ["derive"]
+
+[[example]]
+name = "rename_all"
+path = "examples/derive/rename_all.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "rename"
+path = "examples/derive/rename.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "order"
+path = "examples/derive/order.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "skip"
+path = "examples/derive/skip.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "inline"
+path = "examples/derive/inline.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "inline_enum"
+path = "examples/derive/inline_enum.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "display_with"
+path = "examples/derive/display_with.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "table"
+path = "examples/table.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "builder_index"
+path = "examples/builder_index.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "concat"
+path = "examples/concat.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "custom_style"
+path = "examples/custom_style.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "extended_display"
+path = "examples/extended_display.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "extract"
+path = "examples/extract.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "format"
+path = "examples/format.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "panel"
+path = "examples/panel.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "rotate"
+path = "examples/rotate.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "shadow"
+path = "examples/shadow.rs"
+required-features = ["macros"]
+
+[[example]]
+name = "nested_table_2"
+path = "examples/nested_table_2.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "nested_table_3"
+path = "examples/nested_table_3.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "col_row_macros"
+path = "examples/col_row_macros.rs"
+required-features = [
+ "macros",
+ "derive",
+]
+
+[[example]]
+name = "merge_duplicates"
+path = "examples/merge_duplicates.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "merge_duplicates_2"
+path = "examples/merge_duplicates_2.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "hyperlink"
+path = "examples/hyperlink.rs"
+required-features = [
+ "derive",
+ "color",
+]
+
+[[example]]
+name = "highlight"
+path = "examples/highlight.rs"
+required-features = ["std"]
+
+[[example]]
+name = "highlight_color"
+path = "examples/highlight_color.rs"
+required-features = ["std"]
+
+[[example]]
+name = "border_text"
+path = "examples/border_text.rs"
+required-features = ["std"]
+
+[[example]]
+name = "span"
+path = "examples/span.rs"
+required-features = ["std"]
+
+[[example]]
+name = "nested_table"
+path = "examples/nested_table.rs"
+required-features = ["std"]
+
+[[example]]
+name = "builder"
+path = "examples/builder.rs"
+required-features = ["std"]
+
+[[example]]
+name = "table_width"
+path = "examples/table_width.rs"
+required-features = ["std"]
+
+[[example]]
+name = "table_width_2"
+path = "examples/table_width_2.rs"
+required-features = ["std"]
+
+[[example]]
+name = "height"
+path = "examples/height.rs"
+required-features = ["std"]
+
+[[example]]
+name = "margin"
+path = "examples/margin.rs"
+required-features = ["std"]
+
+[[example]]
+name = "iter_table"
+path = "examples/iter_table.rs"
+required-features = ["std"]
+
+[[example]]
+name = "matrix"
+path = "examples/matrix.rs"
+required-features = ["std"]
+
+[[example]]
+name = "formatting_settings"
+path = "examples/formatting_settings.rs"
+required-features = ["std"]
+
+[[example]]
+name = "settings_list"
+path = "examples/settings_list.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "grid_colors"
+path = "examples/grid_colors.rs"
+required-features = ["derive"]
+
+[[example]]
+name = "compact_table"
+path = "examples/compact_table.rs"
+required-features = []
+
+[[example]]
+name = "compact_table_2"
+path = "examples/compact_table_2.rs"
+required-features = []
+
+[[example]]
+name = "alphabet"
+path = "examples/alphabet.rs"
+required-features = ["std"]
+
+[[example]]
+name = "split"
+path = "examples/split.rs"
+required-features = [
+ "std",
+ "macros",
+]
+
+[[example]]
+name = "pool_table"
+path = "examples/pool_table.rs"
+required-features = ["std"]
+
+[[example]]
+name = "pool_table2"
+path = "examples/pool_table2.rs"
+required-features = ["std"]
+
+[[example]]
+name = "column_names"
+path = "examples/column_names.rs"
+required-features = [
+ "std",
+ "derive",
+]
+
+[[example]]
+name = "colorization"
+path = "examples/colorization.rs"
+required-features = [
+ "std",
+ "derive",
+]
+
+[[example]]
+name = "chess"
+path = "examples/chess.rs"
+required-features = ["std"]
+
+[dependencies.ansi-str]
+version = "0.8"
+optional = true
+
+[dependencies.ansitok]
+version = "0.2"
+optional = true
+
+[dependencies.papergrid]
+version = "0.10"
+default-features = false
+
+[dependencies.tabled_derive]
+version = "0.6"
+optional = true
+
+[dependencies.unicode-width]
+version = "0.1"
+
+[dev-dependencies.owo-colors]
+version = "3.5"
+
+[features]
+color = [
+ "papergrid/color",
+ "ansi-str",
+ "ansitok",
+ "std",
+]
+default = [
+ "derive",
+ "macros",
+]
+derive = [
+ "tabled_derive",
+ "std",
+]
+macros = ["std"]
+std = ["papergrid/std"]
+
+[badges.coveralls]
+branch = "master"
+repository = "https://github.com/zhiburt/tabled"
+service = "github"
+
+[badges.maintenance]
+status = "actively-developed"
diff --git a/vendor/tabled/LICENSE-MIT b/vendor/tabled/LICENSE-MIT
new file mode 100644
index 000000000..6077b9d68
--- /dev/null
+++ b/vendor/tabled/LICENSE-MIT
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Maxim Zhiburt
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/tabled/README.md b/vendor/tabled/README.md
new file mode 100644
index 000000000..0bcc6ef27
--- /dev/null
+++ b/vendor/tabled/README.md
@@ -0,0 +1,92 @@
+[<img alt="github" src="https://img.shields.io/badge/github-zhiburt/tabled-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/zhiburt/tabled/)
+[<img alt="crates.io" src="https://img.shields.io/crates/v/tabled.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/tabled)
+[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-tabled-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/tabled)
+[<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/zhiburt/tabled/ci.yml?branch=master&style=for-the-badge" height="20">](https://github.com/zhiburt/tabled/actions)
+[<img alt="coverage" src="https://img.shields.io/coveralls/github/zhiburt/tabled/master?style=for-the-badge" height="20">](https://coveralls.io/github/zhiburt/tabled)
+[<img alt="dependency status" src="https://deps.rs/repo/github/zhiburt/tabled/status.svg?style=for-the-badge" height="20">](https://deps.rs/repo/github/zhiburt/tabled)
+
+# tabled
+
+An easy to use library for pretty printing tables of Rust `struct`s and `enum`s.
+
+There are more examples and you can find in this [`README`](https://github.com/zhiburt/tabled/blob/master/README.md).
+
+## Usage
+
+To print a list of structs or enums as a table your types should implement the the `Tabled` trait or derive it with a `#[derive(Tabled)]` macro.
+Most of the default types implement the trait out of the box.
+
+Most of a table configuration can be found in [`tabled::settings`](https://docs.rs/tabled/latest/tabled/settings/index.html) module.
+
+```rust
+use tabled::{Table, Tabled};
+
+#[derive(Tabled)]
+struct Language {
+ name: String,
+ designed_by: String,
+ invented_year: usize,
+}
+
+impl Language {
+ fn new(name: &str, designed_by: &str, invented_year: usize) -> Self {
+ Self {
+ name: name.to_string(),
+ designed_by: designed_by.to_string(),
+ invented_year,
+ }
+ }
+}
+
+let languages = vec![
+ Language::new("C", "Dennis Ritchie", 1972),
+ Language::new("Go", "Rob Pike", 2009),
+ Language::new("Rust", "Graydon Hoare", 2010),
+ Language::new("Hare", "Drew DeVault", 2022),
+];
+
+let table = Table::new(languages).to_string();
+
+assert_eq!(
+ table,
+ "+------+----------------+---------------+\n\
+ | name | designed_by | invented_year |\n\
+ +------+----------------+---------------+\n\
+ | C | Dennis Ritchie | 1972 |\n\
+ +------+----------------+---------------+\n\
+ | Go | Rob Pike | 2009 |\n\
+ +------+----------------+---------------+\n\
+ | Rust | Graydon Hoare | 2010 |\n\
+ +------+----------------+---------------+\n\
+ | Hare | Drew DeVault | 2022 |\n\
+ +------+----------------+---------------+"
+);
+```
+
+The same example but we are building a table step by step.
+
+```rust
+use tabled::{builder::Builder, settings::Style};
+
+let mut builder = Builder::new();
+builder.push_record(["C", "Dennis Ritchie", "1972"]);
+builder.push_record(["Go", "Rob Pike", "2009"]);
+builder.push_record(["Rust", "Graydon Hoare", "2010"]);
+builder.push_record(["Hare", "Drew DeVault", "2022"]);
+
+let table = builder.build()
+ .with(Style::ascii_rounded())
+ .to_string();
+
+assert_eq!(
+ table,
+ concat!(
+ ".------------------------------.\n",
+ "| C | Dennis Ritchie | 1972 |\n",
+ "| Go | Rob Pike | 2009 |\n",
+ "| Rust | Graydon Hoare | 2010 |\n",
+ "| Hare | Drew DeVault | 2022 |\n",
+ "'------------------------------'"
+ )
+);
+``` \ No newline at end of file
diff --git a/vendor/tabled/examples/README.md b/vendor/tabled/examples/README.md
new file mode 100644
index 000000000..51c9d2e1c
--- /dev/null
+++ b/vendor/tabled/examples/README.md
@@ -0,0 +1,831 @@
+This file contains an overview of examples.
+
+- `derive` folder contains a list of examples which uses different `#[derive(Tabled)]` attributes.
+- `show` folder contains a program which uses different styles and prints the resulting table.
+- `terminal_size` folder contains a program which spreads the table to the max terminal width and max terminal height.
+ You can use which dimension to use via args `--width`, `--height` by default 2 are used.
+
+Bellow there's a list of results of running some examples.
+
+## table
+
+```
+| name | based_on | is_active | is_cool |
+|---------|----------|-----------|---------|
+| Manjaro | Arch | true | true |
+| Arch | | true | true |
+| Debian | | true | true |
+```
+
+## border_text
+
+```
+ Numbers ─┬────┬────┬────┐
+│ 0 │ 1 │ 2 │ 3 │ 4 │
+ More numbers ─┼────┼────┤
+│ 5 │ 6 │ 7 │ 8 │ 9 │
+│ 10 │ 11 │ 12 │ 13 │ 14 │
+ end. ────┴────┴────┴────┘
+```
+
+## builder_index
+
+```
+┌───────────┬─────────┬──────┬────────┐
+│ │ Manjaro │ Arch │ Debian │
+├───────────┼─────────┼──────┼────────┤
+│ based_on │ Arch │ None │ None │
+├───────────┼─────────┼──────┼────────┤
+│ is_active │ true │ true │ true │
+├───────────┼─────────┼──────┼────────┤
+│ is_cool │ true │ true │ true │
+└───────────┴─────────┴──────┴────────┘
+```
+
+## builder
+
+```
+| https://en.wikipedia.org/wiki/Ocean |
+|---------------+---------------------|
+| The terms "the ocean" or "the sea" |
+| used without specification refer to |
+| the interconnected body of salt wa |
+| ter covering the majority of the Ea |
+| rth's surface |
+| =================================== |
+| # | Ocean |
+| 0 | Atlantic |
+| 1 | Pacific |
+| 2 | Indian |
+| 3 | Southern |
+| 4 | Arctic |
+```
+
+## chess
+
+<picture>
+ <source media="(prefers-color-scheme: dark)" srcset="https://github.com/zhiburt/tabled/assets/20165848/f474bc84-5ed4-4f49-b6e9-fc204c3ecd5d">
+ <img alt="Preview" src="https://github.com/zhiburt/tabled/assets/20165848/68960bba-c5ba-4e0a-85b4-ba4f36bc0ed0">
+</picture>
+
+## col_row_macros
+
+```
++-------------------------------------------+---------------------------------------------+
+| .---------------------------------------. | ┌────────────────────┬─────┬──────────────┐ |
+| | 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 │ |
+| '---------------------------------------' | └────────────────────┴─────┴──────────────┘ |
++-------------------------------------------+---------------------------------------------+
++-------------------------------------------+
+| .---------------------------------------. |
+| | name | age | is_validated | |
+| | Jon Doe | 255 | false | |
+| | Mark Nelson | 13 | true | |
+| | Terminal Monitor | 0 | false | |
+| | Adam Blend | 17 | true | |
+| '---------------------------------------' |
++-------------------------------------------+
+| .---------------------------------------. |
+| | name | age | is_validated | |
+| | Jon Doe | 255 | false | |
+| | Mark Nelson | 13 | true | |
+| | Terminal Monitor | 0 | false | |
+| | Adam Blend | 17 | true | |
+| '---------------------------------------' |
++-------------------------------------------+
+| .---------------------------------------. |
+| | name | age | is_validated | |
+| | Jon Doe | 255 | false | |
+| | Mark Nelson | 13 | true | |
+| | Terminal Monitor | 0 | false | |
+| | Adam Blend | 17 | true | |
+| '---------------------------------------' |
++-------------------------------------------+
++-------------------------------------------------------------------------------+
+| +-------+-----+--------------+ ┌────────────────────┬─────┬──────────────┐ |
+| | 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 | |
+| '---------------------------------------' |
++-------------------------------------------------------------------------------+
+```
+
+## color
+
+<picture>
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/zhiburt/tabled/assets/assets/example-color-1-dark.png">
+ <img alt="Preview" src="https://raw.githubusercontent.com/zhiburt/tabled/assets/assets/example-color-1-light.png">
+</picture>
+
+## colored_borders
+
+<picture>
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/zhiburt/tabled/assets/assets/example-colored_borders-1-dark.png">
+ <img alt="Preview" src="https://raw.githubusercontent.com/zhiburt/tabled/assets/assets/example-colored_borders-1-light.png">
+</picture>
+
+## colored_padding
+
+<picture>
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/zhiburt/tabled/assets/assets/example-padding_color-1-dark.png">
+ <img alt="Preview" src="https://raw.githubusercontent.com/zhiburt/tabled/assets/assets/example-padding_color-1-light.png">
+</picture>
+
+## colorization
+
+<picture>
+ <source media="(prefers-color-scheme: dark)" srcset="https://github.com/zhiburt/tabled/assets/20165848/95ef3c5b-a86d-425c-b95a-b689b61734c5">
+ <img alt="Preview" src="https://github.com/zhiburt/tabled/assets/20165848/b6b8af0a-7c1f-4656-b880-c8ecd130c5be">
+</picture>
+
+## column_names
+
+
+<picture>
+ <source media="(prefers-color-scheme: dark)" srcset="https://github.com/zhiburt/tabled/assets/20165848/82d8e285-3c1b-4dd2-a1f4-45fd49f0dabe">
+ <img alt="Preview" src="https://github.com/zhiburt/tabled/assets/20165848/13054c74-0fae-4df1-87e8-60d498d734e4">
+</picture>
+
+## concat
+
+```
+ temperature_c wind_ms latitude longitude
+ 16 3000 111.111 333.333
+ -20 300 5.111 7282.1
+ 40 100 0 0
+ 0 0
+```
+
+## custom_style
+
+```
+┌────────────────────┬─────────────────────────────────┐
+│ name │ first_release developer │
+├────────────────────┼─────────────────────────────────┤
+│ Sublime Text 3 │ 2008 Sublime HQ │
+│ Visual Studio Code │ 2015 Microsoft │
+│ Notepad++ │ 2003 Don Ho │
+│ GNU Emacs │ 1984 Richard Stallman │
+│ Neovim │ 2015 Vim community │
+└────────────────────┴─────────────────────────────────┘
+```
+
+## disable
+
+```
+###########
+# name # based_on | is_cool |
+###########----------|---------|
+# Debian # | true |
+###########
+# Arch # | true |
+###########
+# Manjaro # Arch | true |
+###########
+```
+
+## expanded_display
+
+```
+-[ RECORD 0 ]------
+name | Manjaro
+based_on | Arch
+is_active | true
+is_cool | true
+-[ RECORD 1 ]------
+name | Arch
+based_on |
+is_active | true
+is_cool | true
+-[ RECORD 2 ]------
+name | Debian
+based_on |
+is_active | true
+is_cool | true
+```
+
+## extract
+
+```
+┌───────────────┬───────────────────────────┬──────────────────┬────────────────────┐
+│ artist │ name │ released │ level_of_greatness │
+├───────────────┼───────────────────────────┼──────────────────┼────────────────────┤
+│ Pink Floyd │ The Dark Side of the Moon │ 01 March 1973 │ Unparalleled │
+├───────────────┼───────────────────────────┼──────────────────┼────────────────────┤
+│ Fleetwood Mac │ Rumours │ 04 February 1977 │ Outstanding │
+├───────────────┼───────────────────────────┼──────────────────┼────────────────────┤
+│ Led Zeppelin │ Led Zeppelin IV │ 08 November 1971 │ Supreme │
+└───────────────┴───────────────────────────┴──────────────────┴────────────────────┘
+
+┼───────────────────────────┼──────────────────┼──────────────┤
+│ The Dark Side of the Moon │ 01 March 1973 │ Unparalleled │
+┼───────────────────────────┼──────────────────┼──────────────┤
+│ Rumours │ 04 February 1977 │ Outstanding │
+┼───────────────────────────┼──────────────────┼──────────────┤
+
+┌───────────────────────────┬──────────────────┬───────────────┐
+│ The Dark Side of the Moon │ 01 March 1973 │ Unparalleled │
+├───────────────────────────┼──────────────────┼───────────────┤
+│ Rumours │ 04 February 1977 │ +Outstanding+ │
+└───────────────────────────┴──────────────────┴───────────────┘
+```
+
+## format
+
+```
+ 0 | 1 | 2
+---------------------------------------------+--------------------------------+-------------------------
+ 8ae4e8957caeaa467acbce963701e227af00a1c7... | bypass open-source transmitter | index neural panel
+ 48c76de71bd685486d97dc8f4f05aa6fcc0c3f86... | program online alarm | copy bluetooth card
+ 6ffc2a2796229fc7bf59471ad907f58b897005d0... | CSV | reboot mobile capacitor
+```
+
+## formatting_settings
+
+```
+╭───────────────────╮
+│ &str │
+├───────────────────┤
+│ │
+│ [ │
+│ "foo", │
+│ { │
+│ "bar": 1, │
+│ "baz": [ │
+│ 2, │
+│ 3 │
+│ ] │
+│ } │
+│ ] │
+╰───────────────────╯
+
+╭───────────────────╮
+│ &str │
+├───────────────────┤
+│ │
+│ [ │
+│ "foo", │
+│ { │
+│ "bar": 1, │
+│ "baz": [ │
+│ 2, │
+│ 3 │
+│ ] │
+│ } │
+│ ] │
+╰───────────────────╯
+
+╭───────────────────╮
+│ &str │
+├───────────────────┤
+│ [ │
+│ "foo", │
+│ { │
+│ "bar": 1, │
+│ "baz": [ │
+│ 2, │
+│ 3 │
+│ ] │
+│ } │
+│ ] │
+│ │
+╰───────────────────╯
+```
+
+## highlight
+
+```
+*************
+* 0 │ 1 │ 2 *
+*****───*****
+│ A * B * C │
+├───*───*───┤
+│ D * E * F │
+├───*───*───┤
+│ G * H * I │
+└───*****───┘
+```
+
+## margin
+
+```
+vvvvvvvvvvvvvvvvvv
+vvvvvvvvvvvvvvvvvv
+<<<<=== === ===>>>
+<<<< 0 1 2 >>>
+<<<<=== === ===>>>
+<<<< A B C >>>
+<<<< D E F >>>
+<<<< G H I >>>
+<<<<=== === ===>>>
+^^^^^^^^^^^^^^^^^^
+```
+
+## nested_table
+
+```
++-----------------------------------------------+
+| +---------------------+ |
+| | Animal | |
+| +---------------------+ |
+| | +-----------------+ | |
+| | | +age: Int | | |
+| | | +gender: String | | |
+| | +-----------------+ | |
+| | +-----------------+ | |
+| | | +isMammal() | | |
+| | | +mate() | | |
+| | +-----------------+ | |
+| +---------------------+ |
+| ▲ |
+| | |
+| | |
+| +-----------------------------------+ |
+| | Duck | |
+| +-----------------------------------+ |
+| | +-------------------------------+ | |
+| | | +beakColor: String = "yellow" | | |
+| | +-------------------------------+ | |
+| | +-------------------------------+ | |
+| | | +swim() | | |
+| | | +quack() | | |
+| | +-------------------------------+ | |
+| +-----------------------------------+ |
++-----------------------------------------------+
+```
+
+## nested_table_2
+
+```
+┌───────┬─────────────────────────────────────────────────┬──────────────────────────────────────────────┐
+│ name │ main_os │ switch_os │
+├───────┼─────────────────────────────────────────────────┼──────────────────────────────────────────────┤
+│ Azure │ ╔═════════╦═════════════╦═══════════╦═════════╗ │ ╔═════════╦══════════╦═══════════╦═════════╗ │
+│ │ ║ name ║ based_on ║ is_active ║ is_cool ║ │ ║ name ║ based_on ║ is_active ║ is_cool ║ │
+│ │ ╠═════════╬═════════════╬═══════════╬═════════╣ │ ╠═════════╬══════════╬═══════════╬═════════╣ │
+│ │ ║ Windows ║ Independent ║ true ║ true ║ │ ║ Manjaro ║ Arch ║ true ║ true ║ │
+│ │ ╚═════════╩═════════════╩═══════════╩═════════╝ │ ╚═════════╩══════════╩═══════════╩═════════╝ │
+├───────┼─────────────────────────────────────────────────┼──────────────────────────────────────────────┤
+│ AWS │ ╔════════╦═════════════╦═══════════╦═════════╗ │ ╔══════╦═════════════╦═══════════╦═════════╗ │
+│ │ ║ name ║ based_on ║ is_active ║ is_cool ║ │ ║ name ║ based_on ║ is_active ║ is_cool ║ │
+│ │ ╠════════╬═════════════╬═══════════╬═════════╣ │ ╠══════╬═════════════╬═══════════╬═════════╣ │
+│ │ ║ Debian ║ Independent ║ true ║ true ║ │ ║ Arch ║ Independent ║ true ║ true ║ │
+│ │ ╚════════╩═════════════╩═══════════╩═════════╝ │ ╚══════╩═════════════╩═══════════╩═════════╝ │
+├───────┼─────────────────────────────────────────────────┼──────────────────────────────────────────────┤
+│ GCP │ ╔════════╦═════════════╦═══════════╦═════════╗ │ ╔══════╦═════════════╦═══════════╦═════════╗ │
+│ │ ║ name ║ based_on ║ is_active ║ is_cool ║ │ ║ name ║ based_on ║ is_active ║ is_cool ║ │
+│ │ ╠════════╬═════════════╬═══════════╬═════════╣ │ ╠══════╬═════════════╬═══════════╬═════════╣ │
+│ │ ║ Debian ║ Independent ║ true ║ true ║ │ ║ Arch ║ Independent ║ true ║ true ║ │
+│ │ ╚════════╩═════════════╩═══════════╩═════════╝ │ ╚══════╩═════════════╩═══════════╩═════════╝ │
+└───────┴─────────────────────────────────────────────────┴──────────────────────────────────────────────┘
+```
+
+## nested_table_3
+
+```
+*************************************************
+* Thank You *
+*************************************************
+| +------------+------------------------------+ |
+| | Contributors | |
+| +------------+------------------------------+ |
+| | author | profile | |
+| +------------+------------------------------+ |
+| | kozmod | https:/github.com/kozmod | |
+| +------------+------------------------------+ |
+| | IsaacCloos | https:/github.com/IsaacCloos | |
+| +------------+------------------------------+ |
+| +-----------+-----------------------------+ |
+| | Issuers | |
+| +-----------+-----------------------------+ |
+| | author | profile | |
+| +-----------+-----------------------------+ |
+| | aharpervc | https:/github.com/aharpervc | |
+| +-----------+-----------------------------+ |
++-----------------------------------------------+
+```
+
+## panel
+
+```
+┌───┬────────────────────────────────────────────────────────────────────┬───┐
+│ S │ Tabled Releases │ S │
+│ o │ │ o │
+│ m │ │ m │
+│ e │ │ e │
+│ ├─────────┬────────────────┬───────────┬─────────────────────────────┤ │
+│ t │ version │ published_date │ is_active │ major_feature │ t │
+│ e │ │ │ │ │ e │
+│ x ├─────────┼────────────────┼───────────┼─────────────────────────────┤ x │
+│ t │ 0.2.1 │ 2021-06-23 │ true │ #[header(inline)] attribute │ t │
+│ │ │ │ │ │ │
+│ g ├─────────┼────────────────┼───────────┼─────────────────────────────┤ g │
+│ o │ 0.2.0 │ 2021-06-19 │ false │ API changes │ o │
+│ e │ │ │ │ │ e │
+│ s ├─────────┼────────────────┼───────────┼─────────────────────────────┤ s │
+│ │ 0.1.4 │ 2021-06-07 │ false │ display_with attribute │ │
+│ h │ │ │ │ │ h │
+│ e ├─────────┴────────────────┴───────────┴─────────────────────────────┤ e │
+│ r │ N - 3 │ r │
+│ e │ │ e │
+└───┴────────────────────────────────────────────────────────────────────┴───┘
+```
+
+## print_matrix
+
+```
+┌────┬────┬────┬────┬────┬────┬────┬────┬────┬─────┐
+│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
+├────┼────┼────┼────┼────┼────┼────┼────┼────┼─────┤
+│ 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 │
+└────┴────┴────┴────┴────┴────┴────┴────┴────┴─────┘
+```
+
+## rotate
+
+```
++--------------+------------------------+---------------------------+--------------------------+
+| link | https://getfedora.org/ | https://www.opensuse.org/ | https://endeavouros.com/ |
++--------------+------------------------+---------------------------+--------------------------+
+| destribution | Fedora | OpenSUSE | Endeavouros |
++--------------+------------------------+---------------------------+--------------------------+
+| id | 0 | 2 | 3 |
++--------------+------------------------+---------------------------+--------------------------+
+```
+
+## shadow
+
+```
+┌──┬┐ ╔══╦╗ ╓──┬╖ ╒═╤╕
+│ ││ ║ ║║ ║ │║ │ ││
+├──┼┤ ╠══╬╣ ╟──┼╢ ╞═╪╡
+└──┴┘ ╚══╩╝ ╙──┴╜ ╘═╧╛
+┌──────────────────┐
+│ ╔═══╗ Some text │▒▒
+│ ╚═╦═╝ In the box│▒▒
+╞═╤══╩══╤══════════╡▒▒
+│ ├──┬──┤ │▒▒
+│ └──┴──┘ │▒▒
+└──────────────────┘▒▒
+ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
+ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
+```
+
+## span
+
+```
+┌───────────────────────────────────────────────────────────────────────────────┐
+│ span all 5 columns │
+├───────────────────────────────────────────────────────────────┬───────────────┤
+│ span 4 columns │ just 1 column │
+├───────────────────────────────┬───────────────┬───────────────┼───────────────┤
+│ span 2 columns │ just 1 column │ │ │
+├───────────────┬───────────────┴───────────────┤ just 1 column │ │
+│ just 1 column │ span 2 columns │ span │ just 1 column │
+│ │ span │ 3 │ span │
+├───────────────┤ 2 │ columns │ 4 │
+│ just 1 column │ columns │ │ columns │
+├───────────────┼───────────────┬───────────────┼───────────────┤ │
+│ just 1 column │ just 1 column │ just 1 column │ just 1 column │ │
+└───────────────┴───────────────┴───────────────┴───────────────┴───────────────┘
+```
+
+## table_width
+
+```
+| 0 | 1 |
+|------------------|-----------|
+| Hello World!!! | 3.3.22.2 |
+| Guten Morgen | 1.1.1.1 |
+| Добры вечар | 127.0.0.1 |
+| Bonjour le monde | |
+| Ciao mondo | |
+
+| 0 | 1 |
+|------------|-----|
+| Hello W... | ... |
+| Guten M... | ... |
+| Добры в... | ... |
+| Bonjour... | |
+| Ciao mondo | |
+
+| 0 | 1 |
+|-------|-----|
+| Hello | ... |
+| W... | |
+| Guten | ... |
+| M... | |
+| Добры | ... |
+| в... | |
+| Bonjo | |
+| ur... | |
+| Ciao | |
+| mondo | |
+
+| 0 | 1 |
+|---------------|------------|
+| Hello | ... |
+| W... | |
+| Guten | ... |
+| M... | |
+| Добры | ... |
+| в... | |
+| Bonjo | |
+| ur... | |
+| Ciao | |
+| mondo | |
+```
+
+## table_width_2
+
+```
+.----------------------------------------.
+| usize | &str |
+| 0 | # Changelog |
+| 1 | All notable changes to this |
+| | projectwill be documented in |
+| | thisfile. |
+| 2 | The format is based on [Keep a |
+| | Changelog](https://keepachange |
+| | log.com/en/1.0.0/), |
+| 3 | and this project adheres to |
+| | [SemanticVersioning](https://s |
+| | emver.org/spec/v2.0.0.html). |
+| 4 | ## Unreleased |
+'-------+--------------------------------'
+```
+
+## alphabet
+
+```
++---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+| 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 |
++---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+```
+
+## compact_table
+
+```
+| Debian | | true |
+|-------|-----|-----|
+| Arch | | true |
+| Manjaro | Arch | true |
+```
+
+## compact_table_2
+
+```
+ Debian | 1.1.1.1 | true
+---------+-----------+------
+ Arch | 127.1.1.1 | true
+ Manjaro | Arch | true
+```
+
+## extended_display
+
+```
+-[ RECORD 0 ]------
+name | Manjaro
+based_on | Arch
+is_active | true
+is_cool | true
+-[ RECORD 1 ]------
+name | Arch
+based_on |
+is_active | true
+is_cool | true
+-[ RECORD 2 ]------
+name | Debian
+based_on |
+is_active | true
+is_cool | true
+```
+
+## height
+
+```
+Table
+
+| &str | i32 |
+|-------------|-----|
+| Multi | 123 |
+| line | |
+| string | |
+| Single line | 234 |
+
+Table increase height to 10
+
+| &str | i32 |
+| | |
+| | |
+|-------------|-----|
+| Multi | 123 |
+| line | |
+| string | |
+| | |
+| Single line | 234 |
+| | |
+
+Table decrease height to 4
+
+| &str | i32 |
+|-------------|-----|
+| Multi | 123 |
+| Single line | 234 |
+Table decrease height to 0
+
+|--|--|
+```
+
+## iter_table
+
+```
+.----------------------------------------------------------------------------------------.
+| 0 | ok | //! The example can be run by this command |
+| 1 | ok | //! `cargo run --example iter_table` |
+| 2 | ok | |
+| 3 | ok | use std::io::BufRead; |
+| 4 | ok | |
+| 5 | ok | use tabled::{settings::Style, tables::IterTable}; |
+| 6 | ok | |
+| 7 | ok | fn main() { |
+| 8 | ok | let path = file!(); |
+| 9 | ok | let file = std::fs::File::open(path).unwrap(); |
+| 10 | ok | let reader = std::io::BufReader::new(file); |
+| 11 | ok | let iterator = reader.lines().enumerate().map(|(i, line)| match line { |
+| 12 | ok | Ok(line) => [i.to_string(), String::from("ok"), line], |
+| 13 | ok | Err(err) => [i.to_string(), String::from("error"), err.to_string()], |
+| 14 | ok | }); |
+| 15 | ok | |
+| 16 | ok | let table = IterTable::new(iterator).with(Style::ascii_rounded()); |
+| 17 | ok | |
+| 18 | ok | table.build(std::io::stdout()).unwrap(); |
+| 19 | ok | println!() |
+| 20 | ok | } |
+'----------------------------------------------------------------------------------------'
+```
+
+## margin
+
+```
+vvvvvvvvvvvvvvvvvv
+vvvvvvvvvvvvvvvvvv
+<<<<=== === ===>>>
+<<<< 0 1 2 >>>
+<<<<=== === ===>>>
+<<<< A B C >>>
+<<<< D E F >>>
+<<<< G H I >>>
+<<<<=== === ===>>>
+^^^^^^^^^^^^^^^^^^
+```
+
+## matrix
+
+```
+┌────┬────┬────┬────┬────┬────┬────┬────┬────┬─────┐
+│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
+├────┼────┼────┼────┼────┼────┼────┼────┼────┼─────┤
+│ 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 │
+└────┴────┴────┴────┴────┴────┴────┴────┴────┴─────┘
+```
+
+## merge_duplicates
+
+```
+┌────────────┬─────────┬────────┐
+│ db │ table │ total │
+├────────────┼─────────┼────────┤
+│ database_1 │ table_1 │ 10712 │
+│ ├─────────┼────────┤
+│ │ table_2 │ 57 │
+│ ├─────────┤ │
+│ │ table_3 │ │
+├────────────┼─────────┼────────┤
+│ database_2 │ table_1 │ 72 │
+│ ├─────────┼────────┤
+│ │ table_2 │ 75 │
+├────────────┼─────────┼────────┤
+│ database_3 │ table_1 │ 20 │
+│ ├─────────┼────────┤
+│ │ table_2 │ 21339 │
+│ ├─────────┼────────┤
+│ │ table_3 │ 141723 │
+└────────────┴─────────┴────────┘
+```
+
+## merge_duplicates_2
+
+```
+╭───────────┬───────────────────────────────────────────────────────────────────────────╮
+│ │ 0 1 2 3 4 5 6 7 │
+├───────────┼───────────────────────────────────────────────────────────────────────────┤
+│ db │ database_1 database_2 database_3 │
+│ origin_db │ database_1 database_3 │
+│ table │ table_1 table_2 table_3 table_1 table_2 table_1 table_2 table_3 │
+│ total │ 10712 57 72 75 20 21339 141723 │
+╰───────────┴───────────────────────────────────────────────────────────────────────────╯
+```
+
+## settings_list
+
+```
++----------------------+-----------------+--------------------+
+| name | first_release | developer |
++----------------------+-----------------+--------------------+
+| Sublime Text 3 | 2008 | Sublime HQ |
++----------------------+-----------------+--------------------+
+| Visual Studio Code | 2015 | Microsoft |
++----------------------+-----------------+--------------------+
+| Notepad++ | 2003 | Don Ho |
++----------------------+-----------------+--------------------+
+| GNU Emacs | 1984 | Richard Stallman |
++----------------------+-----------------+--------------------+
+| Neovim | 2015 | Vim community |
++----------------------+-----------------+--------------------+
+```
+
+## split
+
+```
+┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
+│ 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 │ │ a │ b │ │ a │ b │ │ a │ y │ b │ z │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │
+├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ ├───┼───┤ ├───┼───┤ ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤
+│ m │ n │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ │ c │ d │ │ m │ n │ │ m │ │ n │ │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │
+├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ ├───┼───┤ ├───┼───┤ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
+│ y │ z │ │ │ │ │ │ │ │ │ │ │ │ e │ f │ │ y │ z │ ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
+└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ ├───┼───┤ ├───┼───┤ │ a │ b │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ y │ z │
+ │ g │ h │ │ c │ d │ ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤
+ ├───┼───┤ ├───┼───┤ │ m │ n │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ │ │
+ │ i │ j │ │ o │ p │ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
+ ├───┼───┤ ├───┼───┤
+ │ k │ l │ │ e │ f │
+ ├───┼───┤ ├───┼───┤
+ │ m │ n │ │ q │ r │
+ ├───┼───┤ ├───┼───┤
+ │ o │ p │ │ g │ h │
+ ├───┼───┤ ├───┼───┤
+ │ q │ r │ │ s │ t │
+ ├───┼───┤ ├───┼───┤
+ │ s │ t │ │ i │ j │
+ ├───┼───┤ ├───┼───┤
+ │ u │ v │ │ u │ v │
+ ├───┼───┤ ├───┼───┤
+ │ w │ x │ │ k │ l │
+ ├───┼───┤ ├───┼───┤
+ │ y │ z │ │ w │ x │
+ └───┴───┘ └───┴───┘
+``` \ No newline at end of file
diff --git a/vendor/tabled/examples/alphabet.rs b/vendor/tabled/examples/alphabet.rs
new file mode 100644
index 000000000..5cc8250ad
--- /dev/null
+++ b/vendor/tabled/examples/alphabet.rs
@@ -0,0 +1,13 @@
+//! This example demonstrates instantiating a [`Table`] from an [`IntoIterator`] compliant object.
+//!
+//! * Note how [`Range`] [expression syntax](https://doc.rust-lang.org/reference/expressions/range-expr.html)
+//! is used to idiomatically represent the English alphabet.
+
+use std::iter::FromIterator;
+
+use tabled::Table;
+
+fn main() {
+ let table = Table::from_iter(['a'..='z']);
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/border_text.rs b/vendor/tabled/examples/border_text.rs
new file mode 100644
index 000000000..bbdb7673e
--- /dev/null
+++ b/vendor/tabled/examples/border_text.rs
@@ -0,0 +1,44 @@
+//! This example demonstrates inserting text into the borders
+//! of a [`Table`] with [`BorderText`]; a powerful labeling tool.
+//!
+//! * [`BorderText`] currently supports:
+//! * Horizontal border placement
+//! * Placement starting column offset
+//! * Text colorization
+//!
+//! * Note how the flexibility of [`Style`] is utilized
+//! to remove horizontal borders from the table entirely,
+//! and then granularly reinserts one for a highly customized
+//! visualization.
+//!
+//! * Note how the [`Rows`] utility object is used to idiomatically
+//! reference the first and last rows of a [`Table`] without writing
+//! the necessary logic by hand.
+//!
+//! * 🚀 Combining several easy-to-use tools,
+//! to create unique data representations is what makes [`tabled`] great!
+
+use tabled::{
+ settings::{
+ object::Rows,
+ style::{BorderText, HorizontalLine, Style},
+ },
+ Table,
+};
+
+fn main() {
+ let data = [[5, 6, 7, 8, 9], [10, 11, 12, 13, 14]];
+
+ let table = Table::new(data)
+ .with(
+ Style::modern()
+ .remove_horizontal()
+ .horizontals([HorizontalLine::new(1, Style::modern().get_horizontal())]),
+ )
+ .with(BorderText::new(" Numbers ").horizontal(Rows::first()))
+ .with(BorderText::new(" More numbers ").horizontal(1))
+ .with(BorderText::new(" end. ").horizontal(Rows::last()))
+ .to_string();
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/builder.rs b/vendor/tabled/examples/builder.rs
new file mode 100644
index 000000000..6baf29c24
--- /dev/null
+++ b/vendor/tabled/examples/builder.rs
@@ -0,0 +1,38 @@
+//! This example demonstrates an alternative method for creating a [`Table`].
+//! [`Builder`] is an efficient implementation of the [builder design pattern](https://en.wikipedia.org/wiki/Builder_pattern).
+//!
+//! > The intent of the Builder design pattern is to separate the construction of a complex object from its representation.
+//! > -- <cite>Wikipedia</cite>
+//!
+//! * Note how [Builder] can be used to define a table's shape manually
+//! and can be populated through iteration if it is mutable. This flexibility
+//! is useful when you don't have direct control over the datasets you intend to [table](tabled).
+
+use tabled::{
+ builder::Builder,
+ settings::{object::Rows, Modify, Panel, Style, Width},
+};
+
+fn main() {
+ let message = r#"The terms "the ocean" or "the sea" used without specification refer to the interconnected body of salt water covering the majority of the Earth's surface"#;
+ let link = r#"https://en.wikipedia.org/wiki/Ocean"#;
+
+ let oceans = ["Atlantic", "Pacific", "Indian", "Southern", "Arctic"];
+
+ let mut builder = Builder::default();
+ builder.set_header(["#", "Ocean"]);
+ for (i, ocean) in oceans.iter().enumerate() {
+ builder.push_record([i.to_string(), ocean.to_string()]);
+ }
+
+ let table = builder
+ .build()
+ .with(Panel::header(message))
+ .with(Panel::header(link))
+ .with(Panel::horizontal(2, "=".repeat(link.len())))
+ .with(Modify::new(Rows::single(1)).with(Width::wrap(link.len())))
+ .with(Style::markdown())
+ .to_string();
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/builder_index.rs b/vendor/tabled/examples/builder_index.rs
new file mode 100644
index 000000000..9a53eb566
--- /dev/null
+++ b/vendor/tabled/examples/builder_index.rs
@@ -0,0 +1,50 @@
+//! This example demonstrates evolving the standard [`Builder`] to an [`IndexBuilder`],
+//! and then manipulating the constructing table with a newly prepended index column.
+//!
+//! * An [`IndexBuilder`] is capable of several useful manipulations, including:
+//! * Giving the new index column a name
+//! * Transposing the index column around a table
+//! * Choosing a location for the new index column besides 0; the default
+//!
+//! * Note that like with any builder pattern the [`IndexBuilder::build()`] function
+//! is necessary to produce a displayable [`Table`].
+
+use tabled::{settings::Style, Table, Tabled};
+
+#[derive(Tabled)]
+struct Distribution {
+ name: &'static str,
+ based_on: &'static str,
+ is_active: bool,
+ is_cool: bool,
+}
+
+impl Distribution {
+ fn new(name: &'static str, based_on: &'static str, is_active: bool, is_cool: bool) -> Self {
+ Self {
+ name,
+ based_on,
+ is_active,
+ is_cool,
+ }
+ }
+}
+
+fn main() {
+ let data = [
+ Distribution::new("Manjaro", "Arch", true, true),
+ Distribution::new("Arch", "None", true, true),
+ Distribution::new("Debian", "None", true, true),
+ ];
+
+ let mut table = Table::builder(data)
+ .index()
+ .column(0)
+ .name(None)
+ .transpose()
+ .build();
+
+ table.with(Style::modern());
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/chess.rs b/vendor/tabled/examples/chess.rs
new file mode 100644
index 000000000..db83218c8
--- /dev/null
+++ b/vendor/tabled/examples/chess.rs
@@ -0,0 +1,37 @@
+//! This example demonstrates using the [`Color`] [setting](tabled::settings) to
+//! stylize text, backgrounds, and borders.
+//!
+//! * 🚩 This example requires the `color` feature.
+//!
+//! * Note how [`Format::content()`] is used to break out [`CellOption`]
+//! specifications. This is helpful for organizing extensive [`Table`] configurations.
+
+use std::iter::FromIterator;
+
+use tabled::{
+ builder::Builder,
+ settings::{style::Style, themes::Colorization, Color},
+};
+
+fn main() {
+ let board = [
+ ["♜", "♞", "♝", "♛", "♚", "♝", "♞", "♜"],
+ ["♟", "♟", "♟", "♟", "♟", "♟", "♟", "♟"],
+ [" ", " ", " ", " ", " ", " ", " ", " "],
+ [" ", " ", " ", " ", " ", " ", " ", " "],
+ [" ", " ", " ", " ", " ", " ", " ", " "],
+ [" ", " ", " ", " ", " ", " ", " ", " "],
+ ["♙", "♙", "♙", "♙", "♙", "♙", "♙", "♙"],
+ ["♖", "♘", "♗", "♕", "♔", "♗", "♘", "♖"],
+ ];
+
+ let color_white = Color::BG_WHITE | Color::FG_BLACK;
+ let color_black = Color::FG_WHITE | Color::BG_BLACK;
+
+ let mut table = Builder::from_iter(board).build();
+ table
+ .with(Style::empty())
+ .with(Colorization::chess(color_white, color_black));
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/col_row_macros.rs b/vendor/tabled/examples/col_row_macros.rs
new file mode 100644
index 000000000..1a676eb67
--- /dev/null
+++ b/vendor/tabled/examples/col_row_macros.rs
@@ -0,0 +1,62 @@
+//! This example demonstrates using the [`col!`] and [`row!`] macros to easily
+//! organize multiple tables together into a single, new [`Table`] display.
+//!
+//! * 🚩 This example requires the `macros` feature.
+//!
+//! * Note how both macros can be used in combination to layer
+//! several table arrangements together.
+//!
+//! * Note how [`col!`] and [`row!`] support idiomatic argument duplication
+//! with the familiar `[T; N]` syntax.
+
+use tabled::{
+ col, row,
+ settings::{Alignment, Style},
+ Table, Tabled,
+};
+
+#[derive(Tabled)]
+struct Person {
+ name: String,
+ age: u8,
+ is_validated: bool,
+}
+
+impl Person {
+ fn new(name: &str, age: u8, is_validated: bool) -> Self {
+ Self {
+ name: name.into(),
+ age,
+ is_validated,
+ }
+ }
+}
+
+fn main() {
+ let validated = [Person::new("Sam", 31, true), Person::new("Sarah", 26, true)];
+
+ let not_validated = [
+ Person::new("Jack Black", 51, false),
+ Person::new("Michelle Goldstein", 44, true),
+ ];
+
+ let unsure = [
+ Person::new("Jon Doe", 255, false),
+ Person::new("Mark Nelson", 13, true),
+ Person::new("Terminal Monitor", 0, false),
+ Person::new("Adam Blend", 17, true),
+ ];
+
+ let table_a = Table::new(&validated).with(Style::ascii()).to_string();
+ let table_b = Table::new(&not_validated).with(Style::modern()).to_string();
+ let table_c = Table::new(&unsure).with(Style::ascii_rounded()).to_string();
+
+ let row_table = row![table_c, table_b];
+
+ let col_table = col![table_c; 3];
+
+ let mut row_col_table = col![row![table_a, table_b].with(Style::empty()), table_c];
+ row_col_table.with(Alignment::center());
+
+ println!("{row_table}\n{col_table}\n{row_col_table}",);
+}
diff --git a/vendor/tabled/examples/color.rs b/vendor/tabled/examples/color.rs
new file mode 100644
index 000000000..b7064b306
--- /dev/null
+++ b/vendor/tabled/examples/color.rs
@@ -0,0 +1,74 @@
+//! This example demonstrates using the [`Color`] [setting](tabled::settings) to
+//! stylize text, backgrounds, and borders.
+//!
+//! * 🚩 This example requires the `color` feature.
+//!
+//! * Note how [`Format::content()`] is used to break out [`CellOption`]
+//! specifications. This is helpful for organizing extensive [`Table`] configurations.
+
+use std::convert::TryFrom;
+
+use owo_colors::OwoColorize;
+
+use tabled::{
+ settings::{
+ object::{Columns, Rows},
+ style::{BorderColor, Style},
+ Color, Format, Modify,
+ },
+ Table, Tabled,
+};
+
+#[derive(Tabled)]
+struct Bsd {
+ distribution: &'static str,
+ year_of_first_release: usize,
+ is_active: bool,
+}
+
+impl Bsd {
+ fn new(distribution: &'static str, year_of_first_release: usize, is_active: bool) -> Self {
+ Self {
+ distribution,
+ year_of_first_release,
+ is_active,
+ }
+ }
+}
+
+fn main() {
+ let data = vec![
+ Bsd::new("BSD", 1978, false),
+ Bsd::new("SunOS", 1982, false),
+ Bsd::new("NetBSD", 1993, true),
+ Bsd::new("FreeBSD", 1993, true),
+ Bsd::new("OpenBSD", 1995, true),
+ ];
+
+ let red = Format::content(|s| s.red().on_bright_white().to_string());
+ let blue = Format::content(|s| s.blue().to_string());
+ let green = Format::content(|s| s.green().to_string());
+
+ let color_red = Color::try_from(' '.red().to_string()).unwrap();
+ let color_purple = Color::try_from(' '.purple().to_string()).unwrap();
+
+ let yellow_color = Color::try_from(' '.yellow().to_string()).unwrap();
+
+ let first_row_style = Modify::new(Rows::first()).with(
+ BorderColor::default()
+ .bottom(color_red)
+ .corner_bottom_left(color_purple.clone())
+ .corner_bottom_right(color_purple),
+ );
+
+ let mut table = Table::new(data);
+ table
+ .with(Style::psql())
+ .with(yellow_color)
+ .with(first_row_style)
+ .with(Modify::new(Columns::single(0)).with(red))
+ .with(Modify::new(Columns::single(1)).with(green))
+ .with(Modify::new(Columns::single(2)).with(blue));
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/colored_borders.rs b/vendor/tabled/examples/colored_borders.rs
new file mode 100644
index 000000000..0bbdb4ac4
--- /dev/null
+++ b/vendor/tabled/examples/colored_borders.rs
@@ -0,0 +1,65 @@
+//! This example demonstrates using the [`RawStyle`] [setting](tabled::settings) to
+//! to granularly specify border colors.
+//!
+//! * 🚩 This example requires the `color` feature.
+//!
+//! * Note how [`Color`] containts several helpful, const values covering
+//! a basic selection of foreground and background colors. [`Color`] also
+//! supports custom colors with [`Color::new()`].
+
+use tabled::{
+ settings::{
+ style::{RawStyle, Style},
+ Color,
+ },
+ Table, Tabled,
+};
+
+#[derive(Tabled)]
+struct CodeEditor {
+ name: &'static str,
+ first_release: &'static str,
+ developer: &'static str,
+}
+
+impl CodeEditor {
+ fn new(name: &'static str, first_release: &'static str, developer: &'static str) -> Self {
+ Self {
+ name,
+ first_release,
+ developer,
+ }
+ }
+}
+
+fn main() {
+ let mut style = RawStyle::from(Style::extended());
+ style
+ .set_color_top(Color::FG_RED)
+ .set_color_bottom(Color::FG_CYAN)
+ .set_color_left(Color::FG_BLUE)
+ .set_color_right(Color::FG_GREEN)
+ .set_color_corner_top_left(Color::FG_BLUE)
+ .set_color_corner_top_right(Color::FG_RED)
+ .set_color_corner_bottom_left(Color::FG_CYAN)
+ .set_color_corner_bottom_right(Color::FG_GREEN)
+ .set_color_intersection_bottom(Color::FG_CYAN)
+ .set_color_intersection_top(Color::FG_RED)
+ .set_color_intersection_right(Color::FG_GREEN)
+ .set_color_intersection_left(Color::FG_BLUE)
+ .set_color_intersection(Color::FG_MAGENTA)
+ .set_color_horizontal(Color::FG_MAGENTA)
+ .set_color_vertical(Color::FG_MAGENTA);
+
+ let data = [
+ CodeEditor::new("Sublime Text 3", "2008", "Sublime HQ"),
+ CodeEditor::new("Visual Studio Code", "2015", "Microsoft"),
+ CodeEditor::new("Notepad++", "2003", "Don Ho"),
+ CodeEditor::new("GNU Emacs", "1984", "Richard Stallman"),
+ CodeEditor::new("Neovim", "2015", "Vim community"),
+ ];
+
+ let table = Table::new(data).with(style).to_string();
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/colored_padding.rs b/vendor/tabled/examples/colored_padding.rs
new file mode 100644
index 000000000..96a3a717f
--- /dev/null
+++ b/vendor/tabled/examples/colored_padding.rs
@@ -0,0 +1,173 @@
+//! This example demonstrates using the [`Padding::colorize()`] function in several ways
+//! to give a [`Table`] display a vibrant asthetic.
+//!
+//! * 🚩 This example requires the `color` feature.
+//!
+//! * Note how the [`Color`] [setting](tabled::settings) is used to simplify creating
+//! reusable themes for text, backgrounds, padded whitespace, and borders.
+//!
+//! * Note how a unique color can be set for each direction.
+
+use std::convert::TryFrom;
+
+use owo_colors::OwoColorize;
+
+use tabled::{
+ grid::{
+ config::{ColoredConfig, Entity},
+ dimension::SpannedGridDimension,
+ records::{
+ vec_records::{Cell, VecRecords},
+ ExactRecords, PeekableRecords, Records,
+ },
+ util::string::string_width_multiline,
+ },
+ settings::{
+ object::{Columns, Object, Rows, Segment},
+ Alignment, CellOption, Color, Format, Margin, Modify, Padding, Style,
+ },
+ Table, Tabled,
+};
+
+#[derive(Tabled)]
+#[tabled(rename_all = "PascalCase")]
+struct Fundamental {
+ quantity: &'static str,
+ symbol: &'static str,
+ value: &'static str,
+ unit: &'static str,
+}
+
+impl Fundamental {
+ fn new(
+ quantity: &'static str,
+ symbol: &'static str,
+ value: &'static str,
+ unit: &'static str,
+ ) -> Self {
+ Self {
+ quantity,
+ symbol,
+ value,
+ unit,
+ }
+ }
+}
+
+fn main() {
+ // data source: https://www.britannica.com/science/physical-constant
+ let data = [
+ Fundamental::new(
+ "constant of gravitation",
+ "G",
+ "6.67384 × 10⁻¹¹",
+ "cubic metre per second squared per kilogram",
+ ),
+ Fundamental::new(
+ "speed of light (in a vacuum)",
+ "c",
+ "2.99792458 × 10⁻⁸",
+ "metres per second",
+ ),
+ Fundamental::new(
+ "Planck's constant",
+ "h",
+ "6.626070040 × 10⁻³⁴",
+ "joule second",
+ ),
+ Fundamental::new(
+ "Boltzmann constant",
+ "k",
+ "1.38064852 × 10⁻²³",
+ "joule per kelvin",
+ ),
+ Fundamental::new(
+ "Faraday constant",
+ "F",
+ "9.648533289 × 10⁴",
+ "coulombs per mole",
+ ),
+ ];
+
+ let pane_color = Color::try_from(' '.bg_rgb::<220, 220, 220>().to_string()).unwrap();
+ let border_color = Color::try_from(' '.bg_rgb::<200, 200, 220>().bold().to_string()).unwrap();
+ let data_color = Color::try_from(' '.bg_rgb::<200, 200, 220>().to_string()).unwrap();
+
+ let header_settings = Modify::new(Rows::first())
+ .with(Padding::new(1, 1, 2, 2).colorize(
+ Color::BG_GREEN,
+ Color::BG_YELLOW,
+ Color::BG_MAGENTA,
+ Color::BG_CYAN,
+ ))
+ .with(MakeMaxPadding)
+ .with(Format::content(|s| s.on_black().white().to_string()));
+
+ let data_settings = Modify::new(Rows::first().inverse())
+ .with(Alignment::left())
+ .with(MakeMaxPadding)
+ .with(Padding::new(1, 1, 0, 0).colorize(
+ Color::default(),
+ Color::default(),
+ data_color.clone(),
+ data_color.clone(),
+ ));
+
+ let symbol_settings = Modify::new(Columns::single(1).not(Rows::first()))
+ .with(Format::content(|s| s.bold().to_string()));
+
+ let unit_settings = Modify::new(Columns::single(3).not(Rows::first()))
+ .with(Format::content(|s| s.italic().to_string()));
+
+ let table = Table::new(data)
+ .with(Style::rounded())
+ .with(Margin::new(1, 2, 1, 1).colorize(
+ pane_color.clone(),
+ pane_color.clone(),
+ pane_color.clone(),
+ pane_color,
+ ))
+ .with(border_color)
+ .with(Modify::new(Segment::all()).with(data_color))
+ .with(header_settings)
+ .with(data_settings)
+ .with(symbol_settings)
+ .with(unit_settings)
+ .to_string();
+
+ println!("\n\n{table}\n\n");
+}
+
+#[derive(Debug, Clone)]
+struct MakeMaxPadding;
+
+impl<T> CellOption<VecRecords<T>, ColoredConfig> for MakeMaxPadding
+where
+ T: Cell + AsRef<str>,
+{
+ fn change(self, records: &mut VecRecords<T>, cfg: &mut ColoredConfig, entity: Entity) {
+ let widths = SpannedGridDimension::width(&*records, cfg);
+
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ for (row, col) in entity.iter(count_rows, count_cols) {
+ let column_width = widths[col];
+ let text = records.get_text((row, col));
+ let width = string_width_multiline(text);
+
+ if width < column_width {
+ let available_width = column_width - width;
+ let left = available_width / 2;
+ let right = available_width - left;
+
+ let pos = (row, col).into();
+ let mut pad = cfg.get_padding(pos);
+ pad.left.size = left;
+ pad.right.size = right;
+
+ cfg.set_padding(pos, pad);
+ }
+ }
+ }
+}
diff --git a/vendor/tabled/examples/colorization.rs b/vendor/tabled/examples/colorization.rs
new file mode 100644
index 000000000..a68ca93e8
--- /dev/null
+++ b/vendor/tabled/examples/colorization.rs
@@ -0,0 +1,69 @@
+//! This example demonstrates using the [`Color`] [setting](tabled::settings) to
+//! stylize text, backgrounds, and borders.
+//!
+//! * 🚩 This example requires the `color` feature.
+//!
+//! * Note how [`Format::content()`] is used to break out [`CellOption`]
+//! specifications. This is helpful for organizing extensive [`Table`] configurations.
+
+use tabled::{
+ builder::Builder,
+ settings::{object::Rows, style::Style, themes::Colorization, Color, Concat},
+ Table, Tabled,
+};
+
+#[derive(Tabled)]
+#[tabled(rename_all = "UPPERCASE")]
+struct Employee {
+ id: usize,
+ #[tabled(rename = "FIRST NAME")]
+ first_name: String,
+ #[tabled(rename = "LAST NAME")]
+ last_name: String,
+ salary: usize,
+ comment: String,
+}
+
+impl Employee {
+ fn new(id: usize, first_name: &str, last_name: &str, salary: usize, comment: &str) -> Self {
+ Self {
+ id,
+ salary,
+ first_name: first_name.to_string(),
+ last_name: last_name.to_string(),
+ comment: comment.to_string(),
+ }
+ }
+}
+
+fn main() {
+ let data = vec![
+ Employee::new(1, "Arya", "Stark", 3000, ""),
+ Employee::new(20, "Jon", "Snow", 2000, "You know nothing, Jon Snow!"),
+ Employee::new(300, "Tyrion", "Lannister", 5000, ""),
+ ];
+
+ let total = data.iter().map(|e| e.salary).sum::<usize>();
+ let total_row = Builder::from(vec![vec![
+ String::from(""),
+ String::from(""),
+ String::from("TOTAL"),
+ total.to_string(),
+ ]])
+ .build();
+
+ let color_data_primary = Color::BG_WHITE | Color::FG_BLACK;
+ let color_data_second = Color::BG_BRIGHT_WHITE | Color::FG_BLACK;
+ let color_head = Color::BOLD | Color::BG_CYAN | Color::FG_BLACK;
+ let color_footer = Color::BOLD | Color::BG_BLUE | Color::FG_BLACK;
+
+ let mut table = Table::new(data);
+ table
+ .with(Concat::vertical(total_row))
+ .with(Style::empty())
+ .with(Colorization::rows([color_data_primary, color_data_second]))
+ .with(Colorization::exact([color_head], Rows::first()))
+ .with(Colorization::exact([color_footer], Rows::last()));
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/column_names.rs b/vendor/tabled/examples/column_names.rs
new file mode 100644
index 000000000..0388e6264
--- /dev/null
+++ b/vendor/tabled/examples/column_names.rs
@@ -0,0 +1,63 @@
+//! This example demonstrates how to set column names on a top horizontal line.
+//!
+//! It sets a `clickhouse` like table style (first seen on).
+
+use std::iter::repeat;
+
+use tabled::{
+ grid::config::AlignmentHorizontal,
+ settings::{themes::ColumnNames, Color, Style},
+ Table, Tabled,
+};
+
+#[derive(Debug, Tabled)]
+struct Function {
+ declaration: String,
+ name: String,
+ return_type: String,
+}
+
+impl Function {
+ fn new(decl: &str, name: &str, ret_type: &str) -> Self {
+ Self {
+ declaration: decl.to_string(),
+ name: name.to_string(),
+ return_type: ret_type.to_string(),
+ }
+ }
+}
+
+fn main() {
+ let data = vec![
+ Function::new(
+ "struct stack *stack_create(int)",
+ "stack_create",
+ "struct stack *",
+ ),
+ Function::new(
+ "void stack_destroy(struct stack *)",
+ "stack_destroy",
+ "void",
+ ),
+ Function::new(
+ "int stack_put(struct stack *, vm_offset_t)",
+ "stack_put",
+ "int",
+ ),
+ Function::new(
+ "void stack_copy(const struct stack *, struct stack *)",
+ "stack_copy",
+ "void",
+ ),
+ ];
+
+ let mut table = Table::new(data);
+
+ table.with(Style::modern().remove_horizontal()).with(
+ ColumnNames::default()
+ .set_colors(repeat(Color::BOLD | Color::BG_BLUE | Color::FG_WHITE).take(3))
+ .set_alignment(AlignmentHorizontal::Center),
+ );
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/compact_table.rs b/vendor/tabled/examples/compact_table.rs
new file mode 100644
index 000000000..3a02d448d
--- /dev/null
+++ b/vendor/tabled/examples/compact_table.rs
@@ -0,0 +1,23 @@
+//! This example demonstrates creating a `new()` [`CompactTable`] with
+//! manual specifications for column count, column widths, and border styling.
+//!
+//! * [`CompactTable`] is a [`Table`] alternative that trades off reduced
+//! flexibility for improved performance.
+
+use tabled::{settings::Style, tables::CompactTable};
+
+fn main() {
+ let data = [
+ ["Debian", "", "true"],
+ ["Arch", "", "true"],
+ ["Manjaro", "Arch", "true"],
+ ];
+
+ let _table = CompactTable::new(data)
+ .columns(3)
+ .width([7, 5, 5])
+ .with(Style::markdown());
+
+ #[cfg(feature = "std")]
+ println!("{}", _table.to_string());
+}
diff --git a/vendor/tabled/examples/compact_table_2.rs b/vendor/tabled/examples/compact_table_2.rs
new file mode 100644
index 000000000..5f231ccd8
--- /dev/null
+++ b/vendor/tabled/examples/compact_table_2.rs
@@ -0,0 +1,20 @@
+//! This example demonstrates creating a [`CompactTable`] `from()` a
+//! multidimensional array.
+//!
+//! * Note how [`CompactTable::from()`] inherits the lengths of the nested arrays
+//! as typed definitions through [const generics](https://practice.rs/generics-traits/const-generics.html).
+
+use tabled::{settings::Style, tables::CompactTable};
+
+fn main() {
+ let data = [
+ ["Debian", "1.1.1.1", "true"],
+ ["Arch", "127.1.1.1", "true"],
+ ["Manjaro", "Arch", "true"],
+ ];
+
+ let _table = CompactTable::from(data).with(Style::psql());
+
+ #[cfg(feature = "std")]
+ println!("{}", _table.to_string());
+}
diff --git a/vendor/tabled/examples/compact_table_3.rs b/vendor/tabled/examples/compact_table_3.rs
new file mode 100644
index 000000000..5c7df6d5e
--- /dev/null
+++ b/vendor/tabled/examples/compact_table_3.rs
@@ -0,0 +1,19 @@
+//! This example demonstrates how [`CompactTable`] is limited to single
+//! line rows.
+//!
+//! * Note how the multiline data is accepted, but then truncated in the display.
+
+use tabled::{settings::Style, tables::CompactTable};
+
+fn main() {
+ let data = [
+ ["De\nbi\nan", "1.1.1.1", "true"],
+ ["Arch", "127.1.1.1", "true"],
+ ["Manjaro", "A\nr\nc\nh", "true"],
+ ];
+
+ let _table = CompactTable::from(data).with(Style::psql());
+
+ #[cfg(feature = "std")]
+ println!("{}", _table.to_string());
+}
diff --git a/vendor/tabled/examples/concat.rs b/vendor/tabled/examples/concat.rs
new file mode 100644
index 000000000..09afeed7b
--- /dev/null
+++ b/vendor/tabled/examples/concat.rs
@@ -0,0 +1,58 @@
+//! This example demonstrates using the [`Concat`] [`TableOption`] to concatenate
+//! [`tables`](Table) together.
+//!
+//! * [`Concat`] supports appending tables vertically and horizontally.
+//!
+//! * Note how the base tables style settings take take precedence over the appended table.
+//! If the two tables are of unequal shape, additional blank cells are added as needed.
+
+use tabled::{
+ settings::{object::Segment, Alignment, Concat, Modify, Style},
+ Table, Tabled,
+};
+
+#[derive(Debug, Tabled)]
+struct Weather {
+ temperature_c: f64,
+ wind_ms: f64,
+}
+
+#[derive(Debug, Tabled)]
+struct Location(
+ #[tabled(rename = "latitude")] f64,
+ #[tabled(rename = "longitude")] f64,
+);
+
+fn main() {
+ let weather_data = [
+ Weather {
+ temperature_c: 1.0,
+ wind_ms: 3.0,
+ },
+ Weather {
+ temperature_c: -20.0,
+ wind_ms: 30.0,
+ },
+ Weather {
+ temperature_c: 40.0,
+ wind_ms: 100.0,
+ },
+ ];
+
+ let location_data = [
+ Location(111.111, 333.333),
+ Location(5.111, 7282.1),
+ Location(0.0, 0.0),
+ Location(0.0, 0.0),
+ ];
+
+ let location_table = Table::new(location_data);
+
+ let mut weather_table = Table::new(weather_data);
+ weather_table
+ .with(Concat::horizontal(location_table))
+ .with(Style::empty())
+ .with(Modify::new(Segment::all()).with(Alignment::left()));
+
+ println!("{weather_table}");
+}
diff --git a/vendor/tabled/examples/custom_style.rs b/vendor/tabled/examples/custom_style.rs
new file mode 100644
index 000000000..e6f0848cb
--- /dev/null
+++ b/vendor/tabled/examples/custom_style.rs
@@ -0,0 +1,54 @@
+//! This example demonstrates customizing one of the [`tabled`] default [styles](Style)
+//! to create a unique [`Table`] display.
+//!
+//! * Note that all predesigned styles can be configured completely.
+//! Styles can also be created from scratch!
+//!
+//! * Note that adding and removing borders with a [`Style`] theme doesn't affect the
+//! number of functional columns and rows.
+
+use tabled::{
+ settings::{
+ style::{HorizontalLine, Style, VerticalLine},
+ Alignment,
+ },
+ Table, Tabled,
+};
+
+#[derive(Tabled)]
+struct CodeEditor {
+ name: &'static str,
+ first_release: &'static str,
+ developer: &'static str,
+}
+
+impl CodeEditor {
+ fn new(name: &'static str, first_release: &'static str, developer: &'static str) -> Self {
+ Self {
+ name,
+ first_release,
+ developer,
+ }
+ }
+}
+
+fn main() {
+ let data = [
+ CodeEditor::new("Sublime Text 3", "2008", "Sublime HQ"),
+ CodeEditor::new("Visual Studio Code", "2015", "Microsoft"),
+ CodeEditor::new("Notepad++", "2003", "Don Ho"),
+ CodeEditor::new("GNU Emacs", "1984", "Richard Stallman"),
+ CodeEditor::new("Neovim", "2015", "Vim community"),
+ ];
+
+ let theme = Style::modern()
+ .remove_horizontal()
+ .remove_vertical()
+ .horizontals([HorizontalLine::new(1, Style::modern().get_horizontal()).intersection(None)])
+ .verticals([VerticalLine::new(1, Style::modern().get_vertical())]);
+
+ let mut table = Table::new(data);
+ table.with(theme).with(Alignment::left());
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/derive/display_with.rs b/vendor/tabled/examples/derive/display_with.rs
new file mode 100644
index 000000000..9dfc87a47
--- /dev/null
+++ b/vendor/tabled/examples/derive/display_with.rs
@@ -0,0 +1,65 @@
+//! This example demonstrates using the [attribute macro](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros)
+//! [`display_with`] to seamlessly augment field representations in a [`Table`] display.
+//!
+//! * [`display_with`] functions act as transformers during [`Table`] instantiation.
+//!
+//! * Note how [`display_with`] works with [std] and custom functions alike.
+//!
+//! * [`display_with`] attributes can be constructed in two ways (shown below).
+//!
+//! * Attribute arguments can be directly overridden with static values, effectively ignoring the
+//! augmented fields natural value entirely. Even an entire object can be passed as context with `self`.
+
+use std::borrow::Cow;
+
+use tabled::{Table, Tabled};
+
+#[derive(Tabled)]
+#[tabled(rename_all = "camelCase")]
+struct Country {
+ name: &'static str,
+ capital_city: &'static str,
+ #[tabled(display_with("display_perimeter", self))]
+ surface_area_km2: f32,
+ #[tabled(display_with = "str::to_lowercase")]
+ national_currency: &'static str,
+ national_currency_short: &'static str,
+}
+
+fn display_perimeter(country: &Country) -> Cow<'_, str> {
+ if country.surface_area_km2 > 1_000_000.0 {
+ "Very Big Land".into()
+ } else {
+ "Big Land".into()
+ }
+}
+
+impl Country {
+ fn new(
+ name: &'static str,
+ national_currency: &'static str,
+ national_currency_short: &'static str,
+ capital_city: &'static str,
+ surface_area_km2: f32,
+ ) -> Self {
+ Self {
+ name,
+ national_currency,
+ national_currency_short,
+ capital_city,
+ surface_area_km2,
+ }
+ }
+}
+
+fn main() {
+ let data = [
+ Country::new("Afghanistan", "Afghani", "AFN", "Kabul", 652867.0),
+ Country::new("Angola", "Kwanza", "AOA", "Luanda", 1246700.0),
+ Country::new("Canada", "Canadian Dollar", "CAD", "Ottawa", 9984670.0),
+ ];
+
+ let table = Table::new(data);
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/derive/inline.rs b/vendor/tabled/examples/derive/inline.rs
new file mode 100644
index 000000000..2ce07816f
--- /dev/null
+++ b/vendor/tabled/examples/derive/inline.rs
@@ -0,0 +1,55 @@
+//! This example demonstrates using the [attribute macro](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros)
+//! [`inline`] to expand struct fields to individual columns in a [`Table`] display.
+//!
+//! * Note that without inlining a struct or enum field, those objects
+//! must implement the [`Display`] trait as they will be represented in
+//! a single column with the value of their [`ToString`] output.
+
+use tabled::{Table, Tabled};
+
+#[derive(Tabled)]
+struct Country {
+ name: &'static str,
+ capital_city: &'static str,
+ surface_area_km2: f32,
+ #[tabled(inline)]
+ currency: Currency,
+}
+
+#[derive(Tabled)]
+struct Currency {
+ str: &'static str,
+ short: &'static str,
+}
+
+impl Country {
+ fn new(
+ name: &'static str,
+ national_currency: &'static str,
+ national_currency_short: &'static str,
+ capital_city: &'static str,
+ surface_area_km2: f32,
+ ) -> Self {
+ Self {
+ name,
+ capital_city,
+ surface_area_km2,
+ currency: Currency {
+ str: national_currency,
+ short: national_currency_short,
+ },
+ }
+ }
+}
+
+fn main() {
+ let data = [
+ Country::new("Afghanistan", "Afghani", "AFN", "Kabul", 652867.0),
+ Country::new("Angola", "Kwanza", "AOA", "Luanda", 1246700.0),
+ Country::new("Canada", "Canadian Dollar", "CAD", "Ottawa", 9984670.0),
+ ];
+
+ let table = Table::new(data);
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/derive/inline_enum.rs b/vendor/tabled/examples/derive/inline_enum.rs
new file mode 100644
index 000000000..707d5bb2c
--- /dev/null
+++ b/vendor/tabled/examples/derive/inline_enum.rs
@@ -0,0 +1,50 @@
+//! This example demonstrates using the [attribute macro](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros)
+//! [`inline`] to expand enum fields to individual columns in a [`Table`] display.
+//!
+//! * Note how the optional [`inline`] argument is used to apply prefixes
+//! to decomposed column headers. This is helpful for organizing tables
+//! with repetative fields that would normally result in confusing headers.
+//!
+//! * Note that without inlining a struct or enum field, those objects
+//! must implement the [`Display`] trait as they will be represented in
+//! a single column with the value of their [`ToString`] output.
+
+use tabled::{Table, Tabled};
+
+#[derive(Tabled)]
+enum Contact {
+ #[tabled(inline("telegram::"))]
+ Telegram {
+ username: &'static str,
+ #[tabled(inline("telegram::"))]
+ number: Number,
+ },
+ #[tabled(inline)]
+ Local(#[tabled(inline("local::"))] Number),
+}
+
+#[derive(Tabled)]
+struct Number {
+ number: &'static str,
+ code: usize,
+}
+
+impl Number {
+ fn new(number: &'static str, code: usize) -> Self {
+ Self { number, code }
+ }
+}
+
+fn main() {
+ let data = [
+ Contact::Local(Number::new("654321", 123)),
+ Contact::Telegram {
+ username: "no2Presley",
+ number: Number::new("123456", 123),
+ },
+ ];
+
+ let table = Table::new(data);
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/derive/order.rs b/vendor/tabled/examples/derive/order.rs
new file mode 100644
index 000000000..44e036fad
--- /dev/null
+++ b/vendor/tabled/examples/derive/order.rs
@@ -0,0 +1,48 @@
+//! This example demonstrates using the [attribute macro](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros)
+//! [`order`] to relocate fields to specified indexes in a [`Table`] display.
+//!
+//! * By default, [`Table`] columns are shown in the same ordered they are
+//! defined in the deriving struct/enum definition.
+
+use tabled::{Table, Tabled};
+
+#[derive(Tabled)]
+struct Country {
+ name: &'static str,
+ capital_city: &'static str,
+ surface_area_km2: f32,
+ #[tabled(order = 1)]
+ national_currency: &'static str,
+ #[tabled(order = 2)]
+ national_currency_short: &'static str,
+}
+
+impl Country {
+ fn new(
+ name: &'static str,
+ national_currency: &'static str,
+ national_currency_short: &'static str,
+ capital_city: &'static str,
+ surface_area_km2: f32,
+ ) -> Self {
+ Self {
+ name,
+ national_currency,
+ national_currency_short,
+ capital_city,
+ surface_area_km2,
+ }
+ }
+}
+
+fn main() {
+ let data = [
+ Country::new("Afghanistan", "Afghani", "AFN", "Kabul", 652867.0),
+ Country::new("Angola", "Kwanza", "AOA", "Luanda", 1246700.0),
+ Country::new("Canada", "Canadian Dollar", "CAD", "Ottawa", 9984670.0),
+ ];
+
+ let table = Table::new(data);
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/derive/rename.rs b/vendor/tabled/examples/derive/rename.rs
new file mode 100644
index 000000000..709411948
--- /dev/null
+++ b/vendor/tabled/examples/derive/rename.rs
@@ -0,0 +1,45 @@
+//! This example demonstrates using the [attribute macro](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros)
+//! [`rename`] to alias specific fields in a [`Table`] display.
+
+use tabled::{Table, Tabled};
+
+#[derive(Tabled)]
+struct Country {
+ name: &'static str,
+ capital_city: &'static str,
+ surface_area_km2: f32,
+ #[tabled(rename = "Currency")]
+ national_currency: &'static str,
+ #[tabled(rename = "Currency-ISO")]
+ national_currency_short: &'static str,
+}
+
+impl Country {
+ fn new(
+ name: &'static str,
+ national_currency: &'static str,
+ national_currency_short: &'static str,
+ capital_city: &'static str,
+ surface_area_km2: f32,
+ ) -> Self {
+ Self {
+ name,
+ national_currency,
+ national_currency_short,
+ capital_city,
+ surface_area_km2,
+ }
+ }
+}
+
+fn main() {
+ let data = [
+ Country::new("Afghanistan", "Afghani", "AFN", "Kabul", 652867.0),
+ Country::new("Angola", "Kwanza", "AOA", "Luanda", 1246700.0),
+ Country::new("Canada", "Canadian Dollar", "CAD", "Ottawa", 9984670.0),
+ ];
+
+ let table = Table::new(data);
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/derive/rename_all.rs b/vendor/tabled/examples/derive/rename_all.rs
new file mode 100644
index 000000000..cd408e5ba
--- /dev/null
+++ b/vendor/tabled/examples/derive/rename_all.rs
@@ -0,0 +1,56 @@
+//! This example demonstrates using the [attribute macro](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros)
+//! [`rename_all`] to apply table-wide header formatting in a [`Table`] display.
+//!
+//! * Supported formatting rules include:
+//! * 'camelCase'
+//! * 'kabab-case'
+//! * 'PascalCase'
+//! * 'SCREAMING_SNAKE_CASE'
+//! * 'snake_case'
+//! * 'lowercase'
+//! * 'UPPERCASE'
+//! * 'verbatim'
+
+use tabled::{Table, Tabled};
+
+#[derive(Tabled)]
+#[tabled(rename_all = "camelCase")]
+struct Country {
+ name: &'static str,
+ capital_city: &'static str,
+ surface_area_km2: f32,
+ #[tabled(rename_all = "kebab-case")]
+ national_currency: &'static str,
+ #[tabled(rename_all = "kebab-case")]
+ national_currency_short: &'static str,
+}
+
+impl Country {
+ fn new(
+ name: &'static str,
+ national_currency: &'static str,
+ national_currency_short: &'static str,
+ capital_city: &'static str,
+ surface_area_km2: f32,
+ ) -> Self {
+ Self {
+ name,
+ national_currency,
+ national_currency_short,
+ capital_city,
+ surface_area_km2,
+ }
+ }
+}
+
+fn main() {
+ let data = [
+ Country::new("Afghanistan", "Afghani", "AFN", "Kabul", 652867.0),
+ Country::new("Angola", "Kwanza", "AOA", "Luanda", 1246700.0),
+ Country::new("Canada", "Canadian Dollar", "CAD", "Ottawa", 9984670.0),
+ ];
+
+ let table = Table::new(data);
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/derive/skip.rs b/vendor/tabled/examples/derive/skip.rs
new file mode 100644
index 000000000..86f7a9ba2
--- /dev/null
+++ b/vendor/tabled/examples/derive/skip.rs
@@ -0,0 +1,49 @@
+//! This example demonstrates using the [attribute macro](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros)
+//! [`skip`] to omit specific fields from becoming columns in a [`Table`] display.
+//!
+//! * Note how [`skip`] annoys [clippy](https://doc.rust-lang.org/clippy/) with `dead_code`
+//! warnings. This can be addressed with compiler overrides like `#[allow(dead_code)]`.
+
+use tabled::{Table, Tabled};
+
+#[allow(dead_code)]
+#[derive(Tabled)]
+struct Country {
+ name: &'static str,
+ capital_city: &'static str,
+ #[tabled(skip)]
+ surface_area_km2: f32,
+ national_currency: &'static str,
+ #[tabled(skip)]
+ national_currency_short: &'static str,
+}
+
+impl Country {
+ fn new(
+ name: &'static str,
+ national_currency: &'static str,
+ national_currency_short: &'static str,
+ capital_city: &'static str,
+ surface_area_km2: f32,
+ ) -> Self {
+ Self {
+ name,
+ national_currency,
+ national_currency_short,
+ capital_city,
+ surface_area_km2,
+ }
+ }
+}
+
+fn main() {
+ let data = [
+ Country::new("Afghanistan", "Afghani", "AFN", "Kabul", 652867.0),
+ Country::new("Angola", "Kwanza", "AOA", "Luanda", 1246700.0),
+ Country::new("Canada", "Canadian Dollar", "CAD", "Ottawa", 9984670.0),
+ ];
+
+ let table = Table::new(data);
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/disable.rs b/vendor/tabled/examples/disable.rs
new file mode 100644
index 000000000..1f04de7d9
--- /dev/null
+++ b/vendor/tabled/examples/disable.rs
@@ -0,0 +1,49 @@
+//! This example demonstrates using the [`Disable`] [`TableOption`] to remove specific
+//! cell data from a [`Table`] display.
+//!
+//! * ⚠️ Using [`Disable`] in combination with other [`Style`] customizations may yield unexpected results.
+//! It is safest to use [`Disable`] last in a chain of alterations.
+
+use tabled::{
+ settings::{
+ locator::ByColumnName,
+ style::{Border, Style},
+ Disable, Modify,
+ },
+ Table, Tabled,
+};
+
+#[derive(Tabled)]
+struct Distribution {
+ name: &'static str,
+ based_on: &'static str,
+ is_active: bool,
+ is_cool: bool,
+}
+
+impl Distribution {
+ fn new(name: &'static str, based_on: &'static str, is_active: bool, is_cool: bool) -> Self {
+ Self {
+ name,
+ based_on,
+ is_active,
+ is_cool,
+ }
+ }
+}
+
+fn main() {
+ let data = [
+ Distribution::new("Debian", "", true, true),
+ Distribution::new("Arch", "", true, true),
+ Distribution::new("Manjaro", "Arch", true, true),
+ ];
+
+ let mut table = Table::new(data);
+ table
+ .with(Style::markdown())
+ .with(Disable::column(ByColumnName::new("is_active")))
+ .with(Modify::new(ByColumnName::new("name")).with(Border::filled('#')));
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/extended_display.rs b/vendor/tabled/examples/extended_display.rs
new file mode 100644
index 000000000..d31257881
--- /dev/null
+++ b/vendor/tabled/examples/extended_display.rs
@@ -0,0 +1,35 @@
+//! This example demonstrates using [ExtendedTable], a [Table] alternative with
+//! limited flexibility but a greater emphasis on large data displays.
+
+use tabled::{tables::ExtendedTable, Tabled};
+
+#[derive(Tabled)]
+struct Distribution {
+ name: &'static str,
+ based_on: &'static str,
+ is_active: bool,
+ is_cool: bool,
+}
+
+impl Distribution {
+ fn new(name: &'static str, based_on: &'static str, is_active: bool, is_cool: bool) -> Self {
+ Self {
+ name,
+ based_on,
+ is_active,
+ is_cool,
+ }
+ }
+}
+
+fn main() {
+ let data = vec![
+ Distribution::new("Manjaro", "Arch", true, true),
+ Distribution::new("Arch", "", true, true),
+ Distribution::new("Debian", "", true, true),
+ ];
+
+ let table = ExtendedTable::new(data);
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/extract.rs b/vendor/tabled/examples/extract.rs
new file mode 100644
index 000000000..8d7d86434
--- /dev/null
+++ b/vendor/tabled/examples/extract.rs
@@ -0,0 +1,106 @@
+//! This example demonstrates using the [`Extract`] [`TableOption`] to
+//! produce a subsection of a [`Table`].
+//!
+//! * [`Extract`] can return a new [`Table`] with three functions:
+//! * `rows()` | yields subset of the initial rows
+//! * `columns()` | yields subset of the initial columns
+//! * `segment()` | yields subsection of the initial table
+//!
+//! * Note how [`Extract`] methods accepts [`RangeBounds`] arguments,
+//! making subset specifications concise.
+
+use std::fmt::{Display, Formatter};
+
+use tabled::{
+ settings::{
+ object::{Columns, Rows},
+ Alignment, Extract, Format, Modify, Style,
+ },
+ Table, Tabled,
+};
+
+#[derive(Tabled)]
+struct Album {
+ artist: &'static str,
+ name: &'static str,
+ released: &'static str,
+ level_of_greatness: LevelOfGreatness,
+}
+
+impl Album {
+ fn new(
+ artist: &'static str,
+ name: &'static str,
+ released: &'static str,
+ level_of_greatness: LevelOfGreatness,
+ ) -> Self {
+ Self {
+ artist,
+ name,
+ released,
+ level_of_greatness,
+ }
+ }
+}
+
+#[derive(Debug)]
+enum LevelOfGreatness {
+ Supreme,
+ Outstanding,
+ Unparalleled,
+}
+
+impl Display for LevelOfGreatness {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
+ std::fmt::Debug::fmt(&self, f)
+ }
+}
+
+fn main() {
+ use LevelOfGreatness::*;
+
+ let data = [
+ Album::new(
+ "Pink Floyd",
+ "The Dark Side of the Moon",
+ "01 March 1973",
+ Unparalleled,
+ ),
+ Album::new("Fleetwood Mac", "Rumours", "04 February 1977", Outstanding),
+ Album::new(
+ "Led Zeppelin",
+ "Led Zeppelin IV",
+ "08 November 1971",
+ Supreme,
+ ),
+ ];
+
+ println!("Full");
+
+ let mut table = Table::new(data);
+ table
+ .with(Style::modern())
+ .with(Modify::new(Rows::first()).with(Alignment::center()))
+ .with(Modify::new(Rows::new(1..)).with(Alignment::left()));
+ println!("{table}");
+
+ println!("Segment row: (1..=2) column: (1..)");
+
+ let table = table.with(Extract::segment(1..=2, 1..));
+ println!("{table}");
+
+ println!("Refinished segment");
+
+ let highlight = Format::content(|s| {
+ if s == "Outstanding" {
+ format!("+{s}+")
+ } else {
+ s.to_string()
+ }
+ });
+
+ let table = table
+ .with(Style::modern())
+ .with(Modify::new(Columns::new(1..)).with(highlight));
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/format.rs b/vendor/tabled/examples/format.rs
new file mode 100644
index 000000000..7dc4bddb2
--- /dev/null
+++ b/vendor/tabled/examples/format.rs
@@ -0,0 +1,56 @@
+//! This example demonstrates using the [`Format`] [`CellOption`] factory to alter
+//! the cells of a [`Table`].
+//!
+//! * Note how [`Format::content()`] gives access to the respective cell content for replacement.
+//! And [`Format::positioned()`] additionally provides the index coordinates of that cell.
+//!
+//! * Note how the [std] [`format!`] macro is used to update the values of the affected cells.
+
+use tabled::{
+ settings::{
+ object::{Columns, Object, Rows},
+ Format, Modify, Style,
+ },
+ Table, Tabled,
+};
+
+#[derive(Tabled)]
+struct Commit {
+ id: &'static str,
+ header: &'static str,
+ message: &'static str,
+}
+
+fn main() {
+ let data = [
+ Commit {
+ header: "bypass open-source transmitter",
+ message: "index neural panel",
+ id: "8ae4e8957caeaa467acbce963701e227af00a1c7",
+ },
+ Commit {
+ header: "program online alarm",
+ message: "copy bluetooth card",
+ id: "48c76de71bd685486d97dc8f4f05aa6fcc0c3f86",
+ },
+ Commit {
+ header: "CSV",
+ message: "reboot mobile capacitor",
+ id: "6ffc2a2796229fc7bf59471ad907f58b897005d0",
+ },
+ ];
+
+ let table = Table::new(data)
+ .with(Style::psql())
+ .with(
+ Modify::new(Rows::first())
+ .with(Format::positioned(|_, (_, column)| column.to_string())),
+ )
+ .with(
+ Modify::new(Columns::first().not(Rows::first()))
+ .with(Format::content(|s| format!("{s}..."))),
+ )
+ .to_string();
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/formatting_settings.rs b/vendor/tabled/examples/formatting_settings.rs
new file mode 100644
index 000000000..19c8a4806
--- /dev/null
+++ b/vendor/tabled/examples/formatting_settings.rs
@@ -0,0 +1,47 @@
+//! This example demonstrates using the [`Alignment`], [`AlignmentStrategy`], and [`TrimStrategy`] [`CellOptions`]
+//! to align the content of a [`Table`] in several nuanced ways.
+//!
+//! * Note how [`AlignmentStrategy`] and [`TrimStrategy`] provide useful tools for managing multiline cells and
+//! cell values that are bloated with whitespace.
+
+use tabled::{
+ settings::{
+ formatting::{AlignmentStrategy, TrimStrategy},
+ object::Segment,
+ Alignment, Modify, Style,
+ },
+ Table,
+};
+
+fn main() {
+ let some_json = r#"
+[
+ "foo",
+ {
+ "bar": 1,
+ "baz": [
+ 2,
+ 3
+ ]
+ }
+]"#;
+
+ let mut table = Table::new([some_json]);
+ table
+ .with(Style::rounded())
+ .with(Modify::new(Segment::all()).with(Alignment::center()));
+
+ println!("A default Alignment settings\n{table}");
+
+ table.with(Modify::new(Segment::all()).with(AlignmentStrategy::PerLine));
+
+ println!("Per line Alignment strategy\n{table}");
+
+ table.with(
+ Modify::new(Segment::all())
+ .with(AlignmentStrategy::PerCell)
+ .with(TrimStrategy::Both),
+ );
+
+ println!("A default Alignment; allowing vertical and horizontal trim\n{table}");
+}
diff --git a/vendor/tabled/examples/grid_colors.rs b/vendor/tabled/examples/grid_colors.rs
new file mode 100644
index 000000000..f48b39f49
--- /dev/null
+++ b/vendor/tabled/examples/grid_colors.rs
@@ -0,0 +1,46 @@
+//! This example demonstrates using [`Color`] as a [`CellOption`] modifier to stylize
+//! the cells of a [`Table`].
+//!
+//! * Note how the [`Color`] [setting](tabled::settings) is used to simplify creating
+//! reusable themes for backgrounds.
+
+use tabled::{
+ settings::{Color, Modify, Style},
+ Table, Tabled,
+};
+
+#[derive(Tabled)]
+struct Bsd {
+ distribution: &'static str,
+ year_of_first_release: usize,
+ is_active: bool,
+}
+
+impl Bsd {
+ fn new(distribution: &'static str, year_of_first_release: usize, is_active: bool) -> Self {
+ Self {
+ distribution,
+ year_of_first_release,
+ is_active,
+ }
+ }
+}
+
+fn main() {
+ let data = vec![
+ Bsd::new("BSD", 1978, false),
+ Bsd::new("SunOS", 1982, false),
+ Bsd::new("NetBSD", 1993, true),
+ Bsd::new("FreeBSD", 1993, true),
+ Bsd::new("OpenBSD", 1995, true),
+ ];
+
+ let mut table = Table::new(data);
+ table
+ .with(Style::psql())
+ .with(Modify::new((0, 0)).with(Color::BG_BLUE))
+ .with(Modify::new((1, 1)).with(Color::BG_GREEN))
+ .with(Modify::new((2, 2)).with(Color::BG_RED));
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/height.rs b/vendor/tabled/examples/height.rs
new file mode 100644
index 000000000..2da0f36d9
--- /dev/null
+++ b/vendor/tabled/examples/height.rs
@@ -0,0 +1,49 @@
+//! This example demonstrates using the [`Height`] [`TableOption`] for adjusting
+//! the height of a [`Table`].
+//!
+//! * [`Height`] supports three key features:
+//! * [`CellHeightIncrease`] spreads new whitespace between the [`Table`]
+//! rows up to the specified line count.
+//! * [`CellHeightLimit`] removes lines from the [`Table`] rows fairly, until
+//! it has no choice but to remove single-line-rows entirely, bottom up.
+//! * [`HeightList`] accepts an array of height specifications that are applied
+//! to the rows with the same index. This is helpful for granularly specifying individual
+//! row heights irrespective of [`Padding`] or [`Margin`].
+
+use tabled::{
+ settings::{peaker::PriorityMax, Height, Style},
+ Table,
+};
+
+fn main() {
+ let data = vec![("Multi\nline\nstring", 123), ("Single line", 234)];
+
+ let mut table = Table::builder(data).build();
+ table.with(Style::markdown());
+
+ println!("Table\n");
+ println!("{table}");
+ println!();
+
+ let table_ = table.clone().with(Height::increase(10)).to_string();
+
+ println!("Table increase height to 10\n");
+ println!("{table_}");
+ println!();
+
+ let table_ = table
+ .clone()
+ .with(Height::limit(4).priority::<PriorityMax>())
+ .to_string();
+
+ println!("Table decrease height to 4\n");
+ println!("{table_}");
+
+ let table_ = table
+ .clone()
+ .with(Height::limit(0).priority::<PriorityMax>())
+ .to_string();
+
+ println!("Table decrease height to 0\n");
+ println!("{table_}");
+}
diff --git a/vendor/tabled/examples/highlight.rs b/vendor/tabled/examples/highlight.rs
new file mode 100644
index 000000000..0b6abb8ad
--- /dev/null
+++ b/vendor/tabled/examples/highlight.rs
@@ -0,0 +1,28 @@
+//! This example demonstrates using the [`Highlight`] [`TableOption`] to
+//! decorate sections of a [`Table`] with a unique [`Border`].
+//!
+//! * Note how [`Highlight`] arguments can be chained together to
+//! create cross-sections and non-symmetrical shapes.
+
+use tabled::{
+ settings::{
+ object::{Columns, Object, Rows},
+ style::{Border, Style},
+ Highlight,
+ },
+ Table,
+};
+
+fn main() {
+ let data = vec![["A", "B", "C"], ["D", "E", "F"], ["G", "H", "I"]];
+
+ let table = Table::new(data)
+ .with(Style::modern())
+ .with(Highlight::new(
+ Rows::first().and(Columns::single(1)),
+ Border::filled('*'),
+ ))
+ .to_string();
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/highlight_color.rs b/vendor/tabled/examples/highlight_color.rs
new file mode 100644
index 000000000..4646f3819
--- /dev/null
+++ b/vendor/tabled/examples/highlight_color.rs
@@ -0,0 +1,27 @@
+//! This example demonstrates using [`Highlight`] in combination with [`BorderColor`] to
+//! frame sections of a [`Table`] with a unique background [`Color`].
+//!
+//! * Note how [`Highlight::colored()`] is used to accept the necessary input instead of [`Highlight::new()`].
+
+use tabled::{
+ settings::{
+ object::{Columns, Object, Rows},
+ style::BorderColor,
+ Color, Highlight, Style,
+ },
+ Table,
+};
+
+fn main() {
+ let data = vec![["A", "B", "C"], ["D", "E", "F"], ["G", "H", "I"]];
+
+ let table = Table::new(data)
+ .with(Style::modern())
+ .with(Highlight::colored(
+ Rows::first().and(Columns::single(1)),
+ BorderColor::filled(Color::BG_BRIGHT_BLACK),
+ ))
+ .to_string();
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/hyperlink.rs b/vendor/tabled/examples/hyperlink.rs
new file mode 100644
index 000000000..9fda7a040
--- /dev/null
+++ b/vendor/tabled/examples/hyperlink.rs
@@ -0,0 +1,83 @@
+//! This example demonstrates how hyperlinks can be embedded into a [`Table`] display.
+//!
+//! While not a [`tabled`] specific implementation, it is helpful to know that
+//! most users expect certain elements of interactivity based on the purpose of your display.
+//!
+//! * 🚩 This example requires the `color` feature.
+//!
+//! * ⚠️ Terminal interfaces may differ in how they parse links or make them interactive.
+//! [`tabled`] doesn't have the final say on whether a link is clickable or not.
+
+use tabled::{
+ settings::{object::Segment, Alignment, Modify, Style, Width},
+ Table, Tabled,
+};
+
+fn main() {
+ let multicolored_debian = "\x1b[30mDebian\x1b[0m\
+ \x1b[31m Debian\x1b[0m\
+ \x1b[32m Debian\x1b[0m\
+ \x1b[33m Debian\x1b[0m\
+ \x1b[34m Debian\x1b[0m\
+ \x1b[35m Debian\x1b[0m\
+ \x1b[36m Debian\x1b[0m\
+ \x1b[37m Debian\x1b[0m\
+ \x1b[40m Debian\x1b[0m\
+ \x1b[41m Debian\x1b[0m\
+ \x1b[42m Debian\x1b[0m\
+ \x1b[43m Debian\x1b[0m\
+ \x1b[44m Debian\x1b[0m";
+
+ let debian_repeat =
+ "DebianDebianDebianDebianDebianDebianDebianDebianDebianDebianDebianDebianDebianDebian"
+ .to_string();
+
+ let debian_colored_link = format_osc8_hyperlink("https://www.debian.org/", multicolored_debian);
+ let debian_link = format_osc8_hyperlink("https://www.debian.org/", "Debian");
+ let wiki_link = format_osc8_hyperlink("https://www.wikipedia.org/", "Debian");
+
+ let data = [
+ Distribution::new("Debian".into(), false),
+ Distribution::new(debian_link.clone(), true),
+ Distribution::new(format!("{debian_link} a link followed by text"), true),
+ Distribution::new(
+ format!("{debian_link} links with intervening text {wiki_link}"),
+ true,
+ ),
+ Distribution::new(format!("a link surrounded {debian_link} by text"), true),
+ Distribution::new(debian_colored_link, true),
+ Distribution::new(debian_repeat, false),
+ ];
+
+ let mut table = Table::new(&data);
+ table
+ .with(Style::ascii_rounded())
+ .with(Alignment::left())
+ .with(Modify::new(Segment::all()).with(Width::wrap(16).keep_words()));
+
+ println!("{table}");
+
+ let mut table = Table::new(&data);
+ table
+ .with(Style::ascii_rounded())
+ .with(Alignment::left())
+ .with(Modify::new(Segment::all()).with(Width::wrap(16)));
+
+ println!("{table}");
+}
+
+#[derive(Tabled)]
+struct Distribution {
+ name: String,
+ is_hyperlink: bool,
+}
+
+impl Distribution {
+ fn new(name: String, is_hyperlink: bool) -> Self {
+ Self { name, is_hyperlink }
+ }
+}
+
+fn format_osc8_hyperlink(url: &str, text: &str) -> String {
+ format!("\x1b]8;;{url}\x1b\\{text}\x1b]8;;\x1b\\",)
+}
diff --git a/vendor/tabled/examples/iter_table.rs b/vendor/tabled/examples/iter_table.rs
new file mode 100644
index 000000000..0a77f7435
--- /dev/null
+++ b/vendor/tabled/examples/iter_table.rs
@@ -0,0 +1,31 @@
+//! This example demonstrates using [`IterTable`], an [allocation](https://doc.rust-lang.org/nomicon/vec/vec-alloc.html)
+//! free [`Table`] alternative that translates an iterator into a display.
+//!
+//! * Note how [`IterTable`] supports the familiar `.with()` syntax for applying display
+//! modifications.
+//!
+//! * [`IterTable`] supports manual configuration of:
+//! * Record sniffing (default 1000 rows)
+//! * Row cutoff
+//! * Row height
+//! * Column cutoff
+//! * Column width
+
+use std::io::BufRead;
+
+use tabled::{settings::Style, tables::IterTable};
+
+fn main() {
+ let path = file!();
+ let file = std::fs::File::open(path).unwrap();
+ let reader = std::io::BufReader::new(file);
+ let iterator = reader.lines().enumerate().map(|(i, line)| match line {
+ Ok(line) => [i.to_string(), String::from("ok"), line],
+ Err(err) => [i.to_string(), String::from("error"), err.to_string()],
+ });
+
+ let table = IterTable::new(iterator).with(Style::ascii_rounded());
+
+ table.build(std::io::stdout()).unwrap();
+ println!()
+}
diff --git a/vendor/tabled/examples/margin.rs b/vendor/tabled/examples/margin.rs
new file mode 100644
index 000000000..97498fb40
--- /dev/null
+++ b/vendor/tabled/examples/margin.rs
@@ -0,0 +1,21 @@
+//! This example demonstrates using the [`Margin`] [`TableOption`] to buffer space
+//! around a [`Table`] display.
+//!
+//! * Note how the [`Margin::fill()`] function allows for overriding the default whitespace
+//! with any [`char`].
+
+use tabled::{
+ settings::{Margin, Style},
+ Table,
+};
+
+fn main() {
+ let data = vec![["A", "B", "C"], ["D", "E", "F"], ["G", "H", "I"]];
+
+ let table = Table::new(data)
+ .with(Style::re_structured_text())
+ .with(Margin::new(4, 3, 2, 1).fill('<', '>', 'v', '^'))
+ .to_string();
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/matrix.rs b/vendor/tabled/examples/matrix.rs
new file mode 100644
index 000000000..59e22ae9c
--- /dev/null
+++ b/vendor/tabled/examples/matrix.rs
@@ -0,0 +1,27 @@
+//! This example demonstrates how [`tabled`] is an excellent tool for creating
+//! dataset visualizations.
+//!
+//! * 🚀 When native display solutions, such as the [`Debug`] trait and [pretty printing](https://doc.rust-lang.org/std/fmt/#sign0)
+//! options, aren't enough, [`tabled`] is a great choice for improving the quality of your displays.
+
+use tabled::{settings::Style, Table};
+
+fn matrix<const N: usize>() -> [[usize; N]; N] {
+ let mut matrix = [[0; N]; N];
+
+ #[allow(clippy::needless_range_loop)]
+ for i in 0..N {
+ for j in 0..N {
+ matrix[i][j] = (i + 1) * (j + 1);
+ }
+ }
+
+ matrix
+}
+
+fn main() {
+ let data = matrix::<10>();
+ let table = Table::new(data).with(Style::modern()).to_string();
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/merge_duplicates.rs b/vendor/tabled/examples/merge_duplicates.rs
new file mode 100644
index 000000000..250a95a56
--- /dev/null
+++ b/vendor/tabled/examples/merge_duplicates.rs
@@ -0,0 +1,58 @@
+//! This example demonstrates using the [`Merge`] [`TableOption`] to clarify
+//! redundancies in a [`Table`] display.
+//!
+//! * Note how repetative entries must be consecutive, in their specified direction,
+//! to be merged together.
+//!
+//! * Note how [`BorderSpanCorrection`] is used to resolve display issues incurred
+//! from [`Span`] decisions made through duplicate detection.
+//!
+//! * Merge supports both [`Merge::vertical()`] and [`Merge::horizontal()`].
+
+use tabled::{
+ settings::{
+ style::{BorderSpanCorrection, Style},
+ Merge,
+ },
+ Table, Tabled,
+};
+
+#[derive(Tabled)]
+struct DatabaseTable {
+ #[tabled(rename = "db")]
+ db_name: &'static str,
+ #[tabled(rename = "table")]
+ table_name: &'static str,
+ total: usize,
+}
+
+impl DatabaseTable {
+ fn new(db_name: &'static str, table_name: &'static str, total: usize) -> Self {
+ Self {
+ db_name,
+ table_name,
+ total,
+ }
+ }
+}
+
+fn main() {
+ let data = [
+ DatabaseTable::new("database_1", "table_1", 10712),
+ DatabaseTable::new("database_1", "table_2", 57),
+ DatabaseTable::new("database_1", "table_3", 57),
+ DatabaseTable::new("database_2", "table_1", 72),
+ DatabaseTable::new("database_2", "table_2", 75),
+ DatabaseTable::new("database_3", "table_1", 20),
+ DatabaseTable::new("database_3", "table_2", 21339),
+ DatabaseTable::new("database_3", "table_3", 141723),
+ ];
+
+ let table = Table::new(data)
+ .with(Merge::vertical())
+ .with(Style::modern())
+ .with(BorderSpanCorrection)
+ .to_string();
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/merge_duplicates_2.rs b/vendor/tabled/examples/merge_duplicates_2.rs
new file mode 100644
index 000000000..063664d3c
--- /dev/null
+++ b/vendor/tabled/examples/merge_duplicates_2.rs
@@ -0,0 +1,78 @@
+//! This example demonstrates using the [`Merge`] [`TableOption`] to clarify
+//! redundancies in a [`Table`] display.
+//!
+//! * Note how a custom theme is applied to give the [`Merged`](Merge) cells
+//! a unique look.
+//!
+//! * Merge supports both [`Merge::vertical()`] and [`Merge::horizontal()`].
+
+use tabled::{
+ settings::{
+ object::{Cell, Columns, Object, Rows},
+ style::{Border, BorderSpanCorrection, Style},
+ Merge, Modify,
+ },
+ Table, Tabled,
+};
+
+fn main() {
+ let data = [
+ DatabaseTable::new("database_1", "database_1", "table_1", 10712),
+ DatabaseTable::new("database_1", "database_1", "table_2", 57),
+ DatabaseTable::new("database_1", "database_1", "table_3", 57),
+ DatabaseTable::new("database_2", "", "table_1", 72),
+ DatabaseTable::new("database_2", "", "table_2", 75),
+ DatabaseTable::new("database_3", "database_3", "table_1", 20),
+ DatabaseTable::new("database_3", "", "table_2", 21339),
+ DatabaseTable::new("database_3", "", "table_3", 141723),
+ ];
+
+ let mut table = Table::builder(data).index().transpose().build();
+ config_theme(&mut table);
+ table.with(Merge::horizontal()).with(BorderSpanCorrection);
+
+ println!("{table}");
+}
+
+#[derive(Tabled)]
+struct DatabaseTable {
+ #[tabled(rename = "db")]
+ db_name: &'static str,
+ origin_db: &'static str,
+ #[tabled(rename = "table")]
+ table_name: &'static str,
+ total: usize,
+}
+
+impl DatabaseTable {
+ fn new(
+ db_name: &'static str,
+ origin_db: &'static str,
+ table_name: &'static str,
+ total: usize,
+ ) -> Self {
+ Self {
+ db_name,
+ origin_db,
+ table_name,
+ total,
+ }
+ }
+}
+
+fn config_theme(table: &mut Table) {
+ table
+ .with(Style::rounded().remove_vertical())
+ .with(Modify::new(Columns::first()).with(Border::default().right('│')))
+ .with(
+ Modify::new(Cell::new(0, 0)).with(
+ Border::default()
+ .corner_top_right('┬')
+ .corner_bottom_right('┼'),
+ ),
+ )
+ .with(
+ Modify::new(Columns::first().intersect(Rows::last()))
+ .with(Border::default().corner_bottom_right('┴')),
+ );
+}
diff --git a/vendor/tabled/examples/nested_table.rs b/vendor/tabled/examples/nested_table.rs
new file mode 100644
index 000000000..8884c81c1
--- /dev/null
+++ b/vendor/tabled/examples/nested_table.rs
@@ -0,0 +1,117 @@
+//! This example demonstrates how [`Tables`](Table) can be comprised of other tables.
+//!
+//! * This first nested [`Table`] example showcases the [`Builder`] approach.
+//!
+//! * Note how a great deal of manual customizations have been applied to create a
+//! highly unique display.
+//!
+//! * 🎉 Inspired by https://github.com/p-ranav/tabulate#nested-tables/
+
+use std::iter::FromIterator;
+
+use tabled::{
+ builder::Builder,
+ settings::{
+ object::{Rows, Segment},
+ style::{HorizontalLine, Style},
+ Alignment, Modify, Padding, Width,
+ },
+ Table,
+};
+
+fn main() {
+ let animal = create_class(
+ "Animal",
+ &[("age", "Int", ""), ("gender", "String", "")],
+ &["isMammal", "mate"],
+ );
+
+ let duck = create_class(
+ "Duck",
+ &[("beakColor", "String", "yellow")],
+ &["swim", "quack"],
+ );
+
+ let mut table = Builder::from_iter([
+ [animal.to_string()],
+ [String::from("▲")],
+ [String::from("|")],
+ [String::from("|")],
+ [duck.to_string()],
+ ])
+ .build();
+ table.with(Style::ascii().remove_horizontal()).with(
+ Modify::new(Segment::all())
+ .with(Padding::new(5, 5, 0, 0))
+ .with(Alignment::center()),
+ );
+
+ println!("{table}");
+}
+
+fn create_class(name: &str, fields: &[(&str, &str, &str)], methods: &[&str]) -> Table {
+ let fields = fields
+ .iter()
+ .map(|(field, t, d)| [format_field(field, t, d)]);
+ let mut table_fields = Builder::from_iter(fields).build();
+ table_fields.with(Style::ascii().remove_horizontal().remove_vertical());
+
+ let methods = methods.iter().map(|method| [format_method(method)]);
+ let mut table_methods = Builder::from_iter(methods).build();
+ table_methods.with(Style::ascii().remove_horizontal().remove_vertical());
+
+ let (table_fields, table_methods) = make_equal_width(table_fields, table_methods);
+
+ let mut table = Builder::from_iter([
+ [name.to_string()],
+ [table_fields.to_string()],
+ [table_methods.to_string()],
+ ])
+ .build();
+
+ table
+ .with(
+ Style::ascii()
+ .horizontals([HorizontalLine::new(1, Style::ascii().get_horizontal())])
+ .remove_horizontal()
+ .remove_vertical(),
+ )
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Rows::first()).with(Alignment::center()));
+
+ table
+}
+
+fn format_field(field: &&str, field_type: &&str, default_value: &&str) -> String {
+ if default_value.is_empty() {
+ format!("+{field}: {field_type}")
+ } else {
+ format!("+{field}: {field_type} = {default_value:?}")
+ }
+}
+
+fn format_method(method: &str) -> String {
+ format!("+{method}()")
+}
+
+fn make_equal_width(mut table1: Table, mut table2: Table) -> (Table, Table) {
+ // We want to make a fields table and methods table to have the same width.
+ // To not set it to constant, we check a width of each of them and correct the other.
+ //
+ // it's safe to do .len() because we use ascii theme and no colors.
+
+ let table1_width = table1.to_string().lines().next().unwrap().len();
+ let table2_width = table2.to_string().lines().next().unwrap().len();
+
+ match table1_width.cmp(&table2_width) {
+ std::cmp::Ordering::Less => {
+ table1.with(Width::increase(table2_width));
+ }
+ std::cmp::Ordering::Greater => {
+ table2.with(Width::increase(table1_width));
+ }
+ std::cmp::Ordering::Equal => (),
+ }
+
+ (table1, table2)
+}
diff --git a/vendor/tabled/examples/nested_table_2.rs b/vendor/tabled/examples/nested_table_2.rs
new file mode 100644
index 000000000..40b0cd0a9
--- /dev/null
+++ b/vendor/tabled/examples/nested_table_2.rs
@@ -0,0 +1,90 @@
+//! This example demonstrates a minimalist implementation of [`Tabling`](Table) records
+//! with struct fields.
+//!
+//! * This second nested [`Table`] example showcases the [`derive`] approach.
+//!
+//! * Note how the [`display_with`] attribute macro applies the custom `display_distribution`
+//! filter function, which, in this case, applies styles to the final display.
+
+use tabled::{settings::Style, Table, Tabled};
+
+#[derive(Tabled)]
+struct Vendor {
+ name: &'static str,
+ #[tabled(display_with = "display_distribution")]
+ main_os: Distribution,
+ #[tabled(display_with = "display_distribution")]
+ switch_os: Distribution,
+}
+
+impl Vendor {
+ fn new(name: &'static str, main_os: Distribution, switch_os: Distribution) -> Self {
+ Self {
+ name,
+ main_os,
+ switch_os,
+ }
+ }
+}
+
+fn display_distribution(d: &Distribution) -> String {
+ Table::new([d]).with(Style::extended()).to_string()
+}
+
+#[derive(Tabled)]
+struct Distribution {
+ name: &'static str,
+ #[tabled(display_with = "Self::display_based_on")]
+ based_on: Option<&'static str>,
+ is_active: bool,
+ is_cool: bool,
+}
+
+impl Distribution {
+ fn display_based_on(o: &Option<&'static str>) -> String {
+ match o {
+ &Some(s) => s.into(),
+ None => "Independent".into(),
+ }
+ }
+}
+
+impl Distribution {
+ fn new(
+ name: &'static str,
+ based_on: Option<&'static str>,
+ is_active: bool,
+ is_cool: bool,
+ ) -> Self {
+ Self {
+ name,
+ based_on,
+ is_active,
+ is_cool,
+ }
+ }
+}
+
+fn main() {
+ let data = [
+ Vendor::new(
+ "Azure",
+ Distribution::new("Windows", None, true, true),
+ Distribution::new("Manjaro", Some("Arch"), true, true),
+ ),
+ Vendor::new(
+ "AWS",
+ Distribution::new("Debian", None, true, true),
+ Distribution::new("Arch", None, true, true),
+ ),
+ Vendor::new(
+ "GCP",
+ Distribution::new("Debian", None, true, true),
+ Distribution::new("Arch", None, true, true),
+ ),
+ ];
+
+ let table = Table::new(data).with(Style::modern()).to_string();
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/nested_table_3.rs b/vendor/tabled/examples/nested_table_3.rs
new file mode 100644
index 000000000..8e79b27a5
--- /dev/null
+++ b/vendor/tabled/examples/nested_table_3.rs
@@ -0,0 +1,56 @@
+//! This example demonstrates creating a nested [`Table`] by instantiating a new
+//! [`Table`] from a collection of other [`Tables`](Table).
+//!
+//! * This third nested [`Table`] example showcases the [`Table::new()`] approach.
+
+use tabled::{
+ settings::{
+ object::{Cell, Segment},
+ Alignment, Border, Extract, Highlight, Modify, Panel, Style,
+ },
+ Table, Tabled,
+};
+
+#[derive(Tabled)]
+struct Contribution {
+ author: &'static str,
+ profile: &'static str,
+}
+
+impl Contribution {
+ fn new(author: &'static str, profile: &'static str) -> Self {
+ Self { author, profile }
+ }
+}
+
+fn main() {
+ let committers = [
+ Contribution::new("kozmod", "https:/github.com/kozmod"),
+ Contribution::new("IsaacCloos", "https:/github.com/IsaacCloos"),
+ ];
+
+ let issuers = [Contribution::new(
+ "aharpervc",
+ "https:/github.com/aharpervc",
+ )];
+
+ let committers_table = Table::new(committers)
+ .with(Panel::header("Contributors"))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .to_string();
+
+ let issues_table = Table::new(issuers)
+ .with(Panel::header("Issuers"))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .to_string();
+
+ let mut a_welcome_table =
+ Table::new([String::from("Thank You"), committers_table, issues_table]);
+ a_welcome_table
+ .with(Extract::rows(1..))
+ .with(Style::ascii().remove_horizontal())
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Highlight::new(Cell::new(0, 0), Border::filled('*')));
+
+ println!("{a_welcome_table}");
+}
diff --git a/vendor/tabled/examples/panel.rs b/vendor/tabled/examples/panel.rs
new file mode 100644
index 000000000..343abcd67
--- /dev/null
+++ b/vendor/tabled/examples/panel.rs
@@ -0,0 +1,63 @@
+//! This example demonstrates using the [`Panel`] [`TableOption`] to inject
+//! table-length columns and rows into a [`Table`].
+//!
+//! * [`Panel`] supports four injection options:
+//! * Horizontal | manual index selection
+//! * Vertical | manual index selection
+//! * Header | before first row
+//! * Footer | after last row
+
+use tabled::{
+ settings::{
+ object::Segment,
+ style::{BorderSpanCorrection, Style},
+ Alignment, Modify, Panel, Width,
+ },
+ Table, Tabled,
+};
+
+#[derive(Tabled)]
+struct Release {
+ version: &'static str,
+ published_date: &'static str,
+ is_active: bool,
+ major_feature: &'static str,
+}
+
+impl Release {
+ const fn new(
+ version: &'static str,
+ published_date: &'static str,
+ is_active: bool,
+ major_feature: &'static str,
+ ) -> Self {
+ Self {
+ version,
+ published_date,
+ is_active,
+ major_feature,
+ }
+ }
+}
+
+const DATA: [Release; 3] = [
+ Release::new("0.2.1", "2021-06-23", true, "#[header(inline)] attribute"),
+ Release::new("0.2.0", "2021-06-19", false, "API changes"),
+ Release::new("0.1.4", "2021-06-07", false, "display_with attribute"),
+];
+
+fn main() {
+ let mut table = Table::new(DATA);
+ table
+ .with(Panel::header("Tabled Releases"))
+ .with(Panel::footer(format!("N - {}", DATA.len())))
+ .with(Panel::vertical(0, "Some text goes here"))
+ .with(Panel::vertical(5, "Some text goes here"))
+ .with(Modify::new((0, 0)).with(Width::wrap(1)))
+ .with(Modify::new((0, 5)).with(Width::wrap(1)))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::modern())
+ .with(BorderSpanCorrection);
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/pool_table.rs b/vendor/tabled/examples/pool_table.rs
new file mode 100644
index 000000000..593631e06
--- /dev/null
+++ b/vendor/tabled/examples/pool_table.rs
@@ -0,0 +1,33 @@
+//! This example demonstrates a [`PoolTable`] usage.
+
+use tabled::{
+ settings::{Alignment, Style},
+ tables::{PoolTable, TableValue},
+};
+
+fn main() {
+ let data = vec![
+ vec!["Hello World", "Hello World", "Hello World"],
+ vec!["Hello", "", "Hello"],
+ vec!["W", "o", "r", "l", "d"],
+ ];
+
+ let data = TableValue::Column(
+ data.into_iter()
+ .map(|row| {
+ TableValue::Row(
+ row.into_iter()
+ .map(|text| TableValue::Cell(text.to_owned()))
+ .collect(),
+ )
+ })
+ .collect(),
+ );
+
+ let table = PoolTable::from(data)
+ .with(Style::modern())
+ .with(Alignment::center())
+ .to_string();
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/pool_table2.rs b/vendor/tabled/examples/pool_table2.rs
new file mode 100644
index 000000000..02b457250
--- /dev/null
+++ b/vendor/tabled/examples/pool_table2.rs
@@ -0,0 +1,26 @@
+//! This example demonstrates a [`PoolTable`] usage.
+
+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}");
+}
diff --git a/vendor/tabled/examples/rotate.rs b/vendor/tabled/examples/rotate.rs
new file mode 100644
index 000000000..8db997527
--- /dev/null
+++ b/vendor/tabled/examples/rotate.rs
@@ -0,0 +1,38 @@
+//! This example demonstrates using the [`Rotate`] [`TableOption`] to rotate the cells
+//! of a [`Table`].
+//!
+//! * [`Rotate`] supports four motions:
+//! * `Left` | 90 degree shift
+//! * `Right` | 90 degree shift
+//! * `Top` & `Bottom` | Reverse row order
+
+use tabled::{settings::Rotate, Table, Tabled};
+
+#[derive(Tabled)]
+struct Linux {
+ id: u8,
+ distribution: &'static str,
+ link: &'static str,
+}
+
+impl Linux {
+ fn new(id: u8, distribution: &'static str, link: &'static str) -> Self {
+ Self {
+ id,
+ distribution,
+ link,
+ }
+ }
+}
+
+fn main() {
+ let data = vec![
+ Linux::new(0, "Fedora", "https://getfedora.org/"),
+ Linux::new(2, "OpenSUSE", "https://www.opensuse.org/"),
+ Linux::new(3, "Endeavouros", "https://endeavouros.com/"),
+ ];
+
+ let table = Table::new(data).with(Rotate::Left).to_string();
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/settings_list.rs b/vendor/tabled/examples/settings_list.rs
new file mode 100644
index 000000000..b0b0aca4c
--- /dev/null
+++ b/vendor/tabled/examples/settings_list.rs
@@ -0,0 +1,57 @@
+//! This example demonstrates using the [`Settings`] [`TableOption`] to array
+//! [`Table`] configurations in a separate step from instantiation.
+//!
+//! * Note how this methodoly can lead to huge performance gains
+//! with compile-time constants.
+
+use tabled::{
+ settings::{
+ object::{FirstRow, Rows},
+ style::On,
+ Alignment, Modify, ModifyList, Padding, Settings, Style,
+ },
+ Table, Tabled,
+};
+
+#[derive(Tabled)]
+struct CodeEditor {
+ name: &'static str,
+ first_release: &'static str,
+ developer: &'static str,
+}
+
+impl CodeEditor {
+ fn new(name: &'static str, first_release: &'static str, developer: &'static str) -> Self {
+ Self {
+ name,
+ first_release,
+ developer,
+ }
+ }
+}
+
+// unfortunately we can't leave it as a blank type, so we need to provide it.
+type TableTheme = Settings<
+ Settings<Settings<Settings, Style<On, On, On, On, On, On>>, Padding>,
+ ModifyList<FirstRow, Alignment>,
+>;
+
+const THEME: TableTheme = Settings::empty()
+ .with(Style::ascii())
+ .with(Padding::new(1, 3, 0, 0))
+ .with(Modify::list(Rows::first(), Alignment::center()));
+
+fn main() {
+ let data = [
+ CodeEditor::new("Sublime Text 3", "2008", "Sublime HQ"),
+ CodeEditor::new("Visual Studio Code", "2015", "Microsoft"),
+ CodeEditor::new("Notepad++", "2003", "Don Ho"),
+ CodeEditor::new("GNU Emacs", "1984", "Richard Stallman"),
+ CodeEditor::new("Neovim", "2015", "Vim community"),
+ ];
+
+ let mut table = Table::new(data);
+ table.with(THEME);
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/shadow.rs b/vendor/tabled/examples/shadow.rs
new file mode 100644
index 000000000..baa856ed9
--- /dev/null
+++ b/vendor/tabled/examples/shadow.rs
@@ -0,0 +1,160 @@
+//! This example can be run with the following command:
+//!
+//! `echo -e -n 'Some text\nIn the box' | cargo run --example shadow`
+//!
+//! This example demonstrates using the [`Shadow`] [`TableOption`] to create
+//! a striking frame around a [`Table`] display.
+//!
+//! * [`Shadow`] supports several configurations:
+//! * Thickness
+//! * Offset
+//! * Direction
+//! * Color
+//! * Fill character
+//!
+//! * 🎉 Inspired by <https://en.wikipedia.org/wiki/Box-drawing_character>
+
+use std::{io::Read, iter::FromIterator};
+
+use tabled::{
+ builder::Builder,
+ grid::util::string,
+ row,
+ settings::{
+ object::Cell,
+ style::{BorderChar, Offset, RawStyle, Style},
+ Height, Modify, Padding, Shadow, Width,
+ },
+ Table,
+};
+
+fn main() {
+ let message = read_message();
+ print_table(message);
+}
+
+fn print_table(message: String) {
+ let main_table = create_main_table(&message);
+ let main_table_width = main_table.total_width();
+ let small_table_row = create_small_table_list(main_table_width);
+ println!("{small_table_row}");
+ println!("{main_table}");
+}
+
+fn read_message() -> String {
+ let mut buf = String::new();
+ std::io::stdin().read_to_string(&mut buf).unwrap();
+
+ buf
+}
+
+fn create_small_table_list(width_available: usize) -> String {
+ let mut tables = [
+ create_small_table(Style::modern().into()),
+ create_small_table(Style::extended().into()),
+ create_small_table(
+ Style::modern()
+ .left('║')
+ .right('║')
+ .intersection_left('╟')
+ .intersection_right('╢')
+ .corner_top_right('╖')
+ .corner_top_left('╓')
+ .corner_bottom_right('╜')
+ .corner_bottom_left('╙')
+ .into(),
+ ),
+ create_small_table(
+ Style::modern()
+ .top('═')
+ .bottom('═')
+ .corner_top_right('╕')
+ .corner_top_left('╒')
+ .corner_bottom_right('╛')
+ .corner_bottom_left('╘')
+ .horizontal('═')
+ .intersection_left('╞')
+ .intersection_right('╡')
+ .intersection_top('╤')
+ .intersection_bottom('╧')
+ .intersection('╪')
+ .into(),
+ ),
+ ];
+ const TOTAL_TABLE_WIDTH: usize = 19;
+
+ if width_available > TOTAL_TABLE_WIDTH {
+ let mut rest = width_available - TOTAL_TABLE_WIDTH;
+ while rest > 0 {
+ for table in &mut tables {
+ let current_width = table.total_width();
+ table.with(Width::increase(current_width + 1));
+ rest -= 1;
+
+ if rest == 0 {
+ break;
+ }
+ }
+ }
+ }
+
+ let small_table_row = row![tables[0], tables[1], tables[2], tables[3]]
+ .with(Style::blank())
+ .with(Padding::zero())
+ .to_string();
+ small_table_row
+}
+
+fn create_small_table(style: RawStyle) -> Table {
+ let mut table = Builder::from_iter(vec![vec![" ", ""], vec![" ", ""]]).build();
+ table
+ .with(style)
+ .with(Padding::zero())
+ .with(Height::list([1, 0]));
+
+ table
+}
+
+fn create_main_table(message: &str) -> Table {
+ let (count_lines, message_width) = string::string_dimension(message);
+ let count_additional_separators = if count_lines > 2 { count_lines - 2 } else { 0 };
+
+ let left_table = format!(
+ " ╔═══╗ \n ╚═╦═╝ \n{}═╤══╩══╤\n ├──┬──┤\n └──┴──┘",
+ (0..count_additional_separators)
+ .map(|_| " ║ \n")
+ .collect::<String>()
+ );
+
+ let message = if count_lines < 2 {
+ let mut i = count_lines;
+ let mut buf = message.to_string();
+ while i < 2 {
+ buf.push('\n');
+ i += 1;
+ }
+
+ buf
+ } else {
+ message.to_owned()
+ };
+ let count_lines = count_lines.max(2);
+
+ let message = format!("{}\n{}", message, "═".repeat(message_width));
+
+ let mut table = row![left_table, message];
+ table
+ .with(Padding::zero())
+ .with(Style::modern().remove_vertical())
+ .with(
+ Modify::new(Cell::new(0, 0))
+ .with(BorderChar::vertical('╞', Offset::Begin(count_lines))),
+ )
+ .with(
+ Modify::new(Cell::new(0, 2))
+ .with(BorderChar::vertical('╡', Offset::Begin(count_lines))),
+ )
+ .with(Shadow::new(2));
+
+ table
+}
diff --git a/vendor/tabled/examples/span.rs b/vendor/tabled/examples/span.rs
new file mode 100644
index 000000000..e6a75576a
--- /dev/null
+++ b/vendor/tabled/examples/span.rs
@@ -0,0 +1,40 @@
+//! This example demonstrates using the [`Span`] [`CellOption`] to
+//! extend [Cells](Cell) over a specified number of columns/rows.
+//!
+//! * Note how [`Span`] is available for [`Cell`] modifications
+//! after the [`Modify`] [`TableOption`] is applied.
+//!
+//! * ⚠️ `with()` is a reused pattern within [`tabled`] for both [`Table`]
+//! and [`Cell`] modifications. It can be easy for beginners to mistakenly
+//! try to pass [`settings`] intended for one to the other.
+
+use tabled::{
+ settings::{
+ object::Cell,
+ style::{BorderSpanCorrection, Style},
+ Alignment, Modify, Span,
+ },
+ Table,
+};
+
+fn main() {
+ let data = [["just 1 column"; 5]; 5];
+
+ let h_span = |r, c, span| Modify::new(Cell::new(r, c)).with(Span::row(span));
+ let v_span = |r, c, span| Modify::new(Cell::new(r, c)).with(Span::column(span));
+
+ let table = Table::new(data)
+ .with(h_span(0, 0, 5).with("span all 5 columns"))
+ .with(h_span(1, 0, 4).with("span 4 columns"))
+ .with(h_span(2, 0, 2).with("span 2 columns"))
+ .with(v_span(2, 4, 4).with("just 1 column\nspan\n4\ncolumns"))
+ .with(v_span(3, 1, 2).with("span 2 columns\nspan\n2\ncolumns"))
+ .with(v_span(2, 3, 3).with("just 1 column\nspan\n3\ncolumns"))
+ .with(h_span(3, 1, 2))
+ .with(Style::modern())
+ .with(BorderSpanCorrection)
+ .with(Alignment::center_vertical())
+ .to_string();
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/split.rs b/vendor/tabled/examples/split.rs
new file mode 100644
index 000000000..d8e6c679a
--- /dev/null
+++ b/vendor/tabled/examples/split.rs
@@ -0,0 +1,43 @@
+//! This example demonstrates using the [`Split`] [`TableOption`] to
+//! transform a [`Table`] display in multiple ways.
+//!
+//! * Several configurations are available to customize a [`Split`] instruction:
+//! * [`Index`](usize)
+//! * [`Behavior`]
+//! * [`Direction`]
+//! * [`Display`]
+
+use std::iter::FromIterator;
+use tabled::{
+ col, row,
+ settings::{split::Split, style::Style, Padding},
+ Table,
+};
+
+fn main() {
+ let mut table = Table::from_iter(['a'..='z']);
+ table.with(Style::modern());
+
+ let table_1 = table.clone().with(Split::column(12)).clone();
+ let table_2 = table_1.clone().with(Split::column(2).zip()).to_string();
+ let table_3 = table_1.clone().with(Split::column(2).concat()).to_string();
+ let table_4 = table_1.clone().with(Split::row(2).zip()).to_string();
+ let table_5 = table_1.clone().with(Split::row(2).concat()).to_string();
+
+ let mut table = col![
+ table,
+ row![
+ table_1,
+ table_2,
+ table_3,
+ col![table_4, table_5]
+ .with(Style::blank())
+ .with(Padding::zero())
+ ]
+ .with(Style::blank())
+ .with(Padding::zero()),
+ ];
+ table.with(Style::blank());
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/table.rs b/vendor/tabled/examples/table.rs
new file mode 100644
index 000000000..d6d5c392c
--- /dev/null
+++ b/vendor/tabled/examples/table.rs
@@ -0,0 +1,56 @@
+//! This example demonstrates the fundemental qualities of the [crate](https://crates.io/crates/tabled) [`tabled`].
+//!
+//! * [`tabled`] is powered by convenient [procedural macros](https://doc.rust-lang.org/reference/procedural-macros.html#procedural-macros)
+//! like [`Tabled`]; a deriveable trait that allows your custom
+//! structs and enums to be represented with [`tabled`]'s powerful suite of features.
+//!
+//! * [`Table`] is the root object to which all of [`tabled`]'s implementation
+//! tools guide you, and from which all of its customization options derive value.
+//! The READMEs, examples, and docs found in this project will show you many dozens
+//! of ways you can build tables, and show you how to use the available tools
+//! to build the data representations that best fit your needs.
+//!
+//! * [`Table::with()`] plays a central role in giving the user control over their
+//! displays. A majority of [`Table`] customizations are implemented through this highly
+//! dynamic function. A few [`TableOptions`](TableOption) include:
+//! * [`Style`]
+//! * [`Modify`]
+//! * [`Alignment`]
+//! * [`Padding`]
+
+use tabled::{
+ settings::{object::Rows, Alignment, Modify, Style},
+ Table, Tabled,
+};
+
+#[derive(Debug, Tabled)]
+struct Distribution {
+ name: String,
+ based_on: String,
+ is_active: bool,
+}
+
+impl Distribution {
+ fn new(name: &str, base: &str, is_active: bool) -> Self {
+ Self {
+ based_on: base.to_owned(),
+ name: name.to_owned(),
+ is_active,
+ }
+ }
+}
+
+fn main() {
+ let data = [
+ Distribution::new("Debian", "", true),
+ Distribution::new("Arch", "", true),
+ Distribution::new("Manjaro", "Arch", true),
+ ];
+
+ let mut table = Table::new(data);
+ table
+ .with(Style::markdown())
+ .with(Modify::new(Rows::first()).with(Alignment::center()));
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/examples/table_width.rs b/vendor/tabled/examples/table_width.rs
new file mode 100644
index 000000000..372adb489
--- /dev/null
+++ b/vendor/tabled/examples/table_width.rs
@@ -0,0 +1,40 @@
+//! This example demonstrates using the [`Width`] [`TableOption`] to expand and
+//! contract a [`Table`] display.
+//!
+//! * Note how table-wide size adjustments are applied proportionally to all columns.
+//!
+//! * Note how [fluent](https://en.wikipedia.org/wiki/Fluent_interface) functions
+//! are available to make subtle customizations to [`Width`] primary features like
+//! [`Width::truncate()`], [`Width::increase()`], and [`Width::wrap()`].
+
+use tabled::{
+ settings::{measurement::Percent, object::Segment, Alignment, Modify, Style, Width},
+ Table,
+};
+
+fn main() {
+ let data = [
+ ["Hello World!!!", "3.3.22.2"],
+ ["Guten Morgen", "1.1.1.1"],
+ ["Добры вечар", "127.0.0.1"],
+ ["Bonjour le monde", ""],
+ ["Ciao mondo", ""],
+ ];
+
+ let mut table = Table::builder(data).build();
+ table.with(Style::markdown()).with(Alignment::left());
+
+ println!("Original table\n{table}\n");
+
+ table.with(Width::truncate(20).suffix("..."));
+
+ println!("Truncated table\n{table}\n");
+
+ table.with(Modify::new(Segment::all()).with(Width::wrap(5)));
+
+ println!("Wrapped table\n{table}\n");
+
+ table.with(Width::increase(Percent(200)));
+
+ println!("Widen table\n{table}");
+}
diff --git a/vendor/tabled/examples/table_width_2.rs b/vendor/tabled/examples/table_width_2.rs
new file mode 100644
index 000000000..d2601fc87
--- /dev/null
+++ b/vendor/tabled/examples/table_width_2.rs
@@ -0,0 +1,23 @@
+//! This example demonstrates using [`Wrap::keep_words()`] to preserve
+//! word shape while truncating a table to the specified size. Without
+//! this setting enabled, a word could possibly be split into pieces,
+//! greatly reducing the legibility of the display.
+
+use tabled::{
+ settings::{object::Segment, Alignment, Modify, Style, Width},
+ Table,
+};
+
+fn main() {
+ let readme_text = include_str!("../../CHANGELOG.md");
+ let lines = readme_text.lines().filter(|s| !s.is_empty()).enumerate();
+
+ let mut table = Table::new(lines);
+ table.with(Style::ascii_rounded()).with(
+ Modify::new(Segment::all())
+ .with(Width::wrap(30).keep_words())
+ .with(Alignment::left()),
+ );
+
+ println!("{table}");
+}
diff --git a/vendor/tabled/src/builder/index_builder.rs b/vendor/tabled/src/builder/index_builder.rs
new file mode 100644
index 000000000..081f580df
--- /dev/null
+++ b/vendor/tabled/src/builder/index_builder.rs
@@ -0,0 +1,318 @@
+use crate::Table;
+
+use super::Builder;
+
+/// [`IndexBuilder`] helps to add an index to the table.
+///
+/// Index is a column on the left of the table.
+///
+/// It also can be used to transpose the table.
+///
+/// Creates a new [`IndexBuilder`] instance.
+///
+/// It creates a default index a range from 0 to N. (N - count rows)
+/// It also sets a default columns to the range 0 .. N (N - count columns).
+///nfo<'a>
+/// # Example
+///
+/// ```
+/// use tabled::builder::Builder;
+///
+/// let mut builder = Builder::default();
+/// builder.set_header(["i", "col-1", "col-2"]);
+/// builder.push_record(["0", "value-1", "value-2"]);
+///
+/// let table = builder.index().build().to_string();
+///
+/// assert_eq!(
+/// table,
+/// "+---+---+---------+---------+\n\
+/// | | i | col-1 | col-2 |\n\
+/// +---+---+---------+---------+\n\
+/// | 0 | 0 | value-1 | value-2 |\n\
+/// +---+---+---------+---------+"
+/// )
+/// ```
+///
+/// # Example
+///
+/// ```
+/// use tabled::builder::Builder;
+///
+/// let table = Builder::default()
+/// .index()
+/// .build();
+/// ```
+#[derive(Debug, Clone)]
+pub struct IndexBuilder {
+ /// Index is an index data.
+ /// It's always set.
+ index: Vec<String>,
+ /// Name of an index
+ name: Option<String>,
+ /// A flag which checks if we need to actually use index.
+ ///
+ /// It might happen when it's only necessary to [`Self::transpose`] table.
+ print_index: bool,
+ /// A flag which checks if table was transposed.
+ transposed: bool,
+ /// Data originated in [`Builder`].
+ data: Vec<Vec<String>>,
+}
+
+impl IndexBuilder {
+ /// No flag makes builder to not use an index.
+ ///
+ /// It may be useful when only [`Self::transpose`] need to be used.
+ ///
+ /// ```
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::default();
+ /// builder.set_header(["i", "col-1", "col-2"]);
+ /// builder.push_record(["0", "value-1", "value-2"]);
+ /// builder.push_record(["2", "value-3", "value-4"]);
+ ///
+ /// let table = builder.index().hide().build().to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+---+---------+---------+\n\
+ /// | i | col-1 | col-2 |\n\
+ /// +---+---------+---------+\n\
+ /// | 0 | value-1 | value-2 |\n\
+ /// +---+---------+---------+\n\
+ /// | 2 | value-3 | value-4 |\n\
+ /// +---+---------+---------+"
+ /// )
+ /// ```
+ pub fn hide(mut self) -> Self {
+ self.print_index = false;
+ self
+ }
+
+ /// Set an index name.
+ ///
+ /// When [`None`] the name won't be used.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::default();
+ /// builder.set_header(["i", "column1", "column2"]);
+ /// builder.push_record(["0", "value1", "value2"]);
+ ///
+ /// let table = builder.index()
+ /// .column(1)
+ /// .name(Some(String::from("index")))
+ /// .build();
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+--------+---+---------+\n\
+ /// | | i | column2 |\n\
+ /// +--------+---+---------+\n\
+ /// | index | | |\n\
+ /// +--------+---+---------+\n\
+ /// | value1 | 0 | value2 |\n\
+ /// +--------+---+---------+"
+ /// )
+ /// ```
+ pub fn name(mut self, name: Option<String>) -> Self {
+ self.name = name;
+ self
+ }
+
+ /// Sets a index to the chosen column.
+ ///
+ /// Also sets a name of the index to the column name.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::default();
+ /// builder.set_header(["i", "column1", "column2"]);
+ /// builder.push_record(["0", "value1", "value2"]);
+ ///
+ /// let table = builder.index().column(1).build();
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+---------+---+---------+\n\
+ /// | | i | column2 |\n\
+ /// +---------+---+---------+\n\
+ /// | column1 | | |\n\
+ /// +---------+---+---------+\n\
+ /// | value1 | 0 | value2 |\n\
+ /// +---------+---+---------+"
+ /// )
+ /// ```
+ pub fn column(mut self, column: usize) -> Self {
+ if column >= matrix_count_columns(&self.data) {
+ return self;
+ }
+
+ self.index = get_column(&mut self.data, column);
+
+ let name = remove_or_default(&mut self.index, 0);
+ self.name = Some(name);
+
+ self
+ }
+
+ /// Transpose index and columns.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::default();
+ /// builder.set_header(["i", "column-1", "column-2", "column-3"]);
+ /// builder.push_record(["0", "value-1", "value-2", "value-3"]);
+ /// builder.push_record(["1", "value-4", "value-5", "value-6"]);
+ /// builder.push_record(["2", "value-7", "value-8", "value-9"]);
+ ///
+ /// let table = builder.index().column(1).transpose().build();
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+----------+---------+---------+---------+\n\
+ /// | column-1 | value-1 | value-4 | value-7 |\n\
+ /// +----------+---------+---------+---------+\n\
+ /// | i | 0 | 1 | 2 |\n\
+ /// +----------+---------+---------+---------+\n\
+ /// | column-2 | value-2 | value-5 | value-8 |\n\
+ /// +----------+---------+---------+---------+\n\
+ /// | column-3 | value-3 | value-6 | value-9 |\n\
+ /// +----------+---------+---------+---------+"
+ /// )
+ /// ```
+ pub fn transpose(mut self) -> Self {
+ let columns = &mut self.data[0];
+ std::mem::swap(&mut self.index, columns);
+
+ let columns = self.data.remove(0);
+
+ make_rows_columns(&mut self.data);
+
+ self.data.insert(0, columns);
+
+ self.transposed = !self.transposed;
+
+ self
+ }
+
+ /// Builds a table.
+ pub fn build(self) -> Table {
+ let builder: Builder = self.into();
+ builder.build()
+ }
+}
+
+impl From<Builder> for IndexBuilder {
+ fn from(builder: Builder) -> Self {
+ let has_header = builder.has_header();
+
+ let mut data: Vec<Vec<_>> = builder.into();
+
+ if !has_header {
+ let count_columns = matrix_count_columns(&data);
+ data.insert(0, build_range_index(count_columns));
+ }
+
+ // we exclude first row which contains a header
+ let data_len = data.len().saturating_sub(1);
+ let index = build_range_index(data_len);
+
+ Self {
+ index,
+ name: None,
+ print_index: true,
+ transposed: false,
+ data,
+ }
+ }
+}
+
+impl From<IndexBuilder> for Builder {
+ fn from(b: IndexBuilder) -> Self {
+ build_index(b)
+ }
+}
+
+fn build_index(mut b: IndexBuilder) -> Builder {
+ if b.index.is_empty() {
+ return Builder::default();
+ }
+
+ // add index column
+ if b.print_index {
+ b.index.insert(0, String::default());
+
+ insert_column(&mut b.data, b.index, 0);
+ }
+
+ if let Some(name) = b.name {
+ if b.transposed && b.print_index {
+ b.data[0][0] = name;
+ } else {
+ b.data.insert(1, vec![name]);
+ }
+ }
+
+ Builder::from(b.data)
+}
+
+fn build_range_index(n: usize) -> Vec<String> {
+ (0..n).map(|i| i.to_string()).collect()
+}
+
+fn remove_or_default<T: Default>(v: &mut Vec<T>, i: usize) -> T {
+ if v.len() > i {
+ v.remove(i)
+ } else {
+ T::default()
+ }
+}
+
+fn get_column<T: Default>(v: &mut [Vec<T>], col: usize) -> Vec<T> {
+ let mut column = Vec::with_capacity(v.len());
+ for row in v.iter_mut() {
+ let value = remove_or_default(row, col);
+ column.push(value);
+ }
+
+ column
+}
+
+fn make_rows_columns<T: Default>(v: &mut Vec<Vec<T>>) {
+ let count_columns = matrix_count_columns(v);
+
+ let mut columns = Vec::with_capacity(count_columns);
+ for _ in 0..count_columns {
+ let column = get_column(v, 0);
+ columns.push(column);
+ }
+
+ v.clear();
+
+ for column in columns {
+ v.push(column);
+ }
+}
+
+fn insert_column<T: Default>(v: &mut [Vec<T>], mut column: Vec<T>, col: usize) {
+ for row in v.iter_mut() {
+ let value = remove_or_default(&mut column, col);
+ row.insert(col, value);
+ }
+}
+
+fn matrix_count_columns<T>(v: &[Vec<T>]) -> usize {
+ v.first().map_or(0, |row| row.len())
+}
diff --git a/vendor/tabled/src/builder/mod.rs b/vendor/tabled/src/builder/mod.rs
new file mode 100644
index 000000000..9002ba237
--- /dev/null
+++ b/vendor/tabled/src/builder/mod.rs
@@ -0,0 +1,118 @@
+//! Builder module provides a [`Builder`] type which helps building
+//! a [`Table`] dynamically.
+//!
+//! It also contains [`IndexBuilder`] which can help to build a table with index.
+//!
+//! # Examples
+//!
+//! Here's an example of [`IndexBuilder`] usage
+//!
+#![cfg_attr(feature = "derive", doc = "```")]
+#![cfg_attr(not(feature = "derive"), doc = "```ignore")]
+//! use tabled::{Table, Tabled, settings::Style};
+//!
+//! #[derive(Tabled)]
+//! struct Mission {
+//! name: &'static str,
+//! #[tabled(inline)]
+//! status: Status,
+//! }
+//!
+//! #[derive(Tabled)]
+//! enum Status {
+//! Complete,
+//! Started,
+//! Ready,
+//! Unknown,
+//! }
+//!
+//! let data = [
+//! Mission { name: "Algebra", status: Status::Unknown },
+//! Mission { name: "Apolo", status: Status::Complete },
+//! ];
+//!
+//! let mut builder = Table::builder(&data)
+//! .index()
+//! .column(0)
+//! .name(None)
+//! .transpose();
+//!
+//! let mut table = builder.build();
+//! table.with(Style::modern());
+//!
+//! println!("{}", table);
+//!
+//! assert_eq!(
+//! table.to_string(),
+//! concat!(
+//! "┌──────────┬─────────┬───────┐\n",
+//! "│ │ Algebra │ Apolo │\n",
+//! "├──────────┼─────────┼───────┤\n",
+//! "│ Complete │ │ + │\n",
+//! "├──────────┼─────────┼───────┤\n",
+//! "│ Started │ │ │\n",
+//! "├──────────┼─────────┼───────┤\n",
+//! "│ Ready │ │ │\n",
+//! "├──────────┼─────────┼───────┤\n",
+//! "│ Unknown │ + │ │\n",
+//! "└──────────┴─────────┴───────┘",
+//! ),
+//! )
+//! ```
+//!
+//! Example when we don't want to show empty data of enum where not all variants are used.
+//!
+#![cfg_attr(feature = "derive", doc = "```")]
+#![cfg_attr(not(feature = "derive"), doc = "```ignore")]
+//! use tabled::{Table, Tabled, settings::Style};
+//!
+//! #[derive(Tabled)]
+//! enum Status {
+//! #[tabled(inline)]
+//! Complete {
+//! started_timestamp: usize,
+//! finihsed_timestamp: usize,
+//! },
+//! #[tabled(inline)]
+//! Started {
+//! timestamp: usize,
+//! },
+//! Ready,
+//! Unknown,
+//! }
+//!
+//! let data = [
+//! Status::Unknown,
+//! Status::Complete { started_timestamp: 123, finihsed_timestamp: 234 },
+//! ];
+//!
+//! let mut builder = Table::builder(&data);
+//! builder.clean();
+//!
+//! let table = builder.build()
+//! .with(Style::modern())
+//! .to_string();
+//!
+//! println!("{}", table);
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "┌───────────────────┬────────────────────┬─────────┐\n",
+//! "│ started_timestamp │ finihsed_timestamp │ Unknown │\n",
+//! "├───────────────────┼────────────────────┼─────────┤\n",
+//! "│ │ │ + │\n",
+//! "├───────────────────┼────────────────────┼─────────┤\n",
+//! "│ 123 │ 234 │ │\n",
+//! "└───────────────────┴────────────────────┴─────────┘",
+//! ),
+//! )
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+mod index_builder;
+mod table_builder;
+
+pub use index_builder::IndexBuilder;
+pub use table_builder::Builder;
diff --git a/vendor/tabled/src/builder/table_builder.rs b/vendor/tabled/src/builder/table_builder.rs
new file mode 100644
index 000000000..40316494e
--- /dev/null
+++ b/vendor/tabled/src/builder/table_builder.rs
@@ -0,0 +1,506 @@
+use std::iter::FromIterator;
+
+use crate::{grid::records::vec_records::CellInfo, Table};
+
+use super::IndexBuilder;
+
+/// Builder creates a [`Table`] from dynamic data set.
+///
+/// It useful when the amount of columns or rows is not known statically.
+///
+/// ```rust
+/// use tabled::builder::Builder;
+///
+/// let mut builder = Builder::default();
+/// builder.set_header(["index", "measure", "value"]);
+/// builder.push_record(["0", "weight", "0.443"]);
+///
+/// let table = builder.build();
+///
+/// println!("{}", table);
+/// ```
+///
+/// It may be useful to use [`FromIterator`] for building.
+///
+/// ```rust
+/// use tabled::builder::Builder;
+/// use std::iter::FromIterator;
+///
+/// let data = vec![
+/// ["column1", "column2"],
+/// ["data1", "data2"],
+/// ["data3", "data4"],
+/// ];
+///
+/// let table = Builder::from_iter(data).build();
+///
+/// println!("{}", table);
+/// ```
+#[derive(Debug, Default, Clone)]
+pub struct Builder {
+ /// A list of rows.
+ data: Vec<Vec<CellInfo<String>>>,
+ /// A columns row.
+ columns: Option<Vec<CellInfo<String>>>,
+ /// A number of columns.
+ count_columns: usize,
+ /// A flag that the rows are not consistent.
+ is_consistent: bool,
+ /// A content of cells which are created in case rows has different length.
+ empty_cell_text: Option<String>,
+}
+
+impl Builder {
+ /// Creates a [`Builder`] instance.
+ ///
+ /// ```
+ /// use tabled::builder::Builder;
+ ///
+ /// let builder = Builder::new();
+ /// ```
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Creates a [`Builder`] instance with a given row capacity.
+ ///
+ /// ```
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::with_capacity(2);
+ /// builder.push_record((0..3).map(|i| i.to_string()));
+ /// builder.push_record(["i", "surname", "lastname"]);
+ /// ```
+ pub fn with_capacity(capacity: usize) -> Self {
+ let mut b = Self::new();
+ b.data = Vec::with_capacity(capacity);
+
+ b
+ }
+
+ /// Sets a [`Table`] header.
+ ///
+ /// ```
+ /// # use tabled::builder::Builder;
+ /// let mut builder = Builder::default();
+ /// builder.set_header((0..3).map(|i| i.to_string()));
+ /// ```
+ pub fn set_header<H, T>(&mut self, columns: H) -> &mut Self
+ where
+ H: IntoIterator<Item = T>,
+ T: Into<String>,
+ {
+ let list = create_row(columns, self.count_columns);
+
+ self.update_size(list.len());
+ self.columns = Some(list);
+
+ self
+ }
+
+ /// Sets off a [`Table`] header.
+ ///
+ /// If not set its a nop.
+ ///
+ /// ```rust
+ /// use tabled::Table;
+ ///
+ /// let data = [("Hello", 1u8, false), ("World", 21u8, true)];
+ ///
+ /// let table = Table::builder(data).build().to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+-------+----+-------+\n\
+ /// | &str | u8 | bool |\n\
+ /// +-------+----+-------+\n\
+ /// | Hello | 1 | false |\n\
+ /// +-------+----+-------+\n\
+ /// | World | 21 | true |\n\
+ /// +-------+----+-------+"
+ /// );
+ ///
+ ///
+ /// let mut builder = Table::builder(data);
+ /// builder.remove_header();
+ /// let table = builder.build().to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+-------+----+-------+\n\
+ /// | Hello | 1 | false |\n\
+ /// +-------+----+-------+\n\
+ /// | World | 21 | true |\n\
+ /// +-------+----+-------+"
+ /// );
+ ///
+ /// ```
+ pub fn remove_header(&mut self) -> &mut Self {
+ self.columns = None;
+ self.count_columns = self.get_size();
+
+ self
+ }
+
+ /// Sets a content of cells which are created in case rows has different length.
+ ///
+ ///
+ /// ```rust
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::default();
+ /// builder
+ /// .set_default_text("undefined")
+ /// .set_header((0..3).map(|i| i.to_string()))
+ /// .push_record(["i"]);
+ /// ```
+ pub fn set_default_text<T>(&mut self, text: T) -> &mut Self
+ where
+ T: Into<String>,
+ {
+ self.empty_cell_text = Some(text.into());
+ self
+ }
+
+ /// Build creates a [`Table`] instance.
+ ///
+ /// ```rust
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::default();
+ /// builder.set_header(["i", "column1", "column2"]);
+ /// builder.push_record(["0", "value1", "value2"]);
+ /// ```
+ pub fn build(self) -> Table {
+ Table::from(self)
+ }
+
+ /// Add an index to the [`Table`].
+ ///
+ /// Default index is a range 0-N where N is amount of records.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::Table;
+ ///
+ /// let table = Table::builder(&["Hello", "World", "!"]).index().build();
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+---+-------+\n\
+ /// | | &str |\n\
+ /// +---+-------+\n\
+ /// | 0 | Hello |\n\
+ /// +---+-------+\n\
+ /// | 1 | World |\n\
+ /// +---+-------+\n\
+ /// | 2 | ! |\n\
+ /// +---+-------+"
+ /// )
+ /// ```
+ pub fn index(self) -> IndexBuilder {
+ IndexBuilder::from(self)
+ }
+
+ /// Adds a row to a [`Table`].
+ ///
+ /// ```
+ /// use tabled::builder::Builder;
+ ///
+ /// let mut builder = Builder::default();
+ /// builder.push_record((0..3).map(|i| i.to_string()));
+ /// builder.push_record(["i", "surname", "lastname"]);
+ /// ```
+ pub fn push_record<R, T>(&mut self, row: R) -> &mut Self
+ where
+ R: IntoIterator<Item = T>,
+ T: Into<String>,
+ {
+ let list = create_row(row, self.count_columns);
+
+ self.update_size(list.len());
+ self.data.push(list);
+
+ self
+ }
+
+ /// Insert a row into a specific position.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `index > count_rows`.
+ pub fn insert_record<R>(&mut self, index: usize, record: R) -> bool
+ where
+ R: IntoIterator,
+ R::Item: Into<String>,
+ {
+ let list = create_row(record, self.count_columns);
+
+ self.update_size(list.len());
+ self.data.insert(index, list);
+
+ true
+ }
+
+ /// Clean removes empty columns and rows.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::Table;
+ ///
+ /// let mut builder = Table::builder(&["Hello", "World", ""]);
+ /// builder.clean();
+ ///
+ /// let table = builder.build();
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+-------+\n\
+ /// | &str |\n\
+ /// +-------+\n\
+ /// | Hello |\n\
+ /// +-------+\n\
+ /// | World |\n\
+ /// +-------+"
+ /// )
+ /// ```
+ pub fn clean(&mut self) -> &mut Self {
+ self.clean_columns();
+ self.clean_rows();
+ self
+ }
+
+ /// Set a column size.
+ ///
+ /// If it make it lower then it was originally it is considered NOP.
+ pub fn hint_column_size(&mut self, size: usize) -> &mut Self {
+ self.count_columns = size;
+ self.is_consistent = true;
+ self
+ }
+
+ /// Returns an amount of columns which would be present in a built table.
+ pub fn count_columns(&self) -> usize {
+ self.count_columns
+ }
+
+ /// Returns an amount of rows which would be present in a built table.
+ pub fn count_rows(&self) -> usize {
+ self.data.len()
+ }
+
+ /// Checks whether a builder contains a header set.
+ pub fn has_header(&self) -> bool {
+ self.columns.is_some()
+ }
+
+ fn clean_columns(&mut self) {
+ let mut i = 0;
+ for col in 0..self.count_columns {
+ let col = col - i;
+
+ let mut is_empty = true;
+ for row in 0..self.data.len() {
+ let cell = &self.data[row][col];
+ if !cell.as_ref().is_empty() {
+ is_empty = false;
+ break;
+ }
+ }
+
+ if is_empty {
+ for row in 0..self.data.len() {
+ let _ = self.data[row].remove(col);
+ }
+
+ if let Some(columns) = self.columns.as_mut() {
+ if columns.len() > col {
+ let _ = columns.remove(col);
+ }
+ }
+
+ i += 1;
+ }
+ }
+
+ self.count_columns -= i;
+ }
+
+ fn clean_rows(&mut self) {
+ for row in (0..self.data.len()).rev() {
+ let mut is_empty = true;
+ for col in 0..self.count_columns {
+ let cell = &self.data[row][col];
+ if !cell.as_ref().is_empty() {
+ is_empty = false;
+ break;
+ }
+ }
+
+ if is_empty {
+ let _ = self.data.remove(row);
+ }
+
+ if row == 0 {
+ break;
+ }
+ }
+ }
+
+ fn update_size(&mut self, size: usize) {
+ use std::cmp::Ordering;
+
+ match size.cmp(&self.count_columns) {
+ Ordering::Less => {
+ if !self.data.is_empty() {
+ self.is_consistent = false;
+ }
+ }
+ Ordering::Greater => {
+ self.count_columns = size;
+
+ if !self.data.is_empty() || self.columns.is_some() {
+ self.is_consistent = false;
+ }
+ }
+ Ordering::Equal => (),
+ }
+ }
+
+ fn get_size(&mut self) -> usize {
+ let mut max = self.columns.as_ref().map_or(0, Vec::len);
+ let max_records = self.data.iter().map(Vec::len).max().unwrap_or(0);
+ max = std::cmp::max(max_records, max);
+
+ max
+ }
+
+ fn fix_rows(&mut self) {
+ let empty_cell = self.empty_cell_text.to_owned().unwrap_or_default();
+ let empty = CellInfo::new(empty_cell);
+
+ if let Some(header) = self.columns.as_mut() {
+ if self.count_columns > header.len() {
+ let count = self.count_columns - header.len();
+ append_vec(header, empty.clone(), count);
+ }
+ }
+
+ for row in &mut self.data {
+ if self.count_columns > row.len() {
+ let count = self.count_columns - row.len();
+ append_vec(row, empty.clone(), count);
+ }
+ }
+ }
+}
+
+impl From<Builder> for Vec<Vec<String>> {
+ fn from(mut builder: Builder) -> Self {
+ if !builder.is_consistent {
+ builder.fix_rows();
+ }
+
+ if let Some(columns) = builder.columns {
+ builder.data.insert(0, columns);
+ }
+
+ builder
+ .data
+ .into_iter()
+ .map(|row| row.into_iter().map(|c| c.into_inner()).collect())
+ .collect()
+ }
+}
+
+impl From<Builder> for Vec<Vec<CellInfo<String>>> {
+ fn from(mut builder: Builder) -> Self {
+ if !builder.is_consistent {
+ builder.fix_rows();
+ }
+
+ if let Some(columns) = builder.columns {
+ builder.data.insert(0, columns);
+ }
+
+ builder.data
+ }
+}
+
+impl<R, V> FromIterator<R> for Builder
+where
+ R: IntoIterator<Item = V>,
+ V: Into<String>,
+{
+ fn from_iter<T: IntoIterator<Item = R>>(iter: T) -> Self {
+ let mut builder = Self::default();
+ for row in iter {
+ let _ = builder.push_record(row);
+ }
+
+ builder
+ }
+}
+
+impl<D> Extend<D> for Builder
+where
+ D: Into<String>,
+{
+ fn extend<T: IntoIterator<Item = D>>(&mut self, iter: T) {
+ let _ = self.push_record(iter);
+ }
+}
+
+impl From<Vec<Vec<String>>> for Builder {
+ fn from(data: Vec<Vec<String>>) -> Self {
+ let count_columns = data.get(0).map_or(0, |row| row.len());
+
+ let data = data
+ .into_iter()
+ .map(|row| row.into_iter().map(CellInfo::new).collect())
+ .collect();
+
+ Self {
+ data,
+ count_columns,
+ columns: None,
+ is_consistent: false,
+ empty_cell_text: None,
+ }
+ }
+}
+
+impl From<Vec<Vec<CellInfo<String>>>> for Builder {
+ fn from(data: Vec<Vec<CellInfo<String>>>) -> Self {
+ let count_columns = data.get(0).map_or(0, |row| row.len());
+
+ Self {
+ data,
+ count_columns,
+ columns: None,
+ is_consistent: false,
+ empty_cell_text: None,
+ }
+ }
+}
+
+fn create_row<R, T>(row: R, size: usize) -> Vec<CellInfo<String>>
+where
+ R: IntoIterator<Item = T>,
+ T: Into<String>,
+{
+ let mut list = Vec::with_capacity(size);
+ for text in row {
+ let text = text.into();
+ let info = CellInfo::new(text);
+ list.push(info);
+ }
+
+ list
+}
+
+fn append_vec<T: Clone>(v: &mut Vec<T>, value: T, n: usize) {
+ v.extend((0..n).map(|_| value.clone()));
+}
diff --git a/vendor/tabled/src/grid/colored_config.rs b/vendor/tabled/src/grid/colored_config.rs
new file mode 100644
index 000000000..51a00fbf4
--- /dev/null
+++ b/vendor/tabled/src/grid/colored_config.rs
@@ -0,0 +1,112 @@
+use std::ops::{Deref, DerefMut};
+
+use crate::grid::{
+ color::AnsiColor,
+ config::{Entity, EntityMap, SpannedConfig},
+};
+
+/// A spanned configuration plus colors for cells.
+#[derive(Debug, Default, PartialEq, Eq, Clone)]
+pub struct ColoredConfig {
+ config: SpannedConfig,
+ colors: ColorMap,
+}
+
+impl ColoredConfig {
+ /// Create a new colored config.
+ pub fn new(config: SpannedConfig) -> Self {
+ Self {
+ config,
+ colors: ColorMap::default(),
+ }
+ }
+
+ /// Set a color for a given cell.
+ ///
+ /// The outcome is the same as if you'd use [`Format`] and added a color but it'd work only with `color` feature on.
+ /// While this method works in all contexts.
+ ///
+ /// [`Format`]: crate::settings::Format
+ pub fn set_color(&mut self, pos: Entity, color: AnsiColor<'static>) -> &mut Self {
+ match self.colors.0.as_mut() {
+ Some(map) => map.insert(pos, color),
+ None => {
+ let mut colors = EntityMap::default();
+ colors.insert(pos, color);
+ self.colors = ColorMap(Some(colors));
+ }
+ }
+
+ self
+ }
+
+ /// Set a list of colors.
+ pub fn set_colors(&mut self, colors: EntityMap<AnsiColor<'static>>) -> &mut Self {
+ self.colors = ColorMap(Some(colors));
+ self
+ }
+
+ /// Remove a color for a given cell.
+ pub fn remove_color(&mut self, pos: Entity) -> &mut Self {
+ if let Some(colors) = self.colors.0.as_mut() {
+ colors.remove(pos);
+ }
+
+ self
+ }
+
+ /// Returns a list of colors.
+ pub fn get_colors(&self) -> &ColorMap {
+ &self.colors
+ }
+
+ /// Returns an inner config.
+ pub fn into_inner(self) -> SpannedConfig {
+ self.config
+ }
+}
+
+impl Deref for ColoredConfig {
+ type Target = SpannedConfig;
+
+ fn deref(&self) -> &Self::Target {
+ &self.config
+ }
+}
+
+impl DerefMut for ColoredConfig {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.config
+ }
+}
+
+impl From<SpannedConfig> for ColoredConfig {
+ fn from(value: SpannedConfig) -> Self {
+ Self::new(value)
+ }
+}
+
+impl AsRef<SpannedConfig> for ColoredConfig {
+ fn as_ref(&self) -> &SpannedConfig {
+ &self.config
+ }
+}
+
+/// A colors structure for [`ColoredConfig`].
+#[derive(Debug, Default, PartialEq, Eq, Clone)]
+pub struct ColorMap(Option<EntityMap<AnsiColor<'static>>>);
+
+impl ColorMap {
+ /// Checks if any colors is set on.
+ pub fn is_empty(&self) -> bool {
+ self.0.is_none()
+ }
+}
+
+impl crate::grid::colors::Colors for ColorMap {
+ type Color = AnsiColor<'static>;
+
+ fn get_color(&self, (row, col): (usize, usize)) -> Option<&Self::Color> {
+ self.0.as_ref().map(|map| map.get(Entity::Cell(row, col)))
+ }
+}
diff --git a/vendor/tabled/src/grid/compact_multiline_config.rs b/vendor/tabled/src/grid/compact_multiline_config.rs
new file mode 100644
index 000000000..c9056c911
--- /dev/null
+++ b/vendor/tabled/src/grid/compact_multiline_config.rs
@@ -0,0 +1,211 @@
+use crate::grid::color::StaticColor;
+use crate::grid::config::{
+ AlignmentHorizontal, AlignmentVertical, Borders, CompactConfig, Indent, Line, Sides,
+};
+
+/// A [`CompactConfig`] configuration plus vertical alignment.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct CompactMultilineConfig {
+ config: CompactConfig,
+ alignment_vertical: AlignmentVertical,
+ formatting: Formatting,
+}
+
+impl CompactMultilineConfig {
+ /// Create a new colored config.
+ pub fn new(config: CompactConfig) -> Self {
+ Self::from(config)
+ }
+
+ /// Set a horizontal alignment.
+ pub const fn set_alignment_vertical(mut self, alignment: AlignmentVertical) -> Self {
+ self.alignment_vertical = alignment;
+ self
+ }
+
+ /// Get a alignment horizontal.
+ pub const fn get_alignment_vertical(&self) -> AlignmentVertical {
+ self.alignment_vertical
+ }
+
+ /// Set grid margin.
+ pub const fn set_margin(mut self, margin: Sides<Indent>) -> Self {
+ self.config = self.config.set_margin(margin);
+ self
+ }
+
+ /// Returns a grid margin.
+ pub const fn get_margin(&self) -> &Sides<Indent> {
+ self.config.get_margin()
+ }
+
+ /// Set the [`Borders`] value as correct one.
+ pub const fn set_borders(mut self, borders: Borders<char>) -> Self {
+ self.config = self.config.set_borders(borders);
+ self
+ }
+
+ /// Set the first horizontal line.
+ ///
+ /// It ignores the [`Borders`] horizontal value if set for 1st row.
+ pub const fn set_first_horizontal_line(mut self, line: Line<char>) -> Self {
+ self.config = self.config.set_first_horizontal_line(line);
+ self
+ }
+
+ /// Set the first horizontal line.
+ ///
+ /// It ignores the [`Borders`] horizontal value if set for 1st row.
+ pub const fn get_first_horizontal_line(&self) -> Option<Line<char>> {
+ self.config.get_first_horizontal_line()
+ }
+
+ /// Returns a current [`Borders`] structure.
+ pub const fn get_borders(&self) -> &Borders<char> {
+ self.config.get_borders()
+ }
+
+ /// Returns a current [`Borders`] structure.
+ pub const fn get_borders_color(&self) -> &Borders<StaticColor> {
+ self.config.get_borders_color()
+ }
+
+ /// Set a padding to a given cells.
+ pub const fn set_padding(mut self, padding: Sides<Indent>) -> Self {
+ self.config = self.config.set_padding(padding);
+ self
+ }
+
+ /// Get a padding for a given.
+ pub const fn get_padding(&self) -> &Sides<Indent> {
+ self.config.get_padding()
+ }
+
+ /// Set a horizontal alignment.
+ pub const fn set_alignment_horizontal(mut self, alignment: AlignmentHorizontal) -> Self {
+ self.config = self.config.set_alignment_horizontal(alignment);
+ self
+ }
+
+ /// Get a alignment horizontal.
+ pub const fn get_alignment_horizontal(&self) -> AlignmentHorizontal {
+ self.config.get_alignment_horizontal()
+ }
+
+ /// Sets colors of border carcass on the grid.
+ pub const fn set_borders_color(mut self, borders: Borders<StaticColor>) -> Self {
+ self.config = self.config.set_borders_color(borders);
+ self
+ }
+
+ /// Set colors for a margin.
+ pub const fn set_margin_color(mut self, color: Sides<StaticColor>) -> Self {
+ self.config = self.config.set_margin_color(color);
+ self
+ }
+
+ /// Returns a margin color.
+ pub const fn get_margin_color(&self) -> Sides<StaticColor> {
+ self.config.get_margin_color()
+ }
+
+ /// Set a padding color to all cells.
+ pub const fn set_padding_color(mut self, color: Sides<StaticColor>) -> Self {
+ self.config = self.config.set_padding_color(color);
+ self
+ }
+
+ /// get a padding color.
+ pub const fn get_padding_color(&self) -> Sides<StaticColor> {
+ self.config.get_padding_color()
+ }
+
+ /// Set formatting.
+ pub const fn set_formatting(mut self, formatting: Formatting) -> Self {
+ self.formatting = formatting;
+ self
+ }
+
+ /// Get formatting.
+ pub const fn get_formatting(&self) -> Formatting {
+ self.formatting
+ }
+}
+
+impl Default for CompactMultilineConfig {
+ fn default() -> Self {
+ Self {
+ config: Default::default(),
+ alignment_vertical: AlignmentVertical::Top,
+ formatting: Formatting::default(),
+ }
+ }
+}
+
+impl From<CompactConfig> for CompactMultilineConfig {
+ fn from(config: CompactConfig) -> Self {
+ Self {
+ config,
+ alignment_vertical: AlignmentVertical::Top,
+ formatting: Formatting::default(),
+ }
+ }
+}
+
+impl AsRef<CompactConfig> for CompactMultilineConfig {
+ fn as_ref(&self) -> &CompactConfig {
+ &self.config
+ }
+}
+
+impl AsMut<CompactConfig> for CompactMultilineConfig {
+ fn as_mut(&mut self) -> &mut CompactConfig {
+ &mut self.config
+ }
+}
+
+#[cfg(feature = "std")]
+impl From<CompactMultilineConfig> for crate::grid::config::SpannedConfig {
+ fn from(compact: CompactMultilineConfig) -> Self {
+ use crate::grid::config::Entity;
+
+ let mut cfg = crate::grid::config::SpannedConfig::from(compact.config);
+ cfg.set_alignment_vertical(Entity::Global, compact.alignment_vertical);
+ cfg.set_formatting(Entity::Global, compact.formatting.into());
+
+ cfg
+ }
+}
+
+/// Formatting represent a logic of formatting of a cell.
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Formatting {
+ /// An setting to allow horizontal trim.
+ pub horizontal_trim: bool,
+ /// An setting to allow vertical trim.
+ pub vertical_trim: bool,
+ /// An setting to allow alignment per line.
+ pub allow_lines_alignment: bool,
+}
+
+impl Formatting {
+ /// Creates a new [`Formatting`] structure.
+ pub fn new(horizontal_trim: bool, vertical_trim: bool, allow_lines_alignment: bool) -> Self {
+ Self {
+ horizontal_trim,
+ vertical_trim,
+ allow_lines_alignment,
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl From<Formatting> for crate::grid::config::Formatting {
+ fn from(val: Formatting) -> Self {
+ crate::grid::config::Formatting {
+ allow_lines_alignment: val.allow_lines_alignment,
+ horizontal_trim: val.horizontal_trim,
+ vertical_trim: val.vertical_trim,
+ }
+ }
+}
diff --git a/vendor/tabled/src/grid/dimension/complete_dimension.rs b/vendor/tabled/src/grid/dimension/complete_dimension.rs
new file mode 100644
index 000000000..3147cb27a
--- /dev/null
+++ b/vendor/tabled/src/grid/dimension/complete_dimension.rs
@@ -0,0 +1,136 @@
+use std::borrow::Cow;
+
+use crate::grid::{
+ config::{ColoredConfig, SpannedConfig},
+ dimension::{Dimension, Estimate, SpannedGridDimension},
+ records::Records,
+};
+
+/// CompleteDimension is a [`Dimension`] implementation for a [`Table`]
+///
+/// [`Table`]: crate::Table
+#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone)]
+pub struct CompleteDimension<'a> {
+ width: Option<Cow<'a, [usize]>>,
+ height: Option<Cow<'a, [usize]>>,
+}
+
+impl CompleteDimension<'_> {
+ /// Checks whether is the dimensions is set.
+ pub fn is_complete(&self) -> bool {
+ self.width.is_some() && self.height.is_some()
+ }
+
+ /// Checks whether is nothing was set.
+ pub fn is_empty(&self) -> bool {
+ self.width.is_none() && self.height.is_none()
+ }
+
+ /// Set column widths.
+ ///
+ /// In general the method is only considered to be useful to a [`TableOption`].
+ ///
+ /// BE CAREFUL WITH THIS METHOD as it supposed that the content is not bigger than the provided widths.
+ ///
+ /// [`TableOption`]: crate::settings::TableOption
+ pub fn set_widths(&mut self, columns: Vec<usize>) -> bool {
+ self.width = Some(Cow::Owned(columns));
+
+ true
+ }
+
+ /// Set rows heights.
+ ///
+ /// In general the method is only considered to be useful to a [`TableOption`].
+ ///
+ /// BE CAREFUL WITH THIS METHOD as it supposed that the content is not bigger than the provided heights.
+ ///
+ /// [`TableOption`]: crate::settings::TableOption
+ pub fn set_heights(&mut self, rows: Vec<usize>) -> bool {
+ self.height = Some(Cow::Owned(rows));
+
+ true
+ }
+
+ /// Force width estimation.
+ pub fn clear_width(&mut self) {
+ self.width = None;
+ }
+
+ /// Force height estimation.
+ pub fn clear_height(&mut self) {
+ self.height = None;
+ }
+
+ /// Copies a reference from self.
+ pub fn from_origin(&self) -> CompleteDimension<'_> {
+ let width = self.width.as_deref().map(Cow::Borrowed);
+ let height = self.height.as_deref().map(Cow::Borrowed);
+
+ CompleteDimension { width, height }
+ }
+}
+
+impl Dimension for CompleteDimension<'_> {
+ fn get_width(&self, column: usize) -> usize {
+ let width = self
+ .width
+ .as_ref()
+ .expect("It must always be Some at this point");
+
+ width[column]
+ }
+
+ fn get_height(&self, row: usize) -> usize {
+ let height = self
+ .height
+ .as_ref()
+ .expect("It must always be Some at this point");
+
+ height[row]
+ }
+}
+
+impl<R: Records> Estimate<R, SpannedConfig> for CompleteDimension<'_> {
+ fn estimate(&mut self, records: R, cfg: &SpannedConfig) {
+ match (self.width.is_some(), self.height.is_some()) {
+ (true, true) => {}
+ (true, false) => {
+ self.height = Some(Cow::Owned(SpannedGridDimension::height(records, cfg)));
+ }
+ (false, true) => {
+ self.width = Some(Cow::Owned(SpannedGridDimension::width(records, cfg)));
+ }
+ (false, false) => {
+ let mut dims = SpannedGridDimension::default();
+ dims.estimate(records, cfg);
+
+ let (width, height) = dims.get_values();
+ self.width = Some(Cow::Owned(width));
+ self.height = Some(Cow::Owned(height));
+ }
+ }
+ }
+}
+
+impl<R: Records> Estimate<R, ColoredConfig> for CompleteDimension<'_> {
+ fn estimate(&mut self, records: R, cfg: &ColoredConfig) {
+ match (self.width.is_some(), self.height.is_some()) {
+ (true, true) => {}
+ (true, false) => {
+ self.height = Some(Cow::Owned(SpannedGridDimension::height(records, cfg)));
+ }
+ (false, true) => {
+ self.width = Some(Cow::Owned(SpannedGridDimension::width(records, cfg)));
+ }
+ (false, false) => {
+ let mut dims = SpannedGridDimension::default();
+ dims.estimate(records, cfg);
+
+ let (width, height) = dims.get_values();
+ self.width = Some(Cow::Owned(width));
+ self.height = Some(Cow::Owned(height));
+ }
+ }
+ }
+}
diff --git a/vendor/tabled/src/grid/dimension/complete_dimension_vec_records.rs b/vendor/tabled/src/grid/dimension/complete_dimension_vec_records.rs
new file mode 100644
index 000000000..ddc806a45
--- /dev/null
+++ b/vendor/tabled/src/grid/dimension/complete_dimension_vec_records.rs
@@ -0,0 +1,128 @@
+use std::borrow::Cow;
+
+use papergrid::{
+ dimension::spanned_vec_records::SpannedVecRecordsDimension, records::vec_records::VecRecords,
+};
+
+use crate::grid::{
+ config::{ColoredConfig, SpannedConfig},
+ dimension::{Dimension, Estimate},
+ records::vec_records::Cell,
+};
+
+/// CompleteDimension is a [`Dimension`] implementation for a [`Table`]
+///
+/// [`Table`]: crate::Table
+#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone)]
+pub struct CompleteDimensionVecRecords<'a> {
+ width: Option<Cow<'a, [usize]>>,
+ height: Option<Cow<'a, [usize]>>,
+}
+
+impl CompleteDimensionVecRecords<'_> {
+ /// Checks whether is the dimensions is set.
+ pub fn is_complete(&self) -> bool {
+ self.width.is_some() && self.height.is_some()
+ }
+
+ /// Checks whether is nothing was set.
+ pub fn is_empty(&self) -> bool {
+ self.width.is_none() && self.height.is_none()
+ }
+
+ /// Set column widths.
+ ///
+ /// In general the method is only considered to be useful to a [`TableOption`].
+ ///
+ /// BE CAREFUL WITH THIS METHOD as it supposed that the content is not bigger than the provided widths.
+ ///
+ /// [`TableOption`]: crate::settings::TableOption
+ pub fn set_widths(&mut self, columns: Vec<usize>) -> bool {
+ self.width = Some(Cow::Owned(columns));
+
+ true
+ }
+
+ /// Set rows heights.
+ ///
+ /// In general the method is only considered to be useful to a [`TableOption`].
+ ///
+ /// BE CAREFUL WITH THIS METHOD as it supposed that the content is not bigger than the provided heights.
+ ///
+ /// [`TableOption`]: crate::settings::TableOption
+ pub fn set_heights(&mut self, rows: Vec<usize>) -> bool {
+ self.height = Some(Cow::Owned(rows));
+
+ true
+ }
+
+ /// Force width estimation.
+ pub fn clear_width(&mut self) {
+ self.width = None;
+ }
+
+ /// Force height estimation.
+ pub fn clear_height(&mut self) {
+ self.height = None;
+ }
+
+ /// Copies a reference from self.
+ pub fn from_origin(&self) -> CompleteDimensionVecRecords<'_> {
+ let width = self.width.as_deref().map(Cow::Borrowed);
+ let height = self.height.as_deref().map(Cow::Borrowed);
+
+ CompleteDimensionVecRecords { width, height }
+ }
+}
+
+impl Dimension for CompleteDimensionVecRecords<'_> {
+ fn get_width(&self, column: usize) -> usize {
+ let width = self
+ .width
+ .as_ref()
+ .expect("It must always be Some at this point");
+
+ width[column]
+ }
+
+ fn get_height(&self, row: usize) -> usize {
+ let height = self
+ .height
+ .as_ref()
+ .expect("It must always be Some at this point");
+
+ height[row]
+ }
+}
+
+impl<T: AsRef<str> + Cell> Estimate<&VecRecords<T>, SpannedConfig>
+ for CompleteDimensionVecRecords<'_>
+{
+ fn estimate(&mut self, records: &VecRecords<T>, cfg: &SpannedConfig) {
+ match (self.width.is_some(), self.height.is_some()) {
+ (true, true) => {}
+ (true, false) => {
+ self.height = Some(Cow::Owned(SpannedVecRecordsDimension::height(records, cfg)));
+ }
+ (false, true) => {
+ self.width = Some(Cow::Owned(SpannedVecRecordsDimension::width(records, cfg)));
+ }
+ (false, false) => {
+ let mut dims = SpannedVecRecordsDimension::default();
+ dims.estimate(records, cfg);
+
+ let (width, height) = dims.get_values();
+ self.width = Some(Cow::Owned(width));
+ self.height = Some(Cow::Owned(height));
+ }
+ }
+ }
+}
+
+impl<T: AsRef<str> + Cell> Estimate<&VecRecords<T>, ColoredConfig>
+ for CompleteDimensionVecRecords<'_>
+{
+ fn estimate(&mut self, records: &VecRecords<T>, cfg: &ColoredConfig) {
+ self.estimate(records, cfg.as_ref())
+ }
+}
diff --git a/vendor/tabled/src/grid/dimension/const_dimension.rs b/vendor/tabled/src/grid/dimension/const_dimension.rs
new file mode 100644
index 000000000..450b1abfe
--- /dev/null
+++ b/vendor/tabled/src/grid/dimension/const_dimension.rs
@@ -0,0 +1,70 @@
+//! Module contains a dimension estimator for [`CompactTable`]
+//!
+//! [`CompactTable`]: crate::tables::CompactTable
+
+use crate::grid::dimension::{Dimension, Estimate};
+
+/// A constant size dimension or a value dimension.
+#[derive(Debug, Clone, Copy)]
+pub struct ConstDimension<const COLUMNS: usize, const ROWS: usize> {
+ height: ConstSize<ROWS>,
+ width: ConstSize<COLUMNS>,
+}
+
+impl<const COLUMNS: usize, const ROWS: usize> ConstDimension<COLUMNS, ROWS> {
+ /// Returns a new dimension object with a given estimates.
+ pub const fn new(width: ConstSize<COLUMNS>, height: ConstSize<ROWS>) -> Self {
+ Self { width, height }
+ }
+}
+
+impl<const COLUMNS: usize, const ROWS: usize> Dimension for ConstDimension<COLUMNS, ROWS> {
+ fn get_width(&self, column: usize) -> usize {
+ match self.width {
+ ConstSize::List(list) => list[column],
+ ConstSize::Value(val) => val,
+ }
+ }
+
+ fn get_height(&self, row: usize) -> usize {
+ match self.height {
+ ConstSize::List(list) => list[row],
+ ConstSize::Value(val) => val,
+ }
+ }
+}
+
+impl<const COLUMNS: usize, const ROWS: usize> From<ConstDimension<COLUMNS, ROWS>>
+ for (ConstSize<COLUMNS>, ConstSize<ROWS>)
+{
+ fn from(value: ConstDimension<COLUMNS, ROWS>) -> Self {
+ (value.width, value.height)
+ }
+}
+
+impl<R, D, const COLUMNS: usize, const ROWS: usize> Estimate<R, D>
+ for ConstDimension<COLUMNS, ROWS>
+{
+ fn estimate(&mut self, _: R, _: &D) {}
+}
+
+/// Const size represents either a const array values or a single value which responsible for the whole list.
+#[derive(Debug, Clone, Copy)]
+pub enum ConstSize<const N: usize> {
+ /// A constant array of estimates.
+ List([usize; N]),
+ /// A value which act as a single estimate for all entries.
+ Value(usize),
+}
+
+impl From<usize> for ConstSize<0> {
+ fn from(value: usize) -> Self {
+ ConstSize::Value(value)
+ }
+}
+
+impl<const N: usize> From<[usize; N]> for ConstSize<N> {
+ fn from(value: [usize; N]) -> Self {
+ ConstSize::List(value)
+ }
+}
diff --git a/vendor/tabled/src/grid/dimension/mod.rs b/vendor/tabled/src/grid/dimension/mod.rs
new file mode 100644
index 000000000..2f67b402f
--- /dev/null
+++ b/vendor/tabled/src/grid/dimension/mod.rs
@@ -0,0 +1,32 @@
+//! Module contains a list of implementations of [`Estimate`] and [`Dimension`].
+
+mod const_dimension;
+mod pool_table_dimension;
+
+#[cfg(feature = "std")]
+mod complete_dimension;
+#[cfg(feature = "std")]
+mod complete_dimension_vec_records;
+#[cfg(feature = "std")]
+mod peekable_dimension;
+#[cfg(feature = "std")]
+mod static_dimension;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use self::{
+ complete_dimension::CompleteDimension,
+ complete_dimension_vec_records::CompleteDimensionVecRecords,
+ peekable_dimension::PeekableDimension,
+ static_dimension::{DimensionValue, StaticDimension},
+};
+pub use const_dimension::{ConstDimension, ConstSize};
+pub use papergrid::dimension::{Dimension, Estimate};
+pub use pool_table_dimension::{DimensionPriority, PoolTableDimension};
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use papergrid::dimension::{
+ compact::CompactGridDimension, spanned::SpannedGridDimension,
+ spanned_vec_records::SpannedVecRecordsDimension,
+};
diff --git a/vendor/tabled/src/grid/dimension/peekable_dimension.rs b/vendor/tabled/src/grid/dimension/peekable_dimension.rs
new file mode 100644
index 000000000..1ba6fda21
--- /dev/null
+++ b/vendor/tabled/src/grid/dimension/peekable_dimension.rs
@@ -0,0 +1,335 @@
+use papergrid::records::vec_records::{CellInfo, VecRecords};
+
+use crate::grid::{
+ config::SpannedConfig,
+ dimension::{Dimension, Estimate},
+ records::Records,
+};
+
+/// PeekableDimension is a [`Dimension`] implementation for a [`Table`]
+///
+/// [`Table`]: crate::Table
+#[derive(Debug, Default, Clone)]
+pub struct PeekableDimension {
+ width: Vec<usize>,
+ height: Vec<usize>,
+}
+
+impl PeekableDimension {
+ /// Calculates height of rows.
+ pub fn height<T: AsRef<str>>(
+ records: &VecRecords<CellInfo<T>>,
+ cfg: &SpannedConfig,
+ ) -> Vec<usize> {
+ estimation::build_height(records, cfg)
+ }
+
+ /// Calculates width of columns.
+ pub fn width<T: AsRef<str>>(
+ records: &VecRecords<CellInfo<T>>,
+ cfg: &SpannedConfig,
+ ) -> Vec<usize> {
+ estimation::build_width(records, cfg)
+ }
+
+ /// Return width and height lists.
+ pub fn get_values(self) -> (Vec<usize>, Vec<usize>) {
+ (self.width, self.height)
+ }
+}
+
+impl Dimension for PeekableDimension {
+ fn get_width(&self, column: usize) -> usize {
+ self.width[column]
+ }
+
+ fn get_height(&self, row: usize) -> usize {
+ self.height[row]
+ }
+}
+
+impl<T> Estimate<&VecRecords<CellInfo<T>>, SpannedConfig> for PeekableDimension
+where
+ T: AsRef<str>,
+{
+ fn estimate(&mut self, records: &VecRecords<CellInfo<T>>, cfg: &SpannedConfig) {
+ let (width, height) = estimation::build_dimensions(records, cfg);
+ self.width = width;
+ self.height = height;
+ }
+}
+
+mod estimation {
+ use core::cmp::{max, Ordering};
+ use std::collections::HashMap;
+
+ use papergrid::{
+ config::Position,
+ records::vec_records::{Cell, CellInfo, VecRecords},
+ };
+
+ use super::*;
+
+ pub(super) fn build_dimensions<T: AsRef<str>>(
+ records: &VecRecords<CellInfo<T>>,
+ cfg: &SpannedConfig,
+ ) -> (Vec<usize>, Vec<usize>) {
+ let count_columns = records.count_columns();
+
+ let mut widths = vec![0; count_columns];
+ let mut heights = vec![];
+
+ let mut vspans = HashMap::new();
+ let mut hspans = HashMap::new();
+
+ for (row, columns) in records.iter_rows().enumerate() {
+ let mut row_height = 0;
+ for (col, cell) in columns.iter().enumerate() {
+ let pos = (row, col);
+ if !cfg.is_cell_visible(pos) {
+ continue;
+ }
+
+ let height = cell.count_lines();
+ let width = cell.width();
+
+ let pad = cfg.get_padding(pos.into());
+ let width = width + pad.left.size + pad.right.size;
+ let height = height + pad.top.size + pad.bottom.size;
+
+ match cfg.get_column_span(pos) {
+ Some(n) if n > 1 => {
+ let _ = vspans.insert(pos, (n, width));
+ }
+ _ => widths[col] = max(widths[col], width),
+ }
+
+ match cfg.get_row_span(pos) {
+ Some(n) if n > 1 => {
+ let _ = hspans.insert(pos, (n, height));
+ }
+ _ => row_height = max(row_height, height),
+ }
+ }
+
+ heights.push(row_height);
+ }
+
+ let count_rows = heights.len();
+
+ adjust_vspans(cfg, count_columns, &vspans, &mut widths);
+ adjust_hspans(cfg, count_rows, &hspans, &mut heights);
+
+ (widths, heights)
+ }
+
+ fn adjust_hspans(
+ cfg: &SpannedConfig,
+ len: usize,
+ spans: &HashMap<Position, (usize, usize)>,
+ heights: &mut [usize],
+ ) {
+ if spans.is_empty() {
+ return;
+ }
+
+ let mut spans_ordered = spans
+ .iter()
+ .map(|(k, v)| ((k.0, k.1), *v))
+ .collect::<Vec<_>>();
+ spans_ordered.sort_unstable_by(|(arow, acol), (brow, bcol)| match arow.cmp(brow) {
+ Ordering::Equal => acol.cmp(bcol),
+ ord => ord,
+ });
+
+ for ((row, _), (span, height)) in spans_ordered {
+ adjust_row_range(cfg, height, len, row, row + span, heights);
+ }
+ }
+
+ fn adjust_row_range(
+ cfg: &SpannedConfig,
+ max_span_height: usize,
+ len: usize,
+ start: usize,
+ end: usize,
+ heights: &mut [usize],
+ ) {
+ let range_height = range_height(cfg, len, start, end, heights);
+ if range_height >= max_span_height {
+ return;
+ }
+
+ inc_range(heights, max_span_height - range_height, start, end);
+ }
+
+ fn range_height(
+ cfg: &SpannedConfig,
+ len: usize,
+ start: usize,
+ end: usize,
+ heights: &[usize],
+ ) -> usize {
+ let count_borders = count_horizontal_borders(cfg, len, start, end);
+ let range_height = heights[start..end].iter().sum::<usize>();
+ count_borders + range_height
+ }
+
+ fn count_horizontal_borders(
+ cfg: &SpannedConfig,
+ len: usize,
+ start: usize,
+ end: usize,
+ ) -> usize {
+ (start..end)
+ .skip(1)
+ .filter(|&i| cfg.has_horizontal(i, len))
+ .count()
+ }
+
+ fn inc_range(list: &mut [usize], size: usize, start: usize, end: usize) {
+ if list.is_empty() {
+ return;
+ }
+
+ let span = end - start;
+ let one = size / span;
+ let rest = size - span * one;
+
+ let mut i = start;
+ while i < end {
+ if i == start {
+ list[i] += one + rest;
+ } else {
+ list[i] += one;
+ }
+
+ i += 1;
+ }
+ }
+
+ fn adjust_vspans(
+ cfg: &SpannedConfig,
+ len: usize,
+ spans: &HashMap<Position, (usize, usize)>,
+ widths: &mut [usize],
+ ) {
+ if spans.is_empty() {
+ return;
+ }
+
+ // The overall width distribution will be different depend on the order.
+ //
+ // We sort spans in order to prioritize the smaller spans first.
+ let mut spans_ordered = spans
+ .iter()
+ .map(|(k, v)| ((k.0, k.1), *v))
+ .collect::<Vec<_>>();
+ spans_ordered.sort_unstable_by(|a, b| match a.1 .0.cmp(&b.1 .0) {
+ Ordering::Equal => a.0.cmp(&b.0),
+ o => o,
+ });
+
+ for ((_, col), (span, width)) in spans_ordered {
+ adjust_column_range(cfg, width, len, col, col + span, widths);
+ }
+ }
+
+ fn adjust_column_range(
+ cfg: &SpannedConfig,
+ max_span_width: usize,
+ len: usize,
+ start: usize,
+ end: usize,
+ widths: &mut [usize],
+ ) {
+ let range_width = range_width(cfg, len, start, end, widths);
+ if range_width >= max_span_width {
+ return;
+ }
+
+ inc_range(widths, max_span_width - range_width, start, end);
+ }
+
+ fn range_width(
+ cfg: &SpannedConfig,
+ len: usize,
+ start: usize,
+ end: usize,
+ widths: &[usize],
+ ) -> usize {
+ let count_borders = count_vertical_borders(cfg, len, start, end);
+ let range_width = widths[start..end].iter().sum::<usize>();
+ count_borders + range_width
+ }
+
+ fn count_vertical_borders(cfg: &SpannedConfig, len: usize, start: usize, end: usize) -> usize {
+ (start..end)
+ .skip(1)
+ .filter(|&i| cfg.has_vertical(i, len))
+ .count()
+ }
+
+ pub(super) fn build_height<T: AsRef<str>>(
+ records: &VecRecords<CellInfo<T>>,
+ cfg: &SpannedConfig,
+ ) -> Vec<usize> {
+ let mut heights = vec![];
+ let mut hspans = HashMap::new();
+
+ for (row, columns) in records.iter_rows().enumerate() {
+ let mut row_height = 0;
+ for (col, cell) in columns.iter().enumerate() {
+ let pos = (row, col);
+ if !cfg.is_cell_visible(pos) {
+ continue;
+ }
+
+ let height = cell.count_lines();
+ match cfg.get_row_span(pos) {
+ Some(n) if n > 1 => {
+ let _ = hspans.insert(pos, (n, height));
+ }
+ _ => row_height = max(row_height, height),
+ }
+ }
+
+ heights.push(row_height);
+ }
+
+ adjust_hspans(cfg, heights.len(), &hspans, &mut heights);
+
+ heights
+ }
+
+ pub(super) fn build_width<T: AsRef<str>>(
+ records: &VecRecords<CellInfo<T>>,
+ cfg: &SpannedConfig,
+ ) -> Vec<usize> {
+ let count_columns = records.count_columns();
+
+ let mut widths = vec![0; count_columns];
+ let mut vspans = HashMap::new();
+
+ for (row, columns) in records.iter_rows().enumerate() {
+ for (col, cell) in columns.iter().enumerate() {
+ let pos = (row, col);
+ if !cfg.is_cell_visible(pos) {
+ continue;
+ }
+
+ let width = cell.width();
+ match cfg.get_column_span(pos) {
+ Some(n) if n > 1 => {
+ let _ = vspans.insert(pos, (n, width));
+ }
+ _ => widths[col] = max(widths[col], width),
+ }
+ }
+ }
+
+ adjust_vspans(cfg, count_columns, &vspans, &mut widths);
+
+ widths
+ }
+}
diff --git a/vendor/tabled/src/grid/dimension/pool_table_dimension.rs b/vendor/tabled/src/grid/dimension/pool_table_dimension.rs
new file mode 100644
index 000000000..9909c661d
--- /dev/null
+++ b/vendor/tabled/src/grid/dimension/pool_table_dimension.rs
@@ -0,0 +1,36 @@
+/// PoolTableDimension is a dimension resolve strategy for [`PoolTable`]
+///
+/// [`PoolTable`]: crate::tables::PoolTable
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+pub struct PoolTableDimension {
+ width: DimensionPriority,
+ height: DimensionPriority,
+}
+
+impl PoolTableDimension {
+ /// Creates a new object.
+ pub fn new(width: DimensionPriority, height: DimensionPriority) -> Self {
+ Self { width, height }
+ }
+
+ /// Return a width priority.
+ pub fn width(&self) -> DimensionPriority {
+ self.width
+ }
+
+ /// Return a height priority.
+ pub fn height(&self) -> DimensionPriority {
+ self.height
+ }
+}
+
+/// A control of width/height logic for situations where we must increase some cell to align columns/row.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum DimensionPriority {
+ /// Increase first cell width/height in a row/column.
+ First,
+ /// Increase last cell width/height in a row/column.
+ Last,
+ /// Increase cells width/height 1 by 1 in a row/column.
+ List,
+}
diff --git a/vendor/tabled/src/grid/dimension/static_dimension.rs b/vendor/tabled/src/grid/dimension/static_dimension.rs
new file mode 100644
index 000000000..f9474f212
--- /dev/null
+++ b/vendor/tabled/src/grid/dimension/static_dimension.rs
@@ -0,0 +1,63 @@
+use crate::grid::dimension::{Dimension, Estimate};
+
+/// A constant dimension.
+#[derive(Debug, Clone)]
+pub struct StaticDimension {
+ width: DimensionValue,
+ height: DimensionValue,
+}
+
+impl StaticDimension {
+ /// Creates a constant dimension.
+ pub fn new(width: DimensionValue, height: DimensionValue) -> Self {
+ Self { width, height }
+ }
+}
+
+impl From<StaticDimension> for (DimensionValue, DimensionValue) {
+ fn from(value: StaticDimension) -> Self {
+ (value.width, value.height)
+ }
+}
+
+impl Dimension for StaticDimension {
+ fn get_width(&self, column: usize) -> usize {
+ self.width.get(column)
+ }
+
+ fn get_height(&self, row: usize) -> usize {
+ self.height.get(row)
+ }
+}
+
+impl<R, C> Estimate<R, C> for StaticDimension {
+ fn estimate(&mut self, _: R, _: &C) {}
+}
+
+/// A dimension value.
+#[derive(Debug, Clone)]
+pub enum DimensionValue {
+ /// Const width value.
+ Exact(usize),
+ /// A list of width values for columns.
+ List(Vec<usize>),
+ /// A list of width values for columns and a value for the rest.
+ Partial(Vec<usize>, usize),
+}
+
+impl DimensionValue {
+ /// Get a width by column.
+ pub fn get(&self, col: usize) -> usize {
+ match self {
+ DimensionValue::Exact(val) => *val,
+ DimensionValue::List(cols) => cols[col],
+ DimensionValue::Partial(cols, val) => {
+ if cols.len() > col {
+ cols[col]
+ } else {
+ *val
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/tabled/src/grid/mod.rs b/vendor/tabled/src/grid/mod.rs
new file mode 100644
index 000000000..cdd8c55c0
--- /dev/null
+++ b/vendor/tabled/src/grid/mod.rs
@@ -0,0 +1,48 @@
+//! Module is responsible for tables underlyign grid.
+//!
+//! It might be used when implementing your own [`TableOption`] and [`CellOption`].
+//!
+//! [`TableOption`]: crate::settings::TableOption
+//! [`CellOption`]: crate::settings::CellOption
+#[cfg(feature = "std")]
+mod colored_config;
+
+mod compact_multiline_config;
+
+pub mod dimension;
+pub mod records;
+
+pub use papergrid::color;
+pub use papergrid::colors;
+pub use papergrid::util;
+
+pub mod config {
+ //! Module contains a list of configs for varios tables/grids.
+
+ pub use papergrid::config::{
+ compact::CompactConfig, AlignmentHorizontal, AlignmentVertical, Border, Borders, Entity,
+ EntityIterator, Indent, Line, Position, Sides,
+ };
+
+ #[cfg(feature = "std")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+ pub use papergrid::config::spanned::{
+ EntityMap, Formatting, HorizontalLine, Offset, SpannedConfig, VerticalLine,
+ };
+
+ #[cfg(feature = "std")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+ pub use super::colored_config::{ColorMap, ColoredConfig};
+
+ pub use super::compact_multiline_config::CompactMultilineConfig;
+}
+
+pub use papergrid::grid::compact::CompactGrid;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use papergrid::grid::iterable::Grid;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use papergrid::grid::peekable::PeekableGrid;
diff --git a/vendor/tabled/src/grid/records/empty_records.rs b/vendor/tabled/src/grid/records/empty_records.rs
new file mode 100644
index 000000000..77ebc812d
--- /dev/null
+++ b/vendor/tabled/src/grid/records/empty_records.rs
@@ -0,0 +1,41 @@
+//! An empty [`Records`] implementation.
+
+use core::iter::{repeat, Repeat, Take};
+
+use super::Records;
+
+/// Empty representation of [`Records`].
+#[derive(Debug, Default, Clone)]
+pub struct EmptyRecords {
+ rows: usize,
+ cols: usize,
+}
+
+impl EmptyRecords {
+ /// Constructs an empty representation of [`Records`] with a given shape.
+ pub fn new(rows: usize, cols: usize) -> Self {
+ Self { rows, cols }
+ }
+}
+
+impl From<(usize, usize)> for EmptyRecords {
+ fn from((count_rows, count_columns): (usize, usize)) -> Self {
+ Self::new(count_rows, count_columns)
+ }
+}
+
+impl Records for EmptyRecords {
+ type Iter = Take<Repeat<Take<Repeat<&'static str>>>>;
+
+ fn iter_rows(self) -> Self::Iter {
+ repeat(repeat("").take(self.cols)).take(self.rows)
+ }
+
+ fn count_columns(&self) -> usize {
+ self.cols
+ }
+
+ fn hint_count_rows(&self) -> Option<usize> {
+ Some(self.rows)
+ }
+}
diff --git a/vendor/tabled/src/grid/records/into_records/buf_records.rs b/vendor/tabled/src/grid/records/into_records/buf_records.rs
new file mode 100644
index 000000000..2db3e45bf
--- /dev/null
+++ b/vendor/tabled/src/grid/records/into_records/buf_records.rs
@@ -0,0 +1,213 @@
+//! A module contains [`BufRows`] and [`BufColumns`] iterators.
+//!
+//! Almoust always they both can be used interchangeably but [`BufRows`] is supposed to be lighter cause it
+//! does not reads columns.
+
+use crate::grid::records::IntoRecords;
+
+use super::either_string::EitherString;
+
+/// BufRecords inspects [`IntoRecords`] iterator and keeps read data buffered.
+/// So it can be checking before hand.
+#[derive(Debug)]
+pub struct BufRows<I, T> {
+ iter: I,
+ buf: Vec<T>,
+}
+
+impl BufRows<(), ()> {
+ /// Creates a new [`BufRows`] structure, filling the buffer.
+ pub fn new<I: IntoRecords>(
+ records: I,
+ sniff: usize,
+ ) -> BufRows<<I::IterRows as IntoIterator>::IntoIter, I::IterColumns> {
+ let mut buf = vec![];
+
+ let mut iter = records.iter_rows().into_iter();
+ for _ in 0..sniff {
+ match iter.next() {
+ Some(row) => buf.push(row),
+ None => break,
+ }
+ }
+
+ BufRows { iter, buf }
+ }
+}
+
+impl<I, T> BufRows<I, T> {
+ /// Returns a slice of a record buffer.
+ pub fn as_slice(&self) -> &[T] {
+ &self.buf
+ }
+}
+
+impl<I, T> From<BufRows<I, T>> for BufColumns<I>
+where
+ T: IntoIterator,
+ T::Item: AsRef<str>,
+{
+ fn from(value: BufRows<I, T>) -> Self {
+ let buf = value
+ .buf
+ .into_iter()
+ .map(|row| row.into_iter().map(|s| s.as_ref().to_string()).collect())
+ .collect();
+
+ BufColumns {
+ iter: value.iter,
+ buf,
+ }
+ }
+}
+
+impl<I, T> IntoRecords for BufRows<I, T>
+where
+ I: Iterator<Item = T>,
+ T: IntoIterator,
+ T::Item: AsRef<str>,
+{
+ type Cell = T::Item;
+ type IterColumns = T;
+ type IterRows = BufRowIter<I, T>;
+
+ fn iter_rows(self) -> Self::IterRows {
+ BufRowIter {
+ buf: self.buf.into_iter(),
+ iter: self.iter,
+ }
+ }
+}
+
+/// Buffered [`Iterator`].
+#[derive(Debug)]
+pub struct BufRowIter<I, T> {
+ buf: std::vec::IntoIter<T>,
+ iter: I,
+}
+
+impl<I, T> Iterator for BufRowIter<I, T>
+where
+ I: Iterator<Item = T>,
+ T: IntoIterator,
+ T::Item: AsRef<str>,
+{
+ type Item = T;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.buf.next() {
+ Some(i) => Some(i),
+ None => self.iter.next(),
+ }
+ }
+}
+
+/// BufRecords inspects [`IntoRecords`] iterator and keeps read data buffered.
+/// So it can be checking before hand.
+///
+/// In contrast to [`BufRows`] it keeps records by columns.
+#[derive(Debug)]
+pub struct BufColumns<I> {
+ iter: I,
+ buf: Vec<Vec<String>>,
+}
+
+impl BufColumns<()> {
+ /// Creates new [`BufColumns`] structure, filling the buffer.
+ pub fn new<I: IntoRecords>(
+ records: I,
+ sniff: usize,
+ ) -> BufColumns<<I::IterRows as IntoIterator>::IntoIter> {
+ let mut buf = vec![];
+
+ let mut iter = records.iter_rows().into_iter();
+ for _ in 0..sniff {
+ match iter.next() {
+ Some(row) => {
+ let row = row
+ .into_iter()
+ .map(|cell| cell.as_ref().to_string())
+ .collect::<Vec<_>>();
+ buf.push(row)
+ }
+ None => break,
+ }
+ }
+
+ BufColumns { iter, buf }
+ }
+}
+
+impl<I> BufColumns<I> {
+ /// Returns a slice of a keeping buffer.
+ pub fn as_slice(&self) -> &[Vec<String>] {
+ &self.buf
+ }
+}
+
+impl<I> IntoRecords for BufColumns<I>
+where
+ I: Iterator,
+ I::Item: IntoIterator,
+ <I::Item as IntoIterator>::Item: AsRef<str>,
+{
+ type Cell = EitherString<<I::Item as IntoIterator>::Item>;
+ type IterColumns = EitherRowIterator<<I::Item as IntoIterator>::IntoIter>;
+ type IterRows = BufColumnIter<I>;
+
+ fn iter_rows(self) -> Self::IterRows {
+ BufColumnIter {
+ buf: self.buf.into_iter(),
+ iter: self.iter,
+ }
+ }
+}
+
+/// A row iterator for [`BufColumns`]
+#[derive(Debug)]
+pub struct BufColumnIter<I> {
+ buf: std::vec::IntoIter<Vec<String>>,
+ iter: I,
+}
+
+impl<I> Iterator for BufColumnIter<I>
+where
+ I: Iterator,
+ I::Item: IntoIterator,
+ <I::Item as IntoIterator>::Item: AsRef<str>,
+{
+ type Item = EitherRowIterator<<I::Item as IntoIterator>::IntoIter>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.buf.next() {
+ Some(i) => Some(EitherRowIterator::Owned(i.into_iter())),
+ None => self
+ .iter
+ .next()
+ .map(|i| EitherRowIterator::Some(i.into_iter())),
+ }
+ }
+}
+
+/// An iterator over some iterator or allocated buffer.
+#[derive(Debug)]
+pub enum EitherRowIterator<I> {
+ /// Allocated iterator.
+ Owned(std::vec::IntoIter<String>),
+ /// Given iterator.
+ Some(I),
+}
+
+impl<I> Iterator for EitherRowIterator<I>
+where
+ I: Iterator,
+{
+ type Item = EitherString<I::Item>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self {
+ EitherRowIterator::Owned(iter) => iter.next().map(EitherString::Owned),
+ EitherRowIterator::Some(iter) => iter.next().map(EitherString::Some),
+ }
+ }
+}
diff --git a/vendor/tabled/src/grid/records/into_records/either_string.rs b/vendor/tabled/src/grid/records/into_records/either_string.rs
new file mode 100644
index 000000000..f4d97290d
--- /dev/null
+++ b/vendor/tabled/src/grid/records/into_records/either_string.rs
@@ -0,0 +1,22 @@
+//! A module with a utility enum [`EitherString`].
+
+/// Either allocated string or some type which can be used as a string.
+#[derive(Debug)]
+pub enum EitherString<T> {
+ /// Allocated string.
+ Owned(String),
+ /// Something which can be used as a string.
+ Some(T),
+}
+
+impl<T> AsRef<str> for EitherString<T>
+where
+ T: AsRef<str>,
+{
+ fn as_ref(&self) -> &str {
+ match self {
+ EitherString::Owned(s) => s.as_ref(),
+ EitherString::Some(s) => s.as_ref(),
+ }
+ }
+}
diff --git a/vendor/tabled/src/grid/records/into_records/limit_column_records.rs b/vendor/tabled/src/grid/records/into_records/limit_column_records.rs
new file mode 100644
index 000000000..89b2b89ed
--- /dev/null
+++ b/vendor/tabled/src/grid/records/into_records/limit_column_records.rs
@@ -0,0 +1,82 @@
+//! The module contains [`LimitColumns`] records iterator.
+
+use crate::grid::records::IntoRecords;
+
+/// An iterator which limits amount of columns.
+#[derive(Debug)]
+pub struct LimitColumns<I> {
+ records: I,
+ limit: usize,
+}
+
+impl LimitColumns<()> {
+ /// Creates new [`LimitColumns`].
+ pub fn new<I: IntoRecords>(records: I, limit: usize) -> LimitColumns<I> {
+ LimitColumns { records, limit }
+ }
+}
+
+impl<I> IntoRecords for LimitColumns<I>
+where
+ I: IntoRecords,
+{
+ type Cell = I::Cell;
+ type IterColumns = LimitColumnsColumnsIter<<I::IterColumns as IntoIterator>::IntoIter>;
+ type IterRows = LimitColumnsIter<<I::IterRows as IntoIterator>::IntoIter>;
+
+ fn iter_rows(self) -> Self::IterRows {
+ LimitColumnsIter {
+ iter: self.records.iter_rows().into_iter(),
+ limit: self.limit,
+ }
+ }
+}
+
+/// An iterator over rows for [`LimitColumns`].
+#[derive(Debug)]
+pub struct LimitColumnsIter<I> {
+ iter: I,
+ limit: usize,
+}
+
+impl<I> Iterator for LimitColumnsIter<I>
+where
+ I: Iterator,
+ I::Item: IntoIterator,
+ <I::Item as IntoIterator>::Item: AsRef<str>,
+{
+ type Item = LimitColumnsColumnsIter<<I::Item as IntoIterator>::IntoIter>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let iter = self.iter.next()?;
+ Some(LimitColumnsColumnsIter {
+ iter: iter.into_iter(),
+ limit: self.limit,
+ })
+ }
+}
+
+/// An iterator over columns for [`LimitColumns`].
+#[derive(Debug)]
+pub struct LimitColumnsColumnsIter<I> {
+ iter: I,
+ limit: usize,
+}
+
+impl<I> Iterator for LimitColumnsColumnsIter<I>
+where
+ I: Iterator,
+ I::Item: AsRef<str>,
+{
+ type Item = I::Item;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.limit == 0 {
+ return None;
+ }
+
+ self.limit -= 1;
+
+ self.iter.next()
+ }
+}
diff --git a/vendor/tabled/src/grid/records/into_records/limit_row_records.rs b/vendor/tabled/src/grid/records/into_records/limit_row_records.rs
new file mode 100644
index 000000000..a461c6682
--- /dev/null
+++ b/vendor/tabled/src/grid/records/into_records/limit_row_records.rs
@@ -0,0 +1,59 @@
+//! The module contains [`LimitRows`] records iterator.
+
+use crate::grid::records::IntoRecords;
+
+/// [`LimitRows`] is an records iterator which limits amount of rows.
+#[derive(Debug)]
+pub struct LimitRows<I> {
+ records: I,
+ limit: usize,
+}
+
+impl LimitRows<()> {
+ /// Creates new [`LimitRows`] iterator.
+ pub fn new<I: IntoRecords>(records: I, limit: usize) -> LimitRows<I> {
+ LimitRows { records, limit }
+ }
+}
+
+impl<I> IntoRecords for LimitRows<I>
+where
+ I: IntoRecords,
+{
+ type Cell = I::Cell;
+ type IterColumns = I::IterColumns;
+ type IterRows = LimitRowsIter<<I::IterRows as IntoIterator>::IntoIter>;
+
+ fn iter_rows(self) -> Self::IterRows {
+ LimitRowsIter {
+ iter: self.records.iter_rows().into_iter(),
+ limit: self.limit,
+ }
+ }
+}
+
+/// A rows iterator for [`LimitRows`]
+#[derive(Debug)]
+pub struct LimitRowsIter<I> {
+ iter: I,
+ limit: usize,
+}
+
+impl<I> Iterator for LimitRowsIter<I>
+where
+ I: Iterator,
+ I::Item: IntoIterator,
+ <I::Item as IntoIterator>::Item: AsRef<str>,
+{
+ type Item = I::Item;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.limit == 0 {
+ return None;
+ }
+
+ self.limit -= 1;
+
+ self.iter.next()
+ }
+}
diff --git a/vendor/tabled/src/grid/records/into_records/mod.rs b/vendor/tabled/src/grid/records/into_records/mod.rs
new file mode 100644
index 000000000..0a52c41c1
--- /dev/null
+++ b/vendor/tabled/src/grid/records/into_records/mod.rs
@@ -0,0 +1,26 @@
+//! The module contains a list of helpers for [`IntoRecords`]
+//!
+//! [`IntoRecords`]: crate::grid::records::IntoRecords
+
+pub mod limit_column_records;
+pub mod limit_row_records;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod buf_records;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod either_string;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod truncate_records;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use buf_records::{BufColumns, BufRows};
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use truncate_records::TruncateContent;
+
+pub use limit_column_records::LimitColumns;
+pub use limit_row_records::LimitRows;
diff --git a/vendor/tabled/src/grid/records/into_records/truncate_records.rs b/vendor/tabled/src/grid/records/into_records/truncate_records.rs
new file mode 100644
index 000000000..17e7e533e
--- /dev/null
+++ b/vendor/tabled/src/grid/records/into_records/truncate_records.rs
@@ -0,0 +1,136 @@
+//! The module contains [`TruncateContent`] records iterator.
+
+use std::borrow::Cow;
+
+use crate::{
+ grid::records::IntoRecords, grid::util::string::string_width_multiline,
+ settings::width::Truncate,
+};
+
+use super::either_string::EitherString;
+
+/// A records iterator which truncates all cells to a given width.
+#[derive(Debug)]
+pub struct TruncateContent<'a, I> {
+ records: I,
+ width: ExactValue<'a>,
+}
+
+impl TruncateContent<'_, ()> {
+ /// Creates new [`TruncateContent`] object.
+ pub fn new<I: IntoRecords>(records: I, width: ExactValue<'_>) -> TruncateContent<'_, I> {
+ TruncateContent { records, width }
+ }
+}
+
+impl<'a, I> IntoRecords for TruncateContent<'a, I>
+where
+ I: IntoRecords,
+{
+ type Cell = EitherString<I::Cell>;
+ type IterColumns = TruncateContentColumnsIter<'a, <I::IterColumns as IntoIterator>::IntoIter>;
+ type IterRows = TruncateContentIter<'a, <I::IterRows as IntoIterator>::IntoIter>;
+
+ fn iter_rows(self) -> Self::IterRows {
+ TruncateContentIter {
+ iter: self.records.iter_rows().into_iter(),
+ width: self.width.clone(),
+ }
+ }
+}
+
+/// A row iterator for [`TruncateContent`].
+#[derive(Debug)]
+pub struct TruncateContentIter<'a, I> {
+ iter: I,
+ width: ExactValue<'a>,
+}
+
+impl<'a, I> Iterator for TruncateContentIter<'a, I>
+where
+ I: Iterator,
+ I::Item: IntoIterator,
+ <I::Item as IntoIterator>::Item: AsRef<str>,
+{
+ type Item = TruncateContentColumnsIter<'a, <I::Item as IntoIterator>::IntoIter>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let iter = self.iter.next()?;
+ Some(TruncateContentColumnsIter {
+ iter: iter.into_iter(),
+ current: 0,
+ width: self.width.clone(),
+ })
+ }
+}
+
+/// A column iterator for [`TruncateContent`].
+#[derive(Debug)]
+pub struct TruncateContentColumnsIter<'a, I> {
+ iter: I,
+ width: ExactValue<'a>,
+ current: usize,
+}
+
+impl<I> Iterator for TruncateContentColumnsIter<'_, I>
+where
+ I: Iterator,
+ I::Item: AsRef<str>,
+{
+ type Item = EitherString<I::Item>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let s = self.iter.next()?;
+
+ let width = self.width.get(self.current);
+ self.current += 1;
+
+ let text_width = string_width_multiline(s.as_ref());
+ if text_width <= width {
+ return Some(EitherString::Some(s));
+ }
+
+ let text = Truncate::truncate_text(s.as_ref(), width);
+ let text = text.into_owned();
+ let text = EitherString::Owned(text);
+
+ Some(text)
+ }
+}
+
+/// A width value.
+#[derive(Debug, Clone)]
+pub enum ExactValue<'a> {
+ /// Const width value.
+ Exact(usize),
+ /// A list of width values for columns.
+ List(Cow<'a, [usize]>),
+}
+
+impl<'a> From<&'a [usize]> for ExactValue<'a> {
+ fn from(value: &'a [usize]) -> Self {
+ Self::List(value.into())
+ }
+}
+
+impl From<Vec<usize>> for ExactValue<'_> {
+ fn from(value: Vec<usize>) -> Self {
+ Self::List(value.into())
+ }
+}
+
+impl From<usize> for ExactValue<'_> {
+ fn from(value: usize) -> Self {
+ Self::Exact(value)
+ }
+}
+
+impl ExactValue<'_> {
+ /// Get a width by column.
+ pub fn get(&self, col: usize) -> usize {
+ match self {
+ ExactValue::Exact(val) => *val,
+ ExactValue::List(cols) => cols[col],
+ }
+ }
+}
diff --git a/vendor/tabled/src/grid/records/mod.rs b/vendor/tabled/src/grid/records/mod.rs
new file mode 100644
index 000000000..494002346
--- /dev/null
+++ b/vendor/tabled/src/grid/records/mod.rs
@@ -0,0 +1,19 @@
+//! The module contains [`Records`], [`ExactRecords`], [`RecordsMut`], [`Resizable`] traits
+//! and its implementations.
+//!
+//! Also it provies a list of helpers for a user built [`Records`] via [`into_records`].
+
+mod empty_records;
+mod records_mut;
+mod resizable;
+
+pub mod into_records;
+
+pub use empty_records::EmptyRecords;
+pub use papergrid::records::{ExactRecords, IntoRecords, IterRecords, PeekableRecords, Records};
+pub use records_mut::RecordsMut;
+pub use resizable::Resizable;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use papergrid::records::vec_records;
diff --git a/vendor/tabled/src/grid/records/records_mut.rs b/vendor/tabled/src/grid/records/records_mut.rs
new file mode 100644
index 000000000..38c42d2c7
--- /dev/null
+++ b/vendor/tabled/src/grid/records/records_mut.rs
@@ -0,0 +1,34 @@
+use crate::grid::config::Position;
+#[cfg(feature = "std")]
+use crate::grid::records::vec_records::{CellInfo, VecRecords};
+
+/// A [`Records`] representation which can modify cell by (row, column) index.
+///
+/// [`Records`]: crate::grid::records::Records
+pub trait RecordsMut<Text> {
+ /// Sets a text to a given cell by index.
+ fn set(&mut self, pos: Position, text: Text);
+}
+
+impl<T, Text> RecordsMut<Text> for &'_ mut T
+where
+ T: RecordsMut<Text>,
+{
+ fn set(&mut self, pos: Position, text: Text) {
+ T::set(self, pos, text)
+ }
+}
+
+#[cfg(feature = "std")]
+impl RecordsMut<String> for VecRecords<CellInfo<String>> {
+ fn set(&mut self, (row, col): Position, text: String) {
+ self[row][col] = CellInfo::new(text);
+ }
+}
+
+#[cfg(feature = "std")]
+impl RecordsMut<&str> for VecRecords<CellInfo<String>> {
+ fn set(&mut self, (row, col): Position, text: &str) {
+ self[row][col] = CellInfo::new(text.to_string());
+ }
+}
diff --git a/vendor/tabled/src/grid/records/resizable.rs b/vendor/tabled/src/grid/records/resizable.rs
new file mode 100644
index 000000000..29ab38038
--- /dev/null
+++ b/vendor/tabled/src/grid/records/resizable.rs
@@ -0,0 +1,217 @@
+use papergrid::config::Position;
+
+#[cfg(feature = "std")]
+use crate::grid::records::vec_records::VecRecords;
+
+/// A records representation which can be modified by moving rows/columns around.
+pub trait Resizable {
+ /// Swap cells with one another.
+ fn swap(&mut self, lhs: Position, rhs: Position);
+ /// Swap rows with one another.
+ fn swap_row(&mut self, lhs: usize, rhs: usize);
+ /// Swap columns with one another.
+ fn swap_column(&mut self, lhs: usize, rhs: usize);
+ /// Adds a new row to a data set.
+ fn push_row(&mut self);
+ /// Adds a new column to a data set.
+ fn push_column(&mut self);
+ /// Removes a row from a data set by index.
+ fn remove_row(&mut self, row: usize);
+ /// Removes a column from a data set by index.
+ fn remove_column(&mut self, column: usize);
+ /// Inserts a row at index.
+ fn insert_row(&mut self, row: usize);
+ /// Inserts column at index.
+ fn insert_column(&mut self, column: usize);
+}
+
+impl<T> Resizable for &'_ mut T
+where
+ T: Resizable,
+{
+ fn swap(&mut self, lhs: Position, rhs: Position) {
+ T::swap(self, lhs, rhs)
+ }
+
+ fn swap_row(&mut self, lhs: usize, rhs: usize) {
+ T::swap_row(self, lhs, rhs)
+ }
+
+ fn swap_column(&mut self, lhs: usize, rhs: usize) {
+ T::swap_column(self, lhs, rhs)
+ }
+
+ fn push_row(&mut self) {
+ T::push_row(self)
+ }
+
+ fn push_column(&mut self) {
+ T::push_column(self)
+ }
+
+ fn remove_row(&mut self, row: usize) {
+ T::remove_row(self, row)
+ }
+
+ fn remove_column(&mut self, column: usize) {
+ T::remove_column(self, column)
+ }
+
+ fn insert_row(&mut self, row: usize) {
+ T::insert_row(self, row)
+ }
+
+ fn insert_column(&mut self, column: usize) {
+ T::insert_column(self, column)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<T> Resizable for Vec<Vec<T>>
+where
+ T: Default + Clone,
+{
+ fn swap(&mut self, lhs: Position, rhs: Position) {
+ if lhs == rhs {
+ return;
+ }
+
+ let t = std::mem::take(&mut self[lhs.0][lhs.1]);
+ let t = std::mem::replace(&mut self[rhs.0][rhs.1], t);
+ let _ = std::mem::replace(&mut self[lhs.0][lhs.1], t);
+ }
+
+ fn swap_row(&mut self, lhs: usize, rhs: usize) {
+ let t = std::mem::take(&mut self[lhs]);
+ let t = std::mem::replace(&mut self[rhs], t);
+ let _ = std::mem::replace(&mut self[lhs], t);
+ }
+
+ fn swap_column(&mut self, lhs: usize, rhs: usize) {
+ for row in self.iter_mut() {
+ row.swap(lhs, rhs);
+ }
+ }
+
+ fn push_row(&mut self) {
+ let count_columns = self.get(0).map(|l| l.len()).unwrap_or(0);
+ self.push(vec![T::default(); count_columns]);
+ }
+
+ fn push_column(&mut self) {
+ for row in self.iter_mut() {
+ row.push(T::default());
+ }
+ }
+
+ fn remove_row(&mut self, row: usize) {
+ let _ = self.remove(row);
+ }
+
+ fn remove_column(&mut self, column: usize) {
+ for row in self.iter_mut() {
+ let _ = row.remove(column);
+ }
+ }
+
+ fn insert_row(&mut self, row: usize) {
+ let count_columns = self.get(0).map(|l| l.len()).unwrap_or(0);
+ self.insert(row, vec![T::default(); count_columns]);
+ }
+
+ fn insert_column(&mut self, column: usize) {
+ for row in self {
+ row.insert(column, T::default());
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<T> Resizable for VecRecords<T>
+where
+ T: Default + Clone,
+{
+ fn swap(&mut self, lhs: Position, rhs: Position) {
+ if lhs == rhs {
+ return;
+ }
+
+ let t = std::mem::take(&mut self[lhs.0][lhs.1]);
+ let t = std::mem::replace(&mut self[rhs.0][rhs.1], t);
+ let _ = std::mem::replace(&mut self[lhs.0][lhs.1], t);
+ }
+
+ fn swap_row(&mut self, lhs: usize, rhs: usize) {
+ let t = std::mem::take(&mut self[lhs]);
+ let t = std::mem::replace(&mut self[rhs], t);
+ let _ = std::mem::replace(&mut self[lhs], t);
+ }
+
+ fn swap_column(&mut self, lhs: usize, rhs: usize) {
+ for row in self.iter_mut() {
+ row.swap(lhs, rhs);
+ }
+ }
+
+ fn push_row(&mut self) {
+ let records = std::mem::replace(self, VecRecords::new(vec![]));
+ let mut data: Vec<Vec<_>> = records.into();
+
+ let count_columns = data.get(0).map(|l| l.len()).unwrap_or(0);
+ data.push(vec![T::default(); count_columns]);
+
+ *self = VecRecords::new(data);
+ }
+
+ fn push_column(&mut self) {
+ let records = std::mem::replace(self, VecRecords::new(vec![]));
+ let mut data: Vec<Vec<_>> = records.into();
+
+ for row in &mut data {
+ row.push(T::default());
+ }
+
+ *self = VecRecords::new(data);
+ }
+
+ fn remove_row(&mut self, row: usize) {
+ let records = std::mem::replace(self, VecRecords::new(vec![]));
+ let mut data: Vec<Vec<_>> = records.into();
+
+ let _ = data.remove(row);
+
+ *self = VecRecords::new(data);
+ }
+
+ fn remove_column(&mut self, column: usize) {
+ let records = std::mem::replace(self, VecRecords::new(vec![]));
+ let mut data: Vec<Vec<_>> = records.into();
+
+ for row in &mut data {
+ let _ = row.remove(column);
+ }
+
+ *self = VecRecords::new(data);
+ }
+
+ fn insert_row(&mut self, row: usize) {
+ let records = std::mem::replace(self, VecRecords::new(vec![]));
+ let mut data: Vec<Vec<_>> = records.into();
+
+ let count_columns = data.get(0).map(|l| l.len()).unwrap_or(0);
+ data.insert(row, vec![T::default(); count_columns]);
+
+ *self = VecRecords::new(data);
+ }
+
+ fn insert_column(&mut self, column: usize) {
+ let records = std::mem::replace(self, VecRecords::new(vec![]));
+ let mut data: Vec<Vec<_>> = records.into();
+
+ for row in &mut data {
+ row.insert(column, T::default());
+ }
+
+ *self = VecRecords::new(data);
+ }
+}
diff --git a/vendor/tabled/src/lib.rs b/vendor/tabled/src/lib.rs
new file mode 100644
index 000000000..fb008b302
--- /dev/null
+++ b/vendor/tabled/src/lib.rs
@@ -0,0 +1,490 @@
+//! An easy to use library for pretty print tables of Rust `struct`s and `enum`s.
+//!
+//! The library supports different approaches of table building.
+//! You can use [`Tabled`] trait if the data type is known.
+//! Or you can use [`Builder`] to construct the table from scratch.
+//!
+//! ## Usage
+//!
+//! If you want to build a table for your custom type.
+//! A starting point is to a anotate your type with `#[derive(Tabled)]`.
+//!
+//! Then to provide your collection to [`Table::new`] and you will be set to render table.
+//!
+#![cfg_attr(all(feature = "derive", feature = "std"), doc = "```")]
+#![cfg_attr(not(all(feature = "derive", feature = "std")), doc = "```ignore")]
+//! use tabled::{Tabled, Table};
+//!
+//! #[derive(Tabled)]
+//! struct Language {
+//! name: &'static str,
+//! designed_by: &'static str,
+//! invented_year: usize,
+//! }
+//!
+//! let languages = vec![
+//! Language{
+//! name: "C",
+//! designed_by: "Dennis Ritchie",
+//! invented_year: 1972
+//! },
+//! Language{
+//! name: "Rust",
+//! designed_by: "Graydon Hoare",
+//! invented_year: 2010
+//! },
+//! Language{
+//! name: "Go",
+//! designed_by: "Rob Pike",
+//! invented_year: 2009
+//! },
+//! ];
+//!
+//! let table = Table::new(languages).to_string();
+//!
+//! let expected = "+------+----------------+---------------+\n\
+//! | name | designed_by | invented_year |\n\
+//! +------+----------------+---------------+\n\
+//! | C | Dennis Ritchie | 1972 |\n\
+//! +------+----------------+---------------+\n\
+//! | Rust | Graydon Hoare | 2010 |\n\
+//! +------+----------------+---------------+\n\
+//! | Go | Rob Pike | 2009 |\n\
+//! +------+----------------+---------------+";
+//!
+//! assert_eq!(table, expected);
+//! ```
+//!
+//! Not all types can derive [`Tabled`] trait though.
+//! The example below can't be compiled.
+//!
+//! ```rust,compile_fail
+//! # use tabled::Tabled;
+//! #[derive(Tabled)]
+//! struct SomeType {
+//! field1: SomeOtherType,
+//! }
+//!
+//! struct SomeOtherType;
+//! ```
+//!
+//! Because `tabled` must know what we're up to print as a field, so
+//! each (almoust) field must implement [`std::fmt::Display`].
+//!
+//! ### Default implementations
+//!
+//! [`Table`] can be build from vast majority of Rust's standard types.
+//! This allows you to run the following code.
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Tabled, Table};
+//! let table = Table::new(&[1, 2, 3]);
+//! # let expected = "+-----+\n\
+//! # | i32 |\n\
+//! # +-----+\n\
+//! # | 1 |\n\
+//! # +-----+\n\
+//! # | 2 |\n\
+//! # +-----+\n\
+//! # | 3 |\n\
+//! # +-----+";
+//! # assert_eq!(table.to_string(), expected);
+//! ```
+//!
+//! ### Dynamic table
+//!
+//! When you data scheme is not known at compile time.
+//! You most likely will not able to relay on [`Tabled`] trait.
+//!
+//! So one option would be is to use [`Builder`].
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use std::iter;
+//!
+//! use tabled::{
+//! builder::Builder,
+//! settings::{Modify, object::Rows, Alignment, Style}
+//! };
+//!
+//! let (x, y) = (3, 10);
+//!
+//! let mut builder = Builder::default();
+//!
+//! let header = iter::once(String::from("i"))
+//! .chain((0..y)
+//! .map(|i| i.to_string()));
+//! builder.set_header(header);
+//!
+//! for i in 0..x {
+//! let row = iter::once(i)
+//! .chain((0..y).map(|j| i * j))
+//! .map(|i| i.to_string());
+//! builder.push_record(row);
+//! }
+//!
+//! let table = builder.build()
+//! .with(Style::rounded())
+//! .with(Modify::new(Rows::new(1..)).with(Alignment::left()))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "╭───┬───┬───┬───┬───┬───┬────┬────┬────┬────┬────╮\n",
+//! "│ i │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │\n",
+//! "├───┼───┼───┼───┼───┼───┼────┼────┼────┼────┼────┤\n",
+//! "│ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │\n",
+//! "│ 1 │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │\n",
+//! "│ 2 │ 0 │ 2 │ 4 │ 6 │ 8 │ 10 │ 12 │ 14 │ 16 │ 18 │\n",
+//! "╰───┴───┴───┴───┴───┴───┴────┴────┴────┴────┴────╯",
+//! )
+//! );
+//! ```
+//!
+//! ### Build table using [`row!`] and [`col!`] macros.
+//!
+#![cfg_attr(all(feature = "macros", feature = "std"), doc = "```")]
+#![cfg_attr(not(all(feature = "macros", feature = "std")), doc = "```ignore")]
+//! use tabled::{row, col};
+//!
+//! let table = row![
+//! col!["Hello", "World", "!"],
+//! col!["Hello"; 3],
+//! col!["World"; 3],
+//! ];
+//!
+//! assert_eq!(
+//! table.to_string(),
+//! concat!(
+//! "+-----------+-----------+-----------+\n",
+//! "| +-------+ | +-------+ | +-------+ |\n",
+//! "| | Hello | | | Hello | | | World | |\n",
+//! "| +-------+ | +-------+ | +-------+ |\n",
+//! "| | World | | | Hello | | | World | |\n",
+//! "| +-------+ | +-------+ | +-------+ |\n",
+//! "| | ! | | | Hello | | | World | |\n",
+//! "| +-------+ | +-------+ | +-------+ |\n",
+//! "+-----------+-----------+-----------+",
+//! )
+//! );
+//! ```
+//!
+//! ### Settings
+//!
+//! You can use many settings which is found in [`tabled::settings`] module.
+//!
+//! # Advanced
+//!
+//! ## Alloc
+//!
+//! [`Table`] keeps data buffered, which sometimes not ideal choise.
+//! For such reason there is [`IterTable`] and [`CompactTable`].
+//!
+//! ### Less allocations
+//!
+//! [`IterTable`] stands on a middle ground between [`Table`] and [`CompactTable`].
+//!
+//! It does allocate memory but in a much smaller chunks that a [`Table`] does.
+//! The benefit is that it can be used interchangebly with [`Table`].
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::tables::IterTable;
+//!
+//! let iterator = (0..3).map(|row| (0..4).map(move |col| format!("{}-{}", row, col)));
+//!
+//! let table = IterTable::new(iterator).to_string();
+//!
+//! assert_eq!(
+//! table,
+//! "+-----+-----+-----+-----+\n\
+//! | 0-0 | 0-1 | 0-2 | 0-3 |\n\
+//! +-----+-----+-----+-----+\n\
+//! | 1-0 | 1-1 | 1-2 | 1-3 |\n\
+//! +-----+-----+-----+-----+\n\
+//! | 2-0 | 2-1 | 2-2 | 2-3 |\n\
+//! +-----+-----+-----+-----+",
+//! );
+//! ```
+//!
+//! ## Alloc free (`#nostd`)
+//!
+//! [`CompactTable`] can be configured ('1) to not make any allocations.
+//! But the price is that the set of settings which can be applied to it is limited.
+//!
+//! It also can be printed directly to [`fmt::Write`] to not have any intermidiaries.
+//!
+//! '1. It does not make any allocations in case you provide it with `width` and `count_rows`.
+//!
+//! ```
+//! use tabled::{settings::Style, tables::CompactTable};
+//! use core::fmt::{Write, Result};
+//!
+//! struct StubWriter;
+//!
+//! impl Write for StubWriter {
+//! fn write_str(&mut self, _: &str) -> Result {
+//! Ok(())
+//! }
+//! }
+//!
+//! let data = [
+//! ["FreeBSD", "1993", "William and Lynne Jolitz", "?"],
+//! ["OpenBSD", "1995", "Theo de Raadt", ""],
+//! ["HardenedBSD", "2014", "Oliver Pinter and Shawn Webb", ""],
+//! ];
+//!
+//! let table = CompactTable::from(data).with(Style::psql());
+//!
+//! table.fmt(StubWriter);
+//! ```
+//!
+//! ## More information
+//!
+//! You can find more examples of settings and attributes in
+//! [README.md](https://github.com/zhiburt/tabled/blob/master/README.md)
+//!
+//! [`Builder`]: crate::builder::Builder
+//! [`IterTable`]: crate::tables::IterTable
+//! [`CompactTable`]: crate::tables::CompactTable
+//! [`fmt::Write`]: core::fmt::Write
+//! [`row!`]: crate::row
+//! [`col!`]: crate::col
+
+#![cfg_attr(not(any(feature = "std", test)), no_std)]
+#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
+#![doc(
+ html_logo_url = "https://raw.githubusercontent.com/zhiburt/tabled/86ac146e532ce9f7626608d7fd05072123603a2e/assets/tabled-gear.svg"
+)]
+#![deny(unused_must_use)]
+#![warn(
+ missing_docs,
+ rust_2018_idioms,
+ rust_2018_compatibility,
+ missing_debug_implementations,
+ unreachable_pub,
+ future_incompatible,
+ single_use_lifetimes,
+ trivial_casts,
+ trivial_numeric_casts,
+ unused_extern_crates,
+ unused_import_braces,
+ unused_qualifications,
+ unused_results,
+ unused_variables,
+ variant_size_differences
+)]
+#![allow(clippy::uninlined_format_args)]
+
+#[cfg(feature = "std")]
+mod tabled;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod builder;
+pub mod settings;
+pub mod tables;
+
+#[cfg(feature = "macros")]
+#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
+pub mod macros;
+
+pub mod grid;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use crate::{tabled::Tabled, tables::Table};
+
+/// A derive to implement a [`Tabled`] trait.
+///
+/// The macros available only when `derive` feature in turned on (and it is by default).
+///
+/// To be able to use the derive each field must implement `std::fmt::Display`.
+/// The following example will cause a error because of that.
+///
+/// ```rust,compile_fail
+/// use tabled::Tabled;
+/// #[derive(Tabled)]
+/// struct SomeType {
+/// field1: SomeOtherType,
+/// }
+///
+/// struct SomeOtherType;
+/// ```
+///
+/// Bellow you'll find available options for it.
+///
+/// ### Override a column name
+///
+/// You can use a `#[tabled(rename = "")]` attribute to override a column name.
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// struct Person {
+/// #[tabled(rename = "Name")]
+/// first_name: &'static str,
+/// #[tabled(rename = "Surname")]
+/// last_name: &'static str,
+/// }
+/// ```
+///
+/// ### Hide a column
+///
+/// You can mark fields as hidden in which case they fill be ignored and not be present on a sheet.
+///
+/// A similar affect could be achieved by the means of a `Disable` setting.
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// struct Person {
+/// id: u8,
+/// #[tabled(skip)]
+/// number: &'static str,
+/// name: &'static str,
+/// }
+/// ```
+///
+/// ### Set column order
+///
+/// You can change the order in which they will be displayed in table.
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// struct Person {
+/// id: u8,
+/// #[tabled(order = 0)]
+/// number: &'static str,
+/// #[tabled(order = 1)]
+/// name: &'static str,
+/// }
+/// ```
+///
+/// ### Format fields
+///
+/// As was said already, using `#[derive(Tabled)]` is possible only when all fields implement a `Display` trait.
+/// However, this may be often not the case for example when a field uses the `Option` type. There's 2 common ways how to solve this:
+///
+/// - Implement `Tabled` trait manually for a type.
+/// - Wrap `Option` to something like `DisplayedOption<T>(Option<T>)` and implement a Display trait for it.
+///
+/// Alternatively, you can use the `#[tabled(display_with = "func")]` attribute for the field to specify a display function.
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// pub struct MyRecord {
+/// pub id: i64,
+/// #[tabled(display_with = "display_option")]
+/// pub valid: Option<bool>
+/// }
+///
+/// fn display_option(o: &Option<bool>) -> String {
+/// match o {
+/// Some(s) => format!("is valid thing = {}", s),
+/// None => format!("is not valid"),
+/// }
+/// }
+/// ```
+///
+/// It's also possible to change function argument to be `&self`,
+/// using `#[tabled(display_with("some_function", self))]`
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// pub struct MyRecord {
+/// pub id: i64,
+/// #[tabled(display_with("Self::display_valid", self))]
+/// pub valid: Option<bool>
+/// }
+///
+/// impl MyRecord {
+/// fn display_valid(&self) -> String {
+/// match self.valid {
+/// Some(s) => format!("is valid thing = {}", s),
+/// None => format!("is not valid"),
+/// }
+/// }
+/// }
+/// ```
+///
+/// ### Format headers
+///
+/// Beside `#[tabled(rename = "")]` you can change a format of a column name using
+/// `#[tabled(rename_all = "UPPERCASE")]`.
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// #[tabled(rename_all = "CamelCase")]
+/// struct Person {
+/// id: u8,
+/// number: &'static str,
+/// name: &'static str,
+/// #[tabled(rename_all = "snake_case")]
+/// middle_name: &'static str,
+/// }
+/// ```
+///
+/// ### Inline
+///
+/// It's possible to inline internal data if it implements the `Tabled` trait using `#[tabled(inline)]`.
+/// You can also set a prefix which will be used for all inlined elements by `#[tabled(inline("prefix>>"))]`.
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// struct Person {
+/// id: u8,
+/// name: &'static str,
+/// #[tabled(inline)]
+/// ed: Education,
+/// }
+///
+/// #[derive(Tabled)]
+/// struct Education {
+/// uni: &'static str,
+/// graduated: bool,
+/// }
+/// ```
+///
+/// And it works for enums as well.
+///
+/// ```rust,no_run
+/// use tabled::Tabled;
+///
+/// #[derive(Tabled)]
+/// enum Vehicle {
+/// #[tabled(inline("Auto::"))]
+/// Auto {
+/// model: &'static str,
+/// engine: &'static str,
+/// },
+/// #[tabled(inline)]
+/// Bikecycle(
+/// &'static str,
+/// #[tabled(inline)] Bike,
+/// ),
+/// }
+///
+/// #[derive(Tabled)]
+/// struct Bike {
+/// brand: &'static str,
+/// price: f32,
+/// }
+/// ```
+#[cfg(feature = "derive")]
+#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
+pub use tabled_derive::Tabled;
diff --git a/vendor/tabled/src/macros/col.rs b/vendor/tabled/src/macros/col.rs
new file mode 100644
index 000000000..1fd67de8a
--- /dev/null
+++ b/vendor/tabled/src/macros/col.rs
@@ -0,0 +1,50 @@
+/// Creates a [`Table`] with [`Display`] arguments nested within.
+///
+/// The macros allows several tables to be displayed vertically.
+///
+/// Companion to [`row!`].
+///
+/// # Examples
+/// ```rust,no_run
+/// # use tabled::{row, col, Table};
+/// # let (table1, table2, table3) = (Table::new(&[String::new()]), Table::new(&[String::new()]), Table::new(&[String::new()]));
+/// let new_table = col![table1, table2];
+/// let new_table_of_clones = col![table1; 3];
+/// let columns_and_rows = col![
+/// table1,
+/// row![table2, table3]
+/// ];
+/// ```
+///
+/// [`row!`]: crate::row
+/// [`Table`]: crate::Table
+/// [`Display`]: std::fmt::Display
+#[macro_export]
+#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
+macro_rules! col {
+ // Vertical
+ ( $($table:expr), * $(,)? ) => {{
+ let mut builder = $crate::builder::Builder::default();
+
+ $(
+ builder.push_record([$table.to_string()]);
+ )*
+
+ builder.build()
+ }};
+
+ // Duplicate single item
+ ( $table:expr; $N:expr) => {{
+ let mut builder = $crate::builder::Builder::default();
+
+ let n = $N;
+ if n > 0 {
+ let t = $table.to_string();
+ for _ in 0..$N {
+ builder.push_record([t.clone()]);
+ }
+ }
+
+ builder.build()
+ }};
+}
diff --git a/vendor/tabled/src/macros/mod.rs b/vendor/tabled/src/macros/mod.rs
new file mode 100644
index 000000000..7db0a9caa
--- /dev/null
+++ b/vendor/tabled/src/macros/mod.rs
@@ -0,0 +1,6 @@
+//! This module contains macro functions for dynamic [`Table`] construction.
+//!
+//! [`Table`]: crate::Table
+
+mod col;
+mod row;
diff --git a/vendor/tabled/src/macros/row.rs b/vendor/tabled/src/macros/row.rs
new file mode 100644
index 000000000..23775b90d
--- /dev/null
+++ b/vendor/tabled/src/macros/row.rs
@@ -0,0 +1,44 @@
+/// Creates a [`Table`] with [`Display`] arguments nested within.
+///
+/// The macros allows several tables to be displayed horizontally.
+///
+/// Companion to [`col!`].
+///
+/// # Examples
+/// ```rust,no_run
+/// # use tabled::{row, col, Table};
+/// # let (table1, table2, table3) = (Table::new(&[String::new()]), Table::new(&[String::new()]), Table::new(&[String::new()]));
+/// let new_table = row![table1, table2];
+/// let new_table_of_clones = row![table1; 3];
+/// let rows_and_columns = row![
+/// table1,
+/// col![table2, table3]
+/// ];
+/// ```
+///
+/// [`col!`]: crate::col
+/// [`Table`]: crate::Table
+/// [`Display`]: std::fmt::Display
+#[macro_export]
+#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
+macro_rules! row {
+ // Horizontal Display
+ ( $($table:expr), * $(,)? ) => {{
+ let mut builder = $crate::builder::Builder::default();
+
+ let record = [ $($table.to_string(),)* ];
+ builder.push_record(record);
+
+ builder.build()
+ }};
+
+ // Duplicate single item
+ ( $table:expr; $N:expr) => {{
+ let mut builder = $crate::builder::Builder::default();
+
+ let duplicates = vec![$table.to_string(); $N];
+ builder.push_record(duplicates);
+
+ builder.build()
+ }};
+}
diff --git a/vendor/tabled/src/settings/alignment/mod.rs b/vendor/tabled/src/settings/alignment/mod.rs
new file mode 100644
index 000000000..81d1717a6
--- /dev/null
+++ b/vendor/tabled/src/settings/alignment/mod.rs
@@ -0,0 +1,189 @@
+//! This module contains an [`Alignment`] setting for cells on the [`Table`].
+//!
+//! # Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! # use tabled::{Table, settings::{Alignment, Modify, object::Rows}};
+//! # let data: Vec<&'static str> = Vec::new();
+//! let mut table = Table::new(&data);
+//! table.with(Modify::new(Rows::single(0)).with(Alignment::center()));
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use crate::{
+ grid::config::CompactConfig,
+ grid::config::{AlignmentHorizontal, AlignmentVertical, CompactMultilineConfig},
+ settings::TableOption,
+};
+
+use AlignmentInner::*;
+
+#[cfg(feature = "std")]
+use crate::grid::config::{ColoredConfig, Entity};
+
+/// Alignment represent a horizontal and vertical alignment setting for any cell on a [`Table`].
+///
+/// An alignment strategy can be set by [`AlignmentStrategy`].
+///
+/// # Example
+///
+#[cfg_attr(feature = "std", doc = "```")]
+#[cfg_attr(not(feature = "std"), doc = "```ignore")]
+/// use tabled::{
+/// Table,
+/// settings::{
+/// formatting::AlignmentStrategy,
+/// object::Segment, Alignment, Modify, Style,
+/// }
+/// };
+///
+/// let data = [
+/// ["1", "2", "3"],
+/// ["Some\nMulti\nLine\nText", "and a line", "here"],
+/// ["4", "5", "6"],
+/// ];
+///
+/// let mut table = Table::new(&data);
+/// table
+/// .with(Style::modern())
+/// .with(
+/// Modify::new(Segment::all())
+/// .with(Alignment::right())
+/// .with(Alignment::center())
+/// .with(AlignmentStrategy::PerCell)
+/// );
+///
+/// assert_eq!(
+/// table.to_string(),
+/// concat!(
+/// "┌───────┬────────────┬──────┐\n",
+/// "│ 0 │ 1 │ 2 │\n",
+/// "├───────┼────────────┼──────┤\n",
+/// "│ 1 │ 2 │ 3 │\n",
+/// "├───────┼────────────┼──────┤\n",
+/// "│ Some │ and a line │ here │\n",
+/// "│ Multi │ │ │\n",
+/// "│ Line │ │ │\n",
+/// "│ Text │ │ │\n",
+/// "├───────┼────────────┼──────┤\n",
+/// "│ 4 │ 5 │ 6 │\n",
+/// "└───────┴────────────┴──────┘",
+/// ),
+/// )
+/// ```
+///
+/// [`Table`]: crate::Table
+/// [`AlignmentStrategy`]: crate::settings::formatting::AlignmentStrategy
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Alignment {
+ inner: AlignmentInner,
+}
+
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
+enum AlignmentInner {
+ /// A horizontal alignment.
+ Horizontal(AlignmentHorizontal),
+ /// A vertical alignment.
+ Vertical(AlignmentVertical),
+}
+
+impl Alignment {
+ /// Left constructs a horizontal alignment to [`AlignmentHorizontal::Left`]
+ pub fn left() -> Self {
+ Self::horizontal(AlignmentHorizontal::Left)
+ }
+
+ /// Right constructs a horizontal alignment to [`AlignmentHorizontal::Right`]
+ ///
+ /// ## Notice
+ ///
+ /// When you use [`MinWidth`] the alignment might not work as you expected.
+ /// You could try to apply [`TrimStrategy`] which may help.
+ ///
+ /// [`MinWidth`]: crate::settings::width::MinWidth
+ /// [`TrimStrategy`]: crate::settings::formatting::TrimStrategy
+ pub fn right() -> Self {
+ Self::horizontal(AlignmentHorizontal::Right)
+ }
+
+ /// Center constructs a horizontal alignment to [`AlignmentHorizontal::Center`]
+ ///
+ /// ## Notice
+ ///
+ /// When you use [`MinWidth`] the alignment might not work as you expected.
+ /// You could try to apply [`TrimStrategy`] which may help.
+ ///
+ /// [`MinWidth`]: crate::settings::width::MinWidth
+ /// [`TrimStrategy`]: crate::settings::formatting::TrimStrategy
+ pub const fn center() -> Self {
+ Self::horizontal(AlignmentHorizontal::Center)
+ }
+
+ /// Top constructs a vertical alignment to [`AlignmentVertical::Top`]
+ pub const fn top() -> Self {
+ Self::vertical(AlignmentVertical::Top)
+ }
+
+ /// Bottom constructs a vertical alignment to [`AlignmentVertical::Bottom`]
+ pub const fn bottom() -> Self {
+ Self::vertical(AlignmentVertical::Bottom)
+ }
+
+ /// `Center_vertical` constructs a vertical alignment to [`AlignmentVertical::Center`]
+ pub const fn center_vertical() -> Self {
+ Self::vertical(AlignmentVertical::Center)
+ }
+
+ /// Returns an alignment with the given horizontal alignment.
+ const fn horizontal(alignment: AlignmentHorizontal) -> Self {
+ Self::new(Horizontal(alignment))
+ }
+
+ /// Returns an alignment with the given vertical alignment.
+ const fn vertical(alignment: AlignmentVertical) -> Self {
+ Self::new(Vertical(alignment))
+ }
+
+ const fn new(inner: AlignmentInner) -> Self {
+ Self { inner }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R> crate::settings::CellOption<R, ColoredConfig> for Alignment {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ match self.inner {
+ Horizontal(a) => cfg.set_alignment_horizontal(entity, a),
+ Vertical(a) => cfg.set_alignment_vertical(entity, a),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, D> TableOption<R, D, ColoredConfig> for Alignment {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ match self.inner {
+ Horizontal(a) => cfg.set_alignment_horizontal(Entity::Global, a),
+ Vertical(a) => cfg.set_alignment_vertical(Entity::Global, a),
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, CompactConfig> for Alignment {
+ fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
+ if let Horizontal(a) = self.inner {
+ *cfg = cfg.set_alignment_horizontal(a)
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, CompactMultilineConfig> for Alignment {
+ fn change(self, _: &mut R, cfg: &mut CompactMultilineConfig, _: &mut D) {
+ match self.inner {
+ Horizontal(a) => *cfg = cfg.set_alignment_horizontal(a),
+ Vertical(a) => *cfg = cfg.set_alignment_vertical(a),
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/cell_option.rs b/vendor/tabled/src/settings/cell_option.rs
new file mode 100644
index 000000000..f589ac47f
--- /dev/null
+++ b/vendor/tabled/src/settings/cell_option.rs
@@ -0,0 +1,55 @@
+use crate::grid::{
+ config::Entity,
+ records::{ExactRecords, Records, RecordsMut},
+};
+
+/// A trait for configuring a single cell.
+///
+/// ~~~~ Where cell represented by 'row' and 'column' indexes. ~~~~
+///
+/// A cell can be targeted by [`Cell`].
+///
+/// [`Cell`]: crate::object::Cell
+pub trait CellOption<R, C> {
+ /// Modification function of a single cell.
+ fn change(self, records: &mut R, cfg: &mut C, entity: Entity);
+}
+
+#[cfg(feature = "std")]
+impl<R, C> CellOption<R, C> for String
+where
+ R: Records + ExactRecords + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, cfg: &mut C, entity: Entity) {
+ (&self).change(records, cfg, entity);
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, C> CellOption<R, C> for &String
+where
+ R: Records + ExactRecords + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, _: &mut C, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_cols) {
+ records.set(pos, self.clone());
+ }
+ }
+}
+
+impl<'a, R, C> CellOption<R, C> for &'a str
+where
+ R: Records + ExactRecords + RecordsMut<&'a str>,
+{
+ fn change(self, records: &mut R, _: &mut C, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_cols) {
+ records.set(pos, self);
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/color/mod.rs b/vendor/tabled/src/settings/color/mod.rs
new file mode 100644
index 000000000..f4bfa5c32
--- /dev/null
+++ b/vendor/tabled/src/settings/color/mod.rs
@@ -0,0 +1,346 @@
+//! This module contains a configuration of a [`Border`] or a [`Table`] to set its borders color via [`Color`].
+//!
+//! [`Border`]: crate::settings::Border
+//! [`Table`]: crate::Table
+
+use std::{borrow::Cow, ops::BitOr};
+
+use crate::{
+ grid::{
+ color::{AnsiColor, StaticColor},
+ config::{ColoredConfig, Entity},
+ },
+ settings::{CellOption, TableOption},
+};
+
+/// Color represents a color which can be set to things like [`Border`], [`Padding`] and [`Margin`].
+///
+/// # Example
+///
+/// ```
+/// use tabled::{settings::Color, Table};
+///
+/// let data = [
+/// (0u8, "Hello"),
+/// (1u8, "World"),
+/// ];
+///
+/// let table = Table::new(data)
+/// .with(Color::BG_BLUE)
+/// .to_string();
+///
+/// println!("{}", table);
+/// ```
+///
+/// [`Padding`]: crate::settings::Padding
+/// [`Margin`]: crate::settings::Margin
+/// [`Border`]: crate::settings::Border
+#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Color(AnsiColor<'static>);
+
+// todo: Add | operation to combine colors
+
+#[rustfmt::skip]
+impl Color {
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BLACK: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[30m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BLUE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[34m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_BLACK: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[90m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_BLUE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[94m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_CYAN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[96m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_GREEN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[92m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_MAGENTA: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[95m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_RED: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[91m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_WHITE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[97m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_BRIGHT_YELLOW: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[93m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_CYAN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[36m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_GREEN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[32m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_MAGENTA: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[35m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_RED: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[31m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_WHITE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[37m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const FG_YELLOW: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[33m"), Cow::Borrowed("\u{1b}[39m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+
+ pub const BG_BLACK: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[40m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BLUE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[44m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_BLACK: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[100m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_BLUE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[104m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_CYAN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[106m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_GREEN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[102m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_MAGENTA: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[105m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_RED: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[101m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_WHITE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[107m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_BRIGHT_YELLOW: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[103m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_CYAN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[46m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_GREEN: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[42m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_MAGENTA: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[45m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_RED: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[41m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_WHITE: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[47m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BG_YELLOW: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[43m"), Cow::Borrowed("\u{1b}[49m")));
+ /// A color representation.
+ ///
+ /// Notice that the colors are constants so you can't combine them.
+ pub const BOLD: Self = Self(AnsiColor::new(Cow::Borrowed("\u{1b}[1m"), Cow::Borrowed("\u{1b}[22m")));
+}
+
+impl Color {
+ /// Creates a new [`Color`]` instance, with ANSI prefix and ANSI suffix.
+ /// You can use [`TryFrom`] to construct it from [`String`].
+ pub fn new(prefix: String, suffix: String) -> Self {
+ Self(AnsiColor::new(prefix.into(), suffix.into()))
+ }
+}
+
+impl From<Color> for AnsiColor<'static> {
+ fn from(c: Color) -> Self {
+ c.0
+ }
+}
+
+impl From<AnsiColor<'static>> for Color {
+ fn from(c: AnsiColor<'static>) -> Self {
+ Self(c)
+ }
+}
+
+impl From<StaticColor> for Color {
+ fn from(c: StaticColor) -> Self {
+ Self(AnsiColor::new(
+ Cow::Borrowed(c.get_prefix()),
+ Cow::Borrowed(c.get_suffix()),
+ ))
+ }
+}
+
+impl BitOr for Color {
+ type Output = Color;
+
+ fn bitor(self, rhs: Self) -> Self::Output {
+ let l_prefix = self.0.get_prefix();
+ let l_suffix = self.0.get_suffix();
+ let r_prefix = rhs.0.get_prefix();
+ let r_suffix = rhs.0.get_suffix();
+
+ let mut prefix = l_prefix.to_string();
+ if l_prefix != r_prefix {
+ prefix.push_str(r_prefix);
+ }
+
+ let mut suffix = l_suffix.to_string();
+ if l_suffix != r_suffix {
+ suffix.push_str(r_suffix);
+ }
+
+ Self::new(prefix, suffix)
+ }
+}
+
+#[cfg(feature = "color")]
+impl std::convert::TryFrom<&str> for Color {
+ type Error = ();
+
+ fn try_from(value: &str) -> Result<Self, Self::Error> {
+ AnsiColor::try_from(value).map(Color)
+ }
+}
+
+#[cfg(feature = "color")]
+impl std::convert::TryFrom<String> for Color {
+ type Error = ();
+
+ fn try_from(value: String) -> Result<Self, Self::Error> {
+ AnsiColor::try_from(value).map(Color)
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for Color {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let _ = cfg.set_color(Entity::Global, self.0.clone());
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for Color {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let _ = cfg.set_color(entity, self.0.clone());
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for &Color {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let _ = cfg.set_color(entity, self.0.clone());
+ }
+}
+
+impl crate::grid::color::Color for Color {
+ fn fmt_prefix<W: std::fmt::Write>(&self, f: &mut W) -> std::fmt::Result {
+ self.0.fmt_prefix(f)
+ }
+
+ fn fmt_suffix<W: std::fmt::Write>(&self, f: &mut W) -> std::fmt::Result {
+ self.0.fmt_suffix(f)
+ }
+
+ fn colorize<W: std::fmt::Write>(&self, f: &mut W, text: &str) -> std::fmt::Result {
+ self.0.colorize(f, text)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[cfg(feature = "color")]
+ use ::{owo_colors::OwoColorize, std::convert::TryFrom};
+
+ #[test]
+ fn test_xor_operation() {
+ assert_eq!(
+ Color::FG_BLACK | Color::FG_BLUE,
+ Color::new(
+ String::from("\u{1b}[30m\u{1b}[34m"),
+ String::from("\u{1b}[39m")
+ )
+ );
+ assert_eq!(
+ Color::FG_BRIGHT_GREEN | Color::BG_BLUE,
+ Color::new(
+ String::from("\u{1b}[92m\u{1b}[44m"),
+ String::from("\u{1b}[39m\u{1b}[49m")
+ )
+ );
+ assert_eq!(
+ Color::new(String::from("..."), String::from("!!!"))
+ | Color::new(String::from("@@@"), String::from("###")),
+ Color::new(String::from("...@@@"), String::from("!!!###"))
+ );
+ assert_eq!(
+ Color::new(String::from("..."), String::from("!!!"))
+ | Color::new(String::from("@@@"), String::from("###"))
+ | Color::new(String::from("$$$"), String::from("%%%")),
+ Color::new(String::from("...@@@$$$"), String::from("!!!###%%%"))
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn test_try_from() {
+ assert_eq!(Color::try_from(""), Err(()));
+ assert_eq!(Color::try_from("".red().on_green().to_string()), Err(()));
+ assert_eq!(
+ Color::try_from("."),
+ Ok(Color::new(String::new(), String::new()))
+ );
+ assert_eq!(
+ Color::try_from("...."),
+ Ok(Color::new(String::new(), String::new()))
+ );
+ assert_eq!(
+ Color::try_from(".".red().on_green().to_string()),
+ Ok(Color::new(
+ String::from("\u{1b}[31m\u{1b}[42m"),
+ String::from("\u{1b}[39m\u{1b}[49m")
+ ))
+ );
+ assert_eq!(
+ Color::try_from("....".red().on_green().to_string()),
+ Ok(Color::new(
+ String::from("\u{1b}[31m\u{1b}[42m"),
+ String::from("\u{1b}[39m\u{1b}[49m")
+ ))
+ );
+ }
+}
diff --git a/vendor/tabled/src/settings/concat/mod.rs b/vendor/tabled/src/settings/concat/mod.rs
new file mode 100644
index 000000000..23b0dcda1
--- /dev/null
+++ b/vendor/tabled/src/settings/concat/mod.rs
@@ -0,0 +1,171 @@
+//! This module contains a [`Concat`] primitive which can be in order to combine 2 [`Table`]s into 1.
+//!
+//! # Example
+//!
+//! ```
+//! use tabled::{Table, settings::Concat};
+//! let table1 = Table::new([0, 1, 2, 3]);
+//! let table2 = Table::new(["A", "B", "C", "D"]);
+//!
+//! let mut table3 = table1;
+//! table3.with(Concat::horizontal(table2));
+//! ```
+
+use std::borrow::Cow;
+
+use crate::{
+ grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut, Resizable},
+ settings::TableOption,
+ Table,
+};
+
+/// [`Concat`] concatenates tables along a particular axis [Horizontal | Vertical].
+/// It doesn't do any key or column comparisons like SQL's join does.
+///
+/// When the tables has different sizes, empty cells will be created by default.
+///
+/// [`Concat`] in horizontal mode has similar behaviour to tuples `(a, b)`.
+/// But it behaves on tables rather than on an actual data.
+///
+/// # Example
+///
+///
+#[cfg_attr(feature = "derive", doc = "```")]
+#[cfg_attr(not(feature = "derive"), doc = "```ignore")]
+/// use tabled::{Table, Tabled, settings::{Style, Concat}};
+///
+/// #[derive(Tabled)]
+/// struct Message {
+/// id: &'static str,
+/// text: &'static str,
+/// }
+///
+/// #[derive(Tabled)]
+/// struct Department(#[tabled(rename = "department")] &'static str);
+///
+/// let messages = [
+/// Message { id: "0", text: "Hello World" },
+/// Message { id: "1", text: "Do do do something", },
+/// ];
+///
+/// let departments = [
+/// Department("Admins"),
+/// Department("DevOps"),
+/// Department("R&D"),
+/// ];
+///
+/// let mut table = Table::new(messages);
+/// table
+/// .with(Concat::horizontal(Table::new(departments)))
+/// .with(Style::extended());
+///
+/// assert_eq!(
+/// table.to_string(),
+/// concat!(
+/// "╔════╦════════════════════╦════════════╗\n",
+/// "║ id ║ text ║ department ║\n",
+/// "╠════╬════════════════════╬════════════╣\n",
+/// "║ 0 ║ Hello World ║ Admins ║\n",
+/// "╠════╬════════════════════╬════════════╣\n",
+/// "║ 1 ║ Do do do something ║ DevOps ║\n",
+/// "╠════╬════════════════════╬════════════╣\n",
+/// "║ ║ ║ R&D ║\n",
+/// "╚════╩════════════════════╩════════════╝",
+/// )
+/// )
+/// ```
+
+#[derive(Debug)]
+pub struct Concat {
+ table: Table,
+ mode: ConcatMode,
+ default_cell: Cow<'static, str>,
+}
+
+#[derive(Debug)]
+enum ConcatMode {
+ Vertical,
+ Horizontal,
+}
+
+impl Concat {
+ fn new(table: Table, mode: ConcatMode) -> Self {
+ Self {
+ table,
+ mode,
+ default_cell: Cow::Borrowed(""),
+ }
+ }
+
+ /// Concatenate 2 tables horizontally (along axis=0)
+ pub fn vertical(table: Table) -> Self {
+ Self::new(table, ConcatMode::Vertical)
+ }
+
+ /// Concatenate 2 tables vertically (along axis=1)
+ pub fn horizontal(table: Table) -> Self {
+ Self::new(table, ConcatMode::Horizontal)
+ }
+
+ /// Sets a cell's content for cases where 2 tables has different sizes.
+ pub fn default_cell(mut self, cell: impl Into<Cow<'static, str>>) -> Self {
+ self.default_cell = cell.into();
+ self
+ }
+}
+
+impl<R, D, C> TableOption<R, D, C> for Concat
+where
+ R: Records + ExactRecords + Resizable + PeekableRecords + RecordsMut<String>,
+{
+ fn change(mut self, records: &mut R, _: &mut C, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ let rhs = &mut self.table;
+ match self.mode {
+ ConcatMode::Horizontal => {
+ for _ in 0..rhs.count_columns() {
+ records.push_column();
+ }
+
+ for row in count_rows..rhs.count_rows() {
+ records.push_row();
+
+ for col in 0..records.count_columns() {
+ records.set((row, col), self.default_cell.to_string());
+ }
+ }
+
+ for row in 0..rhs.shape().0 {
+ for col in 0..rhs.shape().1 {
+ let text = rhs.get_records().get_text((row, col)).to_string();
+ let col = col + count_cols;
+ records.set((row, col), text);
+ }
+ }
+ }
+ ConcatMode::Vertical => {
+ for _ in 0..rhs.count_rows() {
+ records.push_row();
+ }
+
+ for col in count_cols..rhs.shape().1 {
+ records.push_column();
+
+ for row in 0..records.count_rows() {
+ records.set((row, col), self.default_cell.to_string());
+ }
+ }
+
+ for row in 0..rhs.shape().0 {
+ for col in 0..rhs.shape().1 {
+ let text = rhs.get_records().get_text((row, col)).to_string();
+ let row = row + count_rows;
+ records.set((row, col), text);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/disable/mod.rs b/vendor/tabled/src/settings/disable/mod.rs
new file mode 100644
index 000000000..bfbf03db0
--- /dev/null
+++ b/vendor/tabled/src/settings/disable/mod.rs
@@ -0,0 +1,196 @@
+//! This module contains a [`Disable`] structure which helps to
+//! remove an etheir column or row from a [`Table`].
+//!
+//! # Example
+//!
+//! ```rust,no_run
+//! # use tabled::{Table, settings::{Disable, object::Rows}};
+//! # let data: Vec<&'static str> = Vec::new();
+//! let table = Table::new(&data).with(Disable::row(Rows::first()));
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use std::marker::PhantomData;
+
+use crate::{
+ grid::records::{ExactRecords, Records, Resizable},
+ settings::{locator::Locator, TableOption},
+};
+
+/// Disable removes particular rows/columns from a [`Table`].
+///
+/// It tries to keeps track of style changes which may occur.
+/// But it's not guaranteed will be the way you would expect it to be.
+///
+/// Generally you should avoid use of [`Disable`] because it's a slow function and modifies the underlying records.
+/// Providing correct data right away is better.
+///
+/// # Example
+///
+/// ```
+/// use tabled::{Table, settings::{Disable, object::Rows}};
+///
+/// let data = vec!["Hello", "World", "!!!"];
+///
+/// let table = Table::new(data).with(Disable::row(Rows::new(1..2))).to_string();
+///
+/// assert_eq!(
+/// table,
+/// "+-------+\n\
+/// | &str |\n\
+/// +-------+\n\
+/// | World |\n\
+/// +-------+\n\
+/// | !!! |\n\
+/// +-------+"
+/// );
+///
+/// ```
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Disable<L, Target> {
+ locator: L,
+ target: PhantomData<Target>,
+}
+
+impl<L> Disable<L, TargetColumn> {
+ /// Disable columns.
+ ///
+ /// Available locators are:
+ ///
+ /// - [`Columns`]
+ /// - [`Column`]
+ /// - [`FirstColumn`]
+ /// - [`LastColumn`]
+ /// - [`ByColumnName`]
+ ///
+ /// ```rust
+ /// use tabled::{builder::Builder, settings::{Disable, locator::ByColumnName, object::Columns}};
+ ///
+ /// let mut builder = Builder::default();
+ ///
+ /// builder.push_record(["col1", "col2", "col3"]);
+ /// builder.push_record(["Hello", "World", "1"]);
+ ///
+ /// let table = builder.build()
+ /// .with(Disable::column(ByColumnName::new("col3")))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+-------+-------+\n\
+ /// | col1 | col2 |\n\
+ /// +-------+-------+\n\
+ /// | Hello | World |\n\
+ /// +-------+-------+"
+ /// );
+ /// ```
+ ///
+ /// [`Columns`]: crate::settings::object::Columns
+ /// [`Column`]: crate::settings::object::Column
+ /// [`FirstColumn`]: crate::settings::object::FirstColumn
+ /// [`LastColumn`]: crate::settings::object::LastColumn
+ /// [`ByColumnName`]: crate::settings::locator::ByColumnName
+ pub fn column(locator: L) -> Self {
+ Self {
+ locator,
+ target: PhantomData,
+ }
+ }
+}
+
+impl<L> Disable<L, TargetRow> {
+ /// Disable rows.
+ ///
+ /// Available locators are:
+ ///
+ /// - [`Rows`]
+ /// - [`Row`]
+ /// - [`FirstRow`]
+ /// - [`LastRow`]
+ ///
+ /// ```rust
+ /// use tabled::{settings::{Disable, object::Rows}, builder::Builder};
+ ///
+ /// let mut builder = Builder::default();
+ /// builder.push_record(["col1", "col2", "col3"]);
+ /// builder.push_record(["Hello", "World", "1"]);
+ ///
+ /// let table = builder.build()
+ /// .with(Disable::row(Rows::first()))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+-------+-------+---+\n\
+ /// | Hello | World | 1 |\n\
+ /// +-------+-------+---+"
+ /// );
+ /// ```
+ ///
+ /// [`Rows`]: crate::settings::object::Rows
+ /// [`Row`]: crate::settings::object::Row
+ /// [`FirstRow`]: crate::settings::object::FirstRow
+ /// [`LastRow`]: crate::settings::object::LastRow
+ pub fn row(locator: L) -> Self {
+ Self {
+ locator,
+ target: PhantomData,
+ }
+ }
+}
+
+/// A marker struct for [`Disable`].
+#[derive(Debug)]
+pub struct TargetRow;
+
+/// A marker struct for [`Disable`].
+#[derive(Debug)]
+pub struct TargetColumn;
+
+impl<L, R, D, C> TableOption<R, D, C> for Disable<L, TargetColumn>
+where
+ for<'a> L: Locator<&'a R, Coordinate = usize>,
+ R: Records + Resizable,
+{
+ fn change(mut self, records: &mut R, _: &mut C, _: &mut D) {
+ let columns = self.locator.locate(records).into_iter().collect::<Vec<_>>();
+
+ let mut shift = 0;
+ for col in columns.into_iter() {
+ if col - shift > records.count_columns() {
+ continue;
+ }
+
+ records.remove_column(col - shift);
+ shift += 1;
+ }
+
+ // fixme: I am pretty sure that we violate span constrains by removing rows/cols
+ // Because span may be bigger then the max number of rows/cols
+ }
+}
+
+impl<L, R, D, C> TableOption<R, D, C> for Disable<L, TargetRow>
+where
+ for<'a> L: Locator<&'a R, Coordinate = usize>,
+ R: ExactRecords + Resizable,
+{
+ fn change(mut self, records: &mut R, _: &mut C, _: &mut D) {
+ let rows = self.locator.locate(records).into_iter().collect::<Vec<_>>();
+
+ let mut shift = 0;
+ for row in rows.into_iter() {
+ if row - shift > records.count_rows() {
+ continue;
+ }
+
+ records.remove_row(row - shift);
+ shift += 1;
+ }
+
+ // fixme: I am pretty sure that we violate span constrains by removing rows/cols
+ // Because span may be bigger then the max number of rows/cols
+ }
+}
diff --git a/vendor/tabled/src/settings/duplicate/mod.rs b/vendor/tabled/src/settings/duplicate/mod.rs
new file mode 100644
index 000000000..dec967bd6
--- /dev/null
+++ b/vendor/tabled/src/settings/duplicate/mod.rs
@@ -0,0 +1,150 @@
+//! This module contains an [`Dup`] setting the [`Table`].
+//!
+//! # Example
+//!
+//! ```
+//! # use tabled::{Table, settings::{Dup, object::{Columns, Rows}}};
+//! # let data: Vec<&'static str> = Vec::new();
+//! let mut table = Table::new(&data);
+//! table.with(Dup::new(Rows::first(), Columns::first()));
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use papergrid::config::Position;
+
+use crate::{
+ grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ settings::{object::Object, TableOption},
+};
+
+/// [`Dup`] duplicates a given set of cells into another set of ones [`Table`].
+///
+/// # Example
+///
+/// ```
+/// use tabled::{Table, settings::{object::Rows, Dup}};
+///
+/// let data = [
+/// ["1", "2", "3"],
+/// ["Some\nMulti\nLine\nText", "and a line", "here"],
+/// ["4", "5", "6"],
+/// ];
+///
+/// let mut table = Table::new(&data);
+/// table.with(Dup::new(Rows::single(1), Rows::single(2)));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+-------+------------+------+\n\
+/// | 0 | 1 | 2 |\n\
+/// +-------+------------+------+\n\
+/// | Some | and a line | here |\n\
+/// | Multi | | |\n\
+/// | Line | | |\n\
+/// | Text | | |\n\
+/// +-------+------------+------+\n\
+/// | Some | and a line | here |\n\
+/// | Multi | | |\n\
+/// | Line | | |\n\
+/// | Text | | |\n\
+/// +-------+------------+------+\n\
+/// | 4 | 5 | 6 |\n\
+/// +-------+------------+------+",
+/// )
+/// ```
+///
+/// [`Table`]: crate::Table
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Dup<Dst, Src> {
+ src: Src,
+ dst: Dst,
+}
+
+impl<Dst, Src> Dup<Dst, Src> {
+ /// New creates a new [`Dup`] modifier.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use tabled::{Table, settings::{Dup, object::{Columns, Rows}}};
+ /// # let data: Vec<&'static str> = Vec::new();
+ /// let mut table = Table::new(&data);
+ /// table.with(Dup::new(Rows::first(), Columns::last()));
+ /// ```
+ pub fn new(dst: Dst, src: Src) -> Self {
+ Self { src, dst }
+ }
+}
+
+impl<Dst, Src, R, D, C> TableOption<R, D, C> for Dup<Dst, Src>
+where
+ Dst: Object<R>,
+ Src: Object<R>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, _: &mut C, _: &mut D) {
+ let input = collect_input(records, self.src);
+ set_cells(records, &input, self.dst);
+ }
+}
+
+fn collect_input<R, O>(records: &mut R, src: O) -> Vec<String>
+where
+ O: Object<R>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ let mut input = Vec::new();
+ for entity in src.cells(records) {
+ for pos in entity.iter(count_rows, count_columns) {
+ if !is_valid_cell(pos, count_rows, count_columns) {
+ continue;
+ }
+
+ let text = records.get_text(pos).to_owned();
+ input.push(text);
+ }
+ }
+
+ input
+}
+
+fn set_cells<R, O>(records: &mut R, src: &[String], dst: O)
+where
+ O: Object<R>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ if src.is_empty() {
+ return;
+ }
+
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ for entity in dst.cells(records) {
+ let mut source = src.iter().cycle();
+ for pos in entity.iter(count_rows, count_columns) {
+ if !is_valid_cell(pos, count_rows, count_columns) {
+ continue;
+ }
+
+ let text = source.next().unwrap().clone();
+ records.set(pos, text);
+ }
+ }
+}
+
+fn is_valid_cell((row, col): Position, count_rows: usize, count_columns: usize) -> bool {
+ if row > count_rows {
+ return false;
+ }
+
+ if col > count_columns {
+ return false;
+ }
+
+ true
+}
diff --git a/vendor/tabled/src/settings/extract/mod.rs b/vendor/tabled/src/settings/extract/mod.rs
new file mode 100644
index 000000000..bba90a0db
--- /dev/null
+++ b/vendor/tabled/src/settings/extract/mod.rs
@@ -0,0 +1,257 @@
+//! This module contains an [`Extract`] structure which is used to
+//! obtain an ordinary segment from the [`Table`].
+//!
+//! There's a similar structure [`Highlight`] which does a highlighting a of segments.
+//!
+//! [`Table`]: crate::Table
+//! [`Highlight`]: crate::settings::highlight::Highlight
+
+use core::cmp::min;
+use core::ops::{Bound, RangeBounds, RangeFull};
+
+use crate::{
+ grid::records::{ExactRecords, Records, Resizable},
+ settings::TableOption,
+};
+
+/// Returns a new [`Table`] that reflects a segment of the referenced [`Table`]
+///
+/// # Example
+///
+#[cfg_attr(feature = "std", doc = "```")]
+#[cfg_attr(not(feature = "std"), doc = "```ignore")]
+/// use tabled::{Table, settings::{Format, object::Rows, Modify, Extract}};
+///
+/// let data = vec![
+/// (0, "Grodno", true),
+/// (1, "Minsk", true),
+/// (2, "Hamburg", false),
+/// (3, "Brest", true),
+/// ];
+///
+/// let table = Table::new(&data)
+/// .with(Modify::new(Rows::new(1..)).with(Format::content(|s| format!(": {} :", s))))
+/// .with(Extract::segment(1..=2, 1..))
+/// .to_string();
+///
+/// assert_eq!(table, "+------------+----------+\n\
+/// | : Grodno : | : true : |\n\
+/// +------------+----------+\n\
+/// | : Minsk : | : true : |\n\
+/// +------------+----------+");
+/// ```
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Extract<R, C> {
+ rows: R,
+ columns: C,
+}
+
+impl<R, C> Extract<R, C>
+where
+ R: RangeBounds<usize>,
+ C: RangeBounds<usize>,
+{
+ /// Returns a new [`Table`] that reflects a segment of the referenced [`Table`]
+ ///
+ /// ```rust,no_run
+ /// # use tabled::settings::Extract;
+ /// let rows = 1..3;
+ /// let columns = 1..;
+ /// Extract::segment(rows, columns);
+ /// ```
+ ///
+ /// # Range
+ ///
+ /// A [`RangeBounds`] argument can be less than or equal to the shape of a [`Table`]
+ ///
+ /// If a [`RangeBounds`] argument is malformed or too large the thread will panic
+ ///
+ /// ```text
+ /// // Empty Full Out of bounds
+ /// Extract::segment(0..0, 0..0) Extract::segment(.., ..) Extract::segment(0..1, ..4)
+ /// []. . . [O O O [O O O X] //ERROR
+ /// . . . O O O . . .
+ /// . . . O O O] . . .
+ /// ```
+ ///
+ /// [`Table`]: crate::Table
+ pub fn segment(rows: R, columns: C) -> Self {
+ Extract { rows, columns }
+ }
+}
+
+impl<R> Extract<R, RangeFull>
+where
+ R: RangeBounds<usize>,
+{
+ /// Returns a new [`Table`] that reflects a segment of the referenced [`Table`]
+ ///
+ /// The segment is defined by [`RangeBounds`] for Rows
+ ///
+ /// ```rust,no_run
+ /// # use tabled::settings::Extract;
+ /// Extract::rows(1..3);
+ /// ```
+ ///
+ /// # Range
+ ///
+ /// A [`RangeBounds`] argument can be less than or equal to the shape of a [`Table`]
+ ///
+ /// If a [`RangeBounds`] argument is malformed or too large the thread will panic
+ ///
+ /// ```text
+ /// // Empty Full Out of bounds
+ /// Extract::rows(0..0) Extract::rows(..) Extract::rows(0..4)
+ /// []. . . [O O O [O O O
+ /// . . . O O O O O O
+ /// . . . O O O] O O O
+ /// X X X] // ERROR
+ /// ```
+ ///
+ /// [`Table`]: crate::Table
+ pub fn rows(rows: R) -> Self {
+ Extract { rows, columns: .. }
+ }
+}
+
+impl<C> Extract<RangeFull, C>
+where
+ C: RangeBounds<usize>,
+{
+ /// Returns a new [`Table`] that reflects a segment of the referenced [`Table`]
+ ///
+ /// The segment is defined by [`RangeBounds`] for columns.
+ ///
+ /// ```rust,no_run
+ /// # use tabled::settings::Extract;
+ /// Extract::columns(1..3);
+ /// ```
+ ///
+ /// # Range
+ ///
+ /// A [`RangeBounds`] argument can be less than or equal to the shape of a [`Table`]
+ ///
+ /// If a [`RangeBounds`] argument is malformed or too large the thread will panic
+ ///
+ /// ```text
+ /// // Empty Full Out of bounds
+ /// Extract::columns(0..0) Extract::columns(..) Extract::columns(0..4)
+ /// []. . . [O O O [O O O X
+ /// . . . O O O O O O X
+ /// . . . O O O] O O O X] // ERROR
+ /// ```
+ ///
+ /// [`Table`]: crate::Table
+ pub fn columns(columns: C) -> Self {
+ Extract { rows: .., columns }
+ }
+}
+
+impl<R, C, RR, D, Cfg> TableOption<RR, D, Cfg> for Extract<R, C>
+where
+ R: RangeBounds<usize> + Clone,
+ C: RangeBounds<usize> + Clone,
+ RR: Records + ExactRecords + Resizable,
+{
+ fn change(self, records: &mut RR, _: &mut Cfg, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ let mut rows = bounds_to_usize(self.rows.start_bound(), self.rows.end_bound(), count_rows);
+ let mut cols = bounds_to_usize(
+ self.columns.start_bound(),
+ self.columns.end_bound(),
+ count_columns,
+ );
+
+ // Cleanup table in case if boundaries are exceeded.
+ //
+ // todo: can be optimized by adding a clear() method to Resizable
+ rows.0 = min(rows.0, count_rows);
+ cols.0 = min(cols.0, count_columns);
+
+ extract(records, (count_rows, count_columns), rows, cols);
+ }
+}
+
+/// Returns a new [`Grid`] that reflects a segment of the referenced [`Grid`].
+///
+/// # Example
+///
+/// ```text
+/// grid
+/// +---+---+---+
+/// |0-0|0-1|0-2|
+/// +---+---+---+
+/// |1-0|1-1|1-2|
+/// +---+---+---+
+/// |2-0|2-1|2-2|
+/// +---+---+---+
+///
+/// let rows = ..;
+/// let columns = ..1;
+/// grid.extract(rows, columns)
+///
+/// grid
+/// +---+
+/// |0-0|
+/// +---+
+/// |1-0|
+/// +---+
+/// |2-0|
+/// +---+
+/// ```
+fn extract<R>(
+ records: &mut R,
+ (count_rows, count_cols): (usize, usize),
+ (start_row, end_row): (usize, usize),
+ (start_col, end_col): (usize, usize),
+) where
+ R: Resizable,
+{
+ for (i, row) in (0..start_row).enumerate() {
+ let row = row - i;
+ records.remove_row(row);
+ }
+
+ let count_rows = count_rows - start_row;
+ let end_row = end_row - start_row;
+ for (i, row) in (end_row..count_rows).enumerate() {
+ let row = row - i;
+ records.remove_row(row);
+ }
+
+ for (i, col) in (0..start_col).enumerate() {
+ let col = col - i;
+ records.remove_column(col);
+ }
+
+ let count_cols = count_cols - start_col;
+ let end_col = end_col - start_col;
+ for (i, col) in (end_col..count_cols).enumerate() {
+ let col = col - i;
+ records.remove_column(col);
+ }
+}
+
+fn bounds_to_usize(
+ left: Bound<&usize>,
+ right: Bound<&usize>,
+ count_elements: usize,
+) -> (usize, usize) {
+ match (left, right) {
+ (Bound::Included(x), Bound::Included(y)) => (*x, y + 1),
+ (Bound::Included(x), Bound::Excluded(y)) => (*x, *y),
+ (Bound::Included(x), Bound::Unbounded) => (*x, count_elements),
+ (Bound::Unbounded, Bound::Unbounded) => (0, count_elements),
+ (Bound::Unbounded, Bound::Included(y)) => (0, y + 1),
+ (Bound::Unbounded, Bound::Excluded(y)) => (0, *y),
+ (Bound::Excluded(_), Bound::Unbounded)
+ | (Bound::Excluded(_), Bound::Included(_))
+ | (Bound::Excluded(_), Bound::Excluded(_)) => {
+ unreachable!("A start bound can't be excluded")
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/format/format_config.rs b/vendor/tabled/src/settings/format/format_config.rs
new file mode 100644
index 000000000..247ead344
--- /dev/null
+++ b/vendor/tabled/src/settings/format/format_config.rs
@@ -0,0 +1,14 @@
+use crate::settings::TableOption;
+
+/// This is a struct wrapper for a lambda which changes config.
+#[derive(Debug)]
+pub struct FormatConfig<F>(pub(crate) F);
+
+impl<F, R, D, C> TableOption<R, D, C> for FormatConfig<F>
+where
+ F: FnMut(&mut C),
+{
+ fn change(mut self, _: &mut R, cfg: &mut C, _: &mut D) {
+ (self.0)(cfg);
+ }
+}
diff --git a/vendor/tabled/src/settings/format/format_content.rs b/vendor/tabled/src/settings/format/format_content.rs
new file mode 100644
index 000000000..c14eee64f
--- /dev/null
+++ b/vendor/tabled/src/settings/format/format_content.rs
@@ -0,0 +1,86 @@
+use crate::{
+ grid::config::Entity,
+ grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ settings::{CellOption, TableOption},
+};
+
+/// A lambda which formats cell content.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct FormatContent<F> {
+ f: F,
+ multiline: bool,
+}
+
+impl<F> FormatContent<F> {
+ pub(crate) fn new(f: F) -> Self {
+ Self {
+ f,
+ multiline: false,
+ }
+ }
+}
+
+impl<F> FormatContent<F> {
+ /// Multiline a helper function for changing multiline content of cell.
+ /// Using this formatting applied for all rows not to a string as a whole.
+ ///
+ /// ```rust,no_run
+ /// use tabled::{Table, settings::{Format, object::Segment, Modify}};
+ ///
+ /// let data: Vec<&'static str> = Vec::new();
+ /// let table = Table::new(&data)
+ /// .with(Modify::new(Segment::all()).with(Format::content(|s| s.to_string()).multiline()))
+ /// .to_string();
+ /// ```
+ pub fn multiline(mut self) -> Self {
+ self.multiline = true;
+ self
+ }
+}
+
+impl<F, R, D, C> TableOption<R, D, C> for FormatContent<F>
+where
+ F: FnMut(&str) -> String + Clone,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, cfg: &mut C, _: &mut D) {
+ CellOption::change(self, records, cfg, Entity::Global);
+ }
+}
+
+impl<F, R, C> CellOption<R, C> for FormatContent<F>
+where
+ F: FnMut(&str) -> String + Clone,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ fn change(mut self, records: &mut R, _: &mut C, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_cols) {
+ let is_valid_pos = pos.0 < count_rows && pos.1 < count_cols;
+ if !is_valid_pos {
+ continue;
+ }
+
+ let content = records.get_text(pos);
+ let content = if self.multiline {
+ multiline(self.f.clone())(content)
+ } else {
+ (self.f)(content)
+ };
+ records.set(pos, content);
+ }
+ }
+}
+
+fn multiline<F: FnMut(&str) -> String>(mut f: F) -> impl FnMut(&str) -> String {
+ move |s: &str| {
+ let mut v = Vec::new();
+ for line in s.lines() {
+ v.push(f(line));
+ }
+
+ v.join("\n")
+ }
+}
diff --git a/vendor/tabled/src/settings/format/format_positioned.rs b/vendor/tabled/src/settings/format/format_positioned.rs
new file mode 100644
index 000000000..e121f007e
--- /dev/null
+++ b/vendor/tabled/src/settings/format/format_positioned.rs
@@ -0,0 +1,51 @@
+use crate::{
+ grid::config::Entity,
+ grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ settings::{CellOption, TableOption},
+};
+
+/// [`FormatContentPositioned`] is like a [`FormatContent`] an abstraction over a function you can use against a cell.
+///
+/// It different from [`FormatContent`] that it provides a row and column index.
+///
+/// [`FormatContent`]: crate::settings::format::FormatContent
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct FormatContentPositioned<F>(F);
+
+impl<F> FormatContentPositioned<F> {
+ pub(crate) fn new(f: F) -> Self {
+ Self(f)
+ }
+}
+
+impl<F, R, D, C> TableOption<R, D, C> for FormatContentPositioned<F>
+where
+ F: FnMut(&str, (usize, usize)) -> String,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, cfg: &mut C, _: &mut D) {
+ CellOption::change(self, records, cfg, Entity::Global);
+ }
+}
+
+impl<F, R, C> CellOption<R, C> for FormatContentPositioned<F>
+where
+ F: FnMut(&str, (usize, usize)) -> String,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ fn change(mut self, records: &mut R, _: &mut C, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_cols) {
+ let is_valid_pos = pos.0 < count_rows && pos.1 < count_cols;
+ if !is_valid_pos {
+ continue;
+ }
+
+ let content = records.get_text(pos);
+ let content = (self.0)(content, pos);
+ records.set(pos, content);
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/format/mod.rs b/vendor/tabled/src/settings/format/mod.rs
new file mode 100644
index 000000000..e8221ae32
--- /dev/null
+++ b/vendor/tabled/src/settings/format/mod.rs
@@ -0,0 +1,144 @@
+//! This module contains a list of primitives to help to modify a [`Table`].
+//!
+//! [`Table`]: crate::Table
+
+mod format_config;
+mod format_content;
+mod format_positioned;
+
+pub use format_config::FormatConfig;
+pub use format_content::FormatContent;
+pub use format_positioned::FormatContentPositioned;
+
+/// A formatting function of particular cells on a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Format;
+
+impl Format {
+ /// This function creates a new [`Format`] instance, so
+ /// it can be used as a grid setting.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{Table, settings::{Format, object::Rows, Modify}};
+ ///
+ /// let data = vec![
+ /// (0, "Grodno", true),
+ /// (1, "Minsk", true),
+ /// (2, "Hamburg", false),
+ /// (3, "Brest", true),
+ /// ];
+ ///
+ /// let table = Table::new(&data)
+ /// .with(Modify::new(Rows::new(1..)).with(Format::content(|s| format!(": {} :", s))))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+-------+-------------+-----------+\n\
+ /// | i32 | &str | bool |\n\
+ /// +-------+-------------+-----------+\n\
+ /// | : 0 : | : Grodno : | : true : |\n\
+ /// +-------+-------------+-----------+\n\
+ /// | : 1 : | : Minsk : | : true : |\n\
+ /// +-------+-------------+-----------+\n\
+ /// | : 2 : | : Hamburg : | : false : |\n\
+ /// +-------+-------------+-----------+\n\
+ /// | : 3 : | : Brest : | : true : |\n\
+ /// +-------+-------------+-----------+"
+ /// );
+ /// ```
+ pub fn content<F>(f: F) -> FormatContent<F>
+ where
+ F: FnMut(&str) -> String,
+ {
+ FormatContent::new(f)
+ }
+
+ /// This function creates a new [`FormatContentPositioned`], so
+ /// it can be used as a grid setting.
+ ///
+ /// It's different from [`Format::content`] as it also provides a row and column index.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{Table, settings::{Format, object::Rows, Modify}};
+ ///
+ /// let data = vec![
+ /// (0, "Grodno", true),
+ /// (1, "Minsk", true),
+ /// (2, "Hamburg", false),
+ /// (3, "Brest", true),
+ /// ];
+ ///
+ /// let table = Table::new(&data)
+ /// .with(Modify::new(Rows::single(0)).with(Format::positioned(|_, (_, col)| col.to_string())))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+---+---------+-------+\n\
+ /// | 0 | 1 | 2 |\n\
+ /// +---+---------+-------+\n\
+ /// | 0 | Grodno | true |\n\
+ /// +---+---------+-------+\n\
+ /// | 1 | Minsk | true |\n\
+ /// +---+---------+-------+\n\
+ /// | 2 | Hamburg | false |\n\
+ /// +---+---------+-------+\n\
+ /// | 3 | Brest | true |\n\
+ /// +---+---------+-------+"
+ /// );
+ /// ```
+ pub fn positioned<F>(f: F) -> FormatContentPositioned<F>
+ where
+ F: FnMut(&str, (usize, usize)) -> String,
+ {
+ FormatContentPositioned::new(f)
+ }
+
+ /// This function creates [`FormatConfig`] function to modify a table config.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{
+ /// Table,
+ /// settings::{Format, object::Rows, Modify},
+ /// grid::config::ColoredConfig,
+ /// };
+ ///
+ /// let data = vec![
+ /// (0, "Grodno", true),
+ /// (1, "Minsk", true),
+ /// (2, "Hamburg", false),
+ /// (3, "Brest", true),
+ /// ];
+ ///
+ /// let table = Table::new(&data)
+ /// .with(Format::config(|cfg: &mut ColoredConfig| cfg.set_justification((0,1).into(), '.')))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+-----+---------+-------+\n\
+ /// | i32 | &str... | bool |\n\
+ /// +-----+---------+-------+\n\
+ /// | 0 | Grodno | true |\n\
+ /// +-----+---------+-------+\n\
+ /// | 1 | Minsk | true |\n\
+ /// +-----+---------+-------+\n\
+ /// | 2 | Hamburg | false |\n\
+ /// +-----+---------+-------+\n\
+ /// | 3 | Brest | true |\n\
+ /// +-----+---------+-------+"
+ /// );
+ /// ```
+ pub fn config<F>(f: F) -> FormatConfig<F> {
+ FormatConfig(f)
+ }
+}
diff --git a/vendor/tabled/src/settings/formatting/alignment_strategy.rs b/vendor/tabled/src/settings/formatting/alignment_strategy.rs
new file mode 100644
index 000000000..c95facc5c
--- /dev/null
+++ b/vendor/tabled/src/settings/formatting/alignment_strategy.rs
@@ -0,0 +1,172 @@
+use crate::{
+ grid::config::{ColoredConfig, CompactMultilineConfig, Entity},
+ settings::{CellOption, TableOption},
+};
+
+/// `AlignmentStrategy` is a responsible for a flow how we apply an alignment.
+/// It mostly matters for multiline strings.
+///
+/// # Examples
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{
+/// Style, Modify, Alignment, object::Segment,
+/// formatting::{AlignmentStrategy, TrimStrategy}
+/// }
+/// };
+///
+/// // sample_from: https://opensource.adobe.com/Spry/samples/data_region/JSONDataSetSample.html
+/// let json = r#"
+/// {
+/// "id": "0001",
+/// "type": "donut",
+/// "name": "Cake",
+/// "ppu": 0.55,
+/// "batters": {
+/// "batter": [
+/// { "id": "1001", "type": "Regular" },
+/// { "id": "1002", "type": "Chocolate" },
+/// ]
+/// },
+/// "topping": [
+/// { "id": "5001", "type": "None" },
+/// { "id": "5006", "type": "Chocolate with Sprinkles" },
+/// { "id": "5003", "type": "Chocolate" },
+/// { "id": "5004", "type": "Maple" }
+/// ]
+/// }"#;
+///
+/// let mut table = Table::new(&[json]);
+/// table
+/// .with(Style::modern())
+/// .with(Modify::new(Segment::all()).with(Alignment::right()))
+/// .with(Modify::new(Segment::all()).with(TrimStrategy::None));
+///
+/// println!("{}", table);
+///
+/// assert_eq!(
+/// format!("\n{}", table),
+/// r#"
+/// ┌───────────────────────────────────────────────────────────────┐
+/// │ &str │
+/// ├───────────────────────────────────────────────────────────────┤
+/// │ │
+/// │ { │
+/// │ "id": "0001", │
+/// │ "type": "donut", │
+/// │ "name": "Cake", │
+/// │ "ppu": 0.55, │
+/// │ "batters": { │
+/// │ "batter": [ │
+/// │ { "id": "1001", "type": "Regular" }, │
+/// │ { "id": "1002", "type": "Chocolate" }, │
+/// │ ] │
+/// │ }, │
+/// │ "topping": [ │
+/// │ { "id": "5001", "type": "None" }, │
+/// │ { "id": "5006", "type": "Chocolate with Sprinkles" }, │
+/// │ { "id": "5003", "type": "Chocolate" }, │
+/// │ { "id": "5004", "type": "Maple" } │
+/// │ ] │
+/// │ } │
+/// └───────────────────────────────────────────────────────────────┘"#);
+///
+/// table
+/// .with(Modify::new(Segment::all()).with(AlignmentStrategy::PerCell))
+/// .with(Modify::new(Segment::all()).with(TrimStrategy::Horizontal));
+///
+/// assert_eq!(
+/// format!("\n{}", table),
+/// r#"
+/// ┌───────────────────────────────────────────────────────────────┐
+/// │ &str │
+/// ├───────────────────────────────────────────────────────────────┤
+/// │ │
+/// │ { │
+/// │ "id": "0001", │
+/// │ "type": "donut", │
+/// │ "name": "Cake", │
+/// │ "ppu": 0.55, │
+/// │ "batters": { │
+/// │ "batter": [ │
+/// │ { "id": "1001", "type": "Regular" }, │
+/// │ { "id": "1002", "type": "Chocolate" }, │
+/// │ ] │
+/// │ }, │
+/// │ "topping": [ │
+/// │ { "id": "5001", "type": "None" }, │
+/// │ { "id": "5006", "type": "Chocolate with Sprinkles" }, │
+/// │ { "id": "5003", "type": "Chocolate" }, │
+/// │ { "id": "5004", "type": "Maple" } │
+/// │ ] │
+/// │ } │
+/// └───────────────────────────────────────────────────────────────┘"#);
+///
+/// table.with(Modify::new(Segment::all()).with(AlignmentStrategy::PerLine));
+///
+/// assert_eq!(
+/// format!("\n{}", table),
+/// r#"
+/// ┌───────────────────────────────────────────────────────────────┐
+/// │ &str │
+/// ├───────────────────────────────────────────────────────────────┤
+/// │ │
+/// │ { │
+/// │ "id": "0001", │
+/// │ "type": "donut", │
+/// │ "name": "Cake", │
+/// │ "ppu": 0.55, │
+/// │ "batters": { │
+/// │ "batter": [ │
+/// │ { "id": "1001", "type": "Regular" }, │
+/// │ { "id": "1002", "type": "Chocolate" }, │
+/// │ ] │
+/// │ }, │
+/// │ "topping": [ │
+/// │ { "id": "5001", "type": "None" }, │
+/// │ { "id": "5006", "type": "Chocolate with Sprinkles" }, │
+/// │ { "id": "5003", "type": "Chocolate" }, │
+/// │ { "id": "5004", "type": "Maple" } │
+/// │ ] │
+/// │ } │
+/// └───────────────────────────────────────────────────────────────┘"#);
+/// ```
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum AlignmentStrategy {
+ /// Apply alignment for cell content as a whole.
+ PerCell,
+ /// Apply alignment for each line of a cell content as a whole.
+ PerLine,
+}
+
+impl<R> CellOption<R, ColoredConfig> for AlignmentStrategy {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let mut formatting = *cfg.get_formatting(entity);
+ match &self {
+ AlignmentStrategy::PerCell => formatting.allow_lines_alignment = false,
+ AlignmentStrategy::PerLine => formatting.allow_lines_alignment = true,
+ }
+
+ cfg.set_formatting(entity, formatting);
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for AlignmentStrategy {
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ <Self as CellOption<R, ColoredConfig>>::change(self, records, cfg, Entity::Global)
+ }
+}
+
+impl<R, D> TableOption<R, D, CompactMultilineConfig> for AlignmentStrategy {
+ fn change(self, _: &mut R, cfg: &mut CompactMultilineConfig, _: &mut D) {
+ let mut f = cfg.get_formatting();
+ match &self {
+ AlignmentStrategy::PerCell => f.allow_lines_alignment = false,
+ AlignmentStrategy::PerLine => f.allow_lines_alignment = true,
+ }
+
+ *cfg = cfg.set_formatting(f);
+ }
+}
diff --git a/vendor/tabled/src/settings/formatting/charset.rs b/vendor/tabled/src/settings/formatting/charset.rs
new file mode 100644
index 000000000..c58effcd8
--- /dev/null
+++ b/vendor/tabled/src/settings/formatting/charset.rs
@@ -0,0 +1,108 @@
+use papergrid::{
+ config::Entity,
+ records::{ExactRecords, PeekableRecords},
+};
+
+use crate::{
+ grid::records::{Records, RecordsMut},
+ settings::{CellOption, TableOption},
+};
+
+/// A structure to handle special chars.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Charset;
+
+impl Charset {
+ /// Returns [`CleanCharset`] which removes all `\t` and `\r` occurences.
+ ///
+ /// Notice that tab is just removed rather then being replaced with spaces.
+ /// You might be better call [`TabSize`] first if you not expect such behavior.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{Table, settings::formatting::Charset};
+ ///
+ /// let text = "Some\ttext\t\twith \\tabs";
+ ///
+ /// let mut table = Table::new([text]);
+ /// table.with(Charset::clean());
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+--------------------+\n\
+ /// | &str |\n\
+ /// +--------------------+\n\
+ /// | Sometextwith \\tabs |\n\
+ /// +--------------------+"
+ /// )
+ /// ```
+ ///
+ /// [`TabSize`]: crate::settings::formatting::TabSize
+ pub fn clean() -> CleanCharset {
+ CleanCharset
+ }
+}
+
+/// [`CleanCharset`] removes all `\t` and `\r` occurences.
+///
+/// # Example
+///
+/// ```
+/// use tabled::{Table, settings::formatting::Charset};
+///
+/// let text = "Some text which was created on windows \r\n yes they use this \\r\\n";
+///
+/// let mut builder = Table::builder([text]);
+/// builder.set_header(["win. text"]);
+///
+/// let mut table = builder.build();
+/// table.with(Charset::clean());
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+-----------------------------------------+\n\
+/// | win. text |\n\
+/// +-----------------------------------------+\n\
+/// | Some text which was created on windows |\n\
+/// | yes they use this \\r\\n |\n\
+/// +-----------------------------------------+"
+/// )
+/// ```
+#[derive(Debug, Default, Clone)]
+pub struct CleanCharset;
+
+impl<R, D, C> TableOption<R, D, C> for CleanCharset
+where
+ for<'a> &'a R: Records,
+ R: RecordsMut<String>,
+{
+ fn change(self, records: &mut R, _: &mut C, _: &mut D) {
+ let mut list = vec![];
+ for (row, cells) in records.iter_rows().into_iter().enumerate() {
+ for (col, text) in cells.into_iter().enumerate() {
+ let text = text.as_ref().replace(['\t', '\r'], "");
+ list.push(((row, col), text));
+ }
+ }
+
+ for (pos, text) in list {
+ records.set(pos, text);
+ }
+ }
+}
+
+impl<R, C> CellOption<R, C> for CleanCharset
+where
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, _: &mut C, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+ for pos in entity.iter(count_rows, count_cols) {
+ let text = records.get_text(pos);
+ let text = text.replace(['\t', '\r'], "");
+ records.set(pos, text);
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/formatting/justification.rs b/vendor/tabled/src/settings/formatting/justification.rs
new file mode 100644
index 000000000..a427eb95e
--- /dev/null
+++ b/vendor/tabled/src/settings/formatting/justification.rs
@@ -0,0 +1,127 @@
+use crate::{
+ grid::{
+ color::AnsiColor,
+ config::{ColoredConfig, Entity},
+ },
+ settings::{CellOption, Color, TableOption},
+};
+
+/// Set a justification character and a color.
+///
+/// Default value is `' '` (`<space>`) with no color.
+///
+/// # Examples
+///
+/// Setting a justification character.
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::formatting::Justification,
+/// };
+///
+/// let mut table = Table::new(&[("Hello", ""), ("", "World")]);
+/// table.with(Justification::new('#'));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+-------+-------+\n\
+/// | &str# | &str# |\n\
+/// +-------+-------+\n\
+/// | Hello | ##### |\n\
+/// +-------+-------+\n\
+/// | ##### | World |\n\
+/// +-------+-------+"
+/// );
+/// ```
+///
+/// Setting a justification color.
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{formatting::Justification, Color},
+/// };
+///
+/// let mut table = Table::new(&[("Hello", ""), ("", "World")]);
+/// table.with(Justification::default().color(Color::BG_BRIGHT_RED));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+-------+-------+\n\
+/// | &str\u{1b}[101m \u{1b}[49m | &str\u{1b}[101m \u{1b}[49m |\n\
+/// +-------+-------+\n\
+/// | Hello | \u{1b}[101m \u{1b}[49m |\n\
+/// +-------+-------+\n\
+/// | \u{1b}[101m \u{1b}[49m | World |\n\
+/// +-------+-------+"
+/// );
+/// ```
+///
+/// Use different justification for different columns.
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{Modify, object::Columns, formatting::Justification},
+/// };
+///
+/// let mut table = Table::new(&[("Hello", ""), ("", "World")]);
+/// table.with(Modify::new(Columns::single(0)).with(Justification::new('#')));
+/// table.with(Modify::new(Columns::single(1)).with(Justification::new('@')));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+-------+-------+\n\
+/// | &str# | &str@ |\n\
+/// +-------+-------+\n\
+/// | Hello | @@@@@ |\n\
+/// +-------+-------+\n\
+/// | ##### | World |\n\
+/// +-------+-------+"
+/// );
+/// ```
+///
+#[derive(Debug, Default, Clone)]
+pub struct Justification {
+ c: Option<char>,
+ color: Option<AnsiColor<'static>>,
+}
+
+impl Justification {
+ /// Creates new [`Justification`] object.
+ pub fn new(c: char) -> Self {
+ Self {
+ c: Some(c),
+ color: None,
+ }
+ }
+
+ /// Sets a color for a justification.
+ pub fn color(self, color: Color) -> Self {
+ Self {
+ c: self.c,
+ color: Some(color.into()),
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for Justification {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let c = self.c.unwrap_or(' ');
+ let color = self.color;
+
+ cfg.set_justification(Entity::Global, c);
+ cfg.set_justification_color(Entity::Global, color);
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for Justification {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let c = self.c.unwrap_or(' ');
+ let color = self.color;
+
+ cfg.set_justification(entity, c);
+ cfg.set_justification_color(entity, color);
+ }
+}
diff --git a/vendor/tabled/src/settings/formatting/mod.rs b/vendor/tabled/src/settings/formatting/mod.rs
new file mode 100644
index 000000000..e3a56092f
--- /dev/null
+++ b/vendor/tabled/src/settings/formatting/mod.rs
@@ -0,0 +1,20 @@
+//! This module contains settings for render strategy of papergrid.
+//!
+//! - [`TrimStrategy`] and [`AlignmentStrategy`] allows to set [`Alignment`] settings.
+//! - [`TabSize`] sets a default tab size.
+//! - [`Charset`] responsible for special char treatment.
+//! - [`Justification`] responsible for justification space of content.
+//!
+//! [`Alignment`]: crate::settings::Alignment
+
+mod alignment_strategy;
+mod charset;
+mod justification;
+mod tab_size;
+mod trim_strategy;
+
+pub use alignment_strategy::AlignmentStrategy;
+pub use charset::{Charset, CleanCharset};
+pub use justification::Justification;
+pub use tab_size::TabSize;
+pub use trim_strategy::TrimStrategy;
diff --git a/vendor/tabled/src/settings/formatting/tab_size.rs b/vendor/tabled/src/settings/formatting/tab_size.rs
new file mode 100644
index 000000000..677d0d613
--- /dev/null
+++ b/vendor/tabled/src/settings/formatting/tab_size.rs
@@ -0,0 +1,62 @@
+use crate::{
+ grid::records::{Records, RecordsMut},
+ settings::TableOption,
+};
+
+/// Set a tab size.
+///
+/// The size is used in order to calculate width correctly.
+///
+/// Default value is 4 (basically 1 '\t' equals 4 spaces).
+///
+/// IMPORTANT: The tab character might be not present in output,
+/// it might be replaced by spaces.
+///
+/// # Example
+///
+/// ```
+/// use tabled::{Table, settings::formatting::TabSize};
+///
+/// let text = "Some\ttext\t\twith \\tabs";
+///
+/// let mut table = Table::new([text]);
+/// table.with(TabSize::new(4));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+--------------------------------+\n\
+/// | &str |\n\
+/// +--------------------------------+\n\
+/// | Some text with \\tabs |\n\
+/// +--------------------------------+"
+/// )
+/// ```
+#[derive(Debug, Default, Clone)]
+pub struct TabSize(usize);
+
+impl TabSize {
+ /// Creates new [`TabSize`] object.
+ pub fn new(size: usize) -> Self {
+ Self(size)
+ }
+}
+
+impl<R, D, C> TableOption<R, D, C> for TabSize
+where
+ for<'a> &'a R: Records,
+ R: RecordsMut<String>,
+{
+ fn change(self, records: &mut R, _: &mut C, _: &mut D) {
+ let mut list = vec![];
+ for (row, cells) in records.iter_rows().into_iter().enumerate() {
+ for (col, text) in cells.into_iter().enumerate() {
+ let text = text.as_ref().replace('\t', &" ".repeat(self.0));
+ list.push(((row, col), text));
+ }
+ }
+
+ for (pos, text) in list {
+ records.set(pos, text);
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/formatting/trim_strategy.rs b/vendor/tabled/src/settings/formatting/trim_strategy.rs
new file mode 100644
index 000000000..64ec7e10a
--- /dev/null
+++ b/vendor/tabled/src/settings/formatting/trim_strategy.rs
@@ -0,0 +1,118 @@
+use crate::{
+ grid::config::ColoredConfig,
+ grid::config::Entity,
+ settings::{CellOption, TableOption},
+};
+
+/// `TrimStrategy` determines if it's allowed to use empty space while doing [`Alignment`].
+///
+/// # Examples
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{
+/// Style, Modify, Alignment, object::Segment,
+/// formatting::{TrimStrategy, AlignmentStrategy}
+/// }
+/// };
+///
+/// let mut table = Table::new(&[" Hello World"]);
+/// table
+/// .with(Style::modern())
+/// .with(
+/// Modify::new(Segment::all())
+/// .with(Alignment::left())
+/// .with(TrimStrategy::Horizontal)
+/// );
+///
+/// // Note that nothing was changed exactly.
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "┌────────────────┐\n\
+/// │ &str │\n\
+/// ├────────────────┤\n\
+/// │ Hello World │\n\
+/// └────────────────┘"
+/// );
+///
+/// // To trim lines you would need also set [`AlignmentStrategy`].
+/// table.with(Modify::new(Segment::all()).with(AlignmentStrategy::PerLine));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "┌────────────────┐\n\
+/// │ &str │\n\
+/// ├────────────────┤\n\
+/// │ Hello World │\n\
+/// └────────────────┘"
+/// );
+///
+/// let mut table = Table::new(&[" \n\n\n Hello World"]);
+/// table
+/// .with(Style::modern())
+/// .with(
+/// Modify::new(Segment::all())
+/// .with(Alignment::center())
+/// .with(Alignment::top())
+/// .with(TrimStrategy::Vertical)
+/// );
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "┌─────────────────┐\n\
+/// │ &str │\n\
+/// ├─────────────────┤\n\
+/// │ Hello World │\n\
+/// │ │\n\
+/// │ │\n\
+/// │ │\n\
+/// └─────────────────┘"
+/// );
+/// ```
+///
+/// [`Alignment`]: crate::settings::Alignment
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum TrimStrategy {
+ /// Allow vertical trim.
+ Vertical,
+ /// Allow horizontal trim.
+ Horizontal,
+ /// Allow horizontal and vertical trim.
+ Both,
+ /// Doesn't allow any trim.
+ None,
+}
+
+impl<R> CellOption<R, ColoredConfig> for TrimStrategy {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let mut formatting = *cfg.get_formatting(entity);
+
+ // todo: could be changed to be a struct an enum like consts in `impl` block.
+ match self {
+ TrimStrategy::Vertical => {
+ formatting.vertical_trim = true;
+ }
+ TrimStrategy::Horizontal => {
+ formatting.horizontal_trim = true;
+ }
+ TrimStrategy::Both => {
+ formatting.vertical_trim = true;
+ formatting.horizontal_trim = true;
+ }
+ TrimStrategy::None => {
+ formatting.vertical_trim = false;
+ formatting.horizontal_trim = false;
+ }
+ }
+
+ cfg.set_formatting(entity, formatting);
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for TrimStrategy {
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ <Self as CellOption<_, _>>::change(self, records, cfg, Entity::Global)
+ }
+}
diff --git a/vendor/tabled/src/settings/height/cell_height_increase.rs b/vendor/tabled/src/settings/height/cell_height_increase.rs
new file mode 100644
index 000000000..39887f9f6
--- /dev/null
+++ b/vendor/tabled/src/settings/height/cell_height_increase.rs
@@ -0,0 +1,98 @@
+use crate::{
+ grid::config::ColoredConfig,
+ grid::config::Entity,
+ grid::dimension::CompleteDimensionVecRecords,
+ grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ grid::util::string::count_lines,
+ settings::{measurement::Measurement, peaker::Peaker, CellOption, Height, TableOption},
+};
+
+use super::TableHeightIncrease;
+
+/// A modification for cell/table to increase its height.
+///
+/// If used for a [`Table`] [`PriorityNone`] is used.
+///
+/// [`PriorityNone`]: crate::settings::peaker::PriorityNone
+/// [`Table`]: crate::Table
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct CellHeightIncrease<W = usize> {
+ height: W,
+}
+
+impl<W> CellHeightIncrease<W> {
+ /// Creates a new object of the structure.
+ pub fn new(height: W) -> Self
+ where
+ W: Measurement<Height>,
+ {
+ Self { height }
+ }
+
+ /// The priority makes scence only for table, so the function
+ /// converts it to [`TableHeightIncrease`] with a given priority.
+ pub fn priority<P>(self) -> TableHeightIncrease<W, P>
+ where
+ P: Peaker,
+ W: Measurement<Height>,
+ {
+ TableHeightIncrease::new(self.height).priority::<P>()
+ }
+}
+
+impl<W, R> CellOption<R, ColoredConfig> for CellHeightIncrease<W>
+where
+ W: Measurement<Height>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let height = self.height.measure(&*records, cfg);
+
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_columns) {
+ let is_valid_pos = pos.0 < count_rows && pos.1 < count_columns;
+ if !is_valid_pos {
+ continue;
+ }
+
+ let text = records.get_text(pos);
+
+ let cell_height = count_lines(text);
+ if cell_height >= height {
+ continue;
+ }
+
+ let content = add_lines(text, height - cell_height);
+ records.set(pos, content);
+ }
+ }
+}
+
+impl<R, W> TableOption<R, CompleteDimensionVecRecords<'static>, ColoredConfig>
+ for CellHeightIncrease<W>
+where
+ W: Measurement<Height>,
+ R: Records + ExactRecords + PeekableRecords,
+ for<'a> &'a R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ let height = self.height.measure(&*records, cfg);
+ TableHeightIncrease::new(height).change(records, cfg, dims)
+ }
+}
+
+fn add_lines(s: &str, n: usize) -> String {
+ let mut text = String::with_capacity(s.len() + n);
+ text.push_str(s);
+ text.extend(std::iter::repeat('\n').take(n));
+
+ text
+}
diff --git a/vendor/tabled/src/settings/height/cell_height_limit.rs b/vendor/tabled/src/settings/height/cell_height_limit.rs
new file mode 100644
index 000000000..788f0300c
--- /dev/null
+++ b/vendor/tabled/src/settings/height/cell_height_limit.rs
@@ -0,0 +1,103 @@
+use crate::{
+ grid::{
+ config::{ColoredConfig, Entity},
+ dimension::CompleteDimensionVecRecords,
+ records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ util::string::{count_lines, get_lines},
+ },
+ settings::{measurement::Measurement, peaker::Peaker, CellOption, Height, TableOption},
+};
+
+use super::table_height_limit::TableHeightLimit;
+
+/// A modification for cell/table to increase its height.
+///
+/// If used for a [`Table`] [`PriorityNone`] is used.
+///
+/// [`PriorityNone`]: crate::settings::peaker::PriorityNone
+/// [`Table`]: crate::Table
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct CellHeightLimit<W = usize> {
+ height: W,
+}
+
+impl<W> CellHeightLimit<W> {
+ /// Constructs a new object.
+ pub fn new(height: W) -> Self
+ where
+ W: Measurement<Height>,
+ {
+ Self { height }
+ }
+
+ /// Set's a priority by which the limit logic will be applied.
+ pub fn priority<P>(self) -> TableHeightLimit<W, P>
+ where
+ P: Peaker,
+ W: Measurement<Height>,
+ {
+ TableHeightLimit::new(self.height).priority::<P>()
+ }
+}
+
+impl<W, R> CellOption<R, ColoredConfig> for CellHeightLimit<W>
+where
+ W: Measurement<Height>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let height = self.height.measure(&*records, cfg);
+
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_columns) {
+ let is_valid_pos = pos.0 < count_rows && pos.1 < count_columns;
+ if !is_valid_pos {
+ continue;
+ }
+
+ let text = records.get_text(pos);
+ let count_lines = count_lines(text);
+
+ if count_lines <= height {
+ continue;
+ }
+
+ let content = limit_lines(text, height);
+ records.set(pos, content);
+ }
+ }
+}
+
+impl<R, W> TableOption<R, CompleteDimensionVecRecords<'static>, ColoredConfig>
+ for CellHeightLimit<W>
+where
+ W: Measurement<Height>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ let height = self.height.measure(&*records, cfg);
+ TableHeightLimit::new(height).change(records, cfg, dims)
+ }
+}
+
+fn limit_lines(s: &str, n: usize) -> String {
+ let mut text = String::new();
+ for (i, line) in get_lines(s).take(n).enumerate() {
+ if i > 0 {
+ text.push('\n');
+ }
+
+ text.push_str(&line);
+ }
+
+ text
+}
diff --git a/vendor/tabled/src/settings/height/height_list.rs b/vendor/tabled/src/settings/height/height_list.rs
new file mode 100644
index 000000000..7861dd4d2
--- /dev/null
+++ b/vendor/tabled/src/settings/height/height_list.rs
@@ -0,0 +1,47 @@
+use std::iter::FromIterator;
+
+use crate::{
+ grid::dimension::CompleteDimensionVecRecords,
+ grid::records::{ExactRecords, Records},
+ settings::TableOption,
+};
+
+/// A structure used to set [`Table`] height via a list of rows heights.
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct HeightList {
+ list: Vec<usize>,
+}
+
+impl HeightList {
+ /// Creates a new object.
+ pub fn new(list: Vec<usize>) -> Self {
+ Self { list }
+ }
+}
+
+impl From<Vec<usize>> for HeightList {
+ fn from(list: Vec<usize>) -> Self {
+ Self::new(list)
+ }
+}
+
+impl FromIterator<usize> for HeightList {
+ fn from_iter<T: IntoIterator<Item = usize>>(iter: T) -> Self {
+ Self::new(iter.into_iter().collect())
+ }
+}
+
+impl<R, C> TableOption<R, CompleteDimensionVecRecords<'static>, C> for HeightList
+where
+ R: ExactRecords + Records,
+{
+ fn change(self, records: &mut R, _: &mut C, dims: &mut CompleteDimensionVecRecords<'static>) {
+ if self.list.len() < records.count_rows() {
+ return;
+ }
+
+ let _ = dims.set_heights(self.list);
+ }
+}
diff --git a/vendor/tabled/src/settings/height/mod.rs b/vendor/tabled/src/settings/height/mod.rs
new file mode 100644
index 000000000..ad9caff77
--- /dev/null
+++ b/vendor/tabled/src/settings/height/mod.rs
@@ -0,0 +1,228 @@
+//! The module contains [`Height`] structure which is responsible for a table and cell height.
+
+mod cell_height_increase;
+mod cell_height_limit;
+mod height_list;
+mod table_height_increase;
+mod table_height_limit;
+mod util;
+
+use crate::settings::measurement::Measurement;
+
+pub use cell_height_increase::CellHeightIncrease;
+pub use cell_height_limit::CellHeightLimit;
+pub use height_list::HeightList;
+pub use table_height_increase::TableHeightIncrease;
+pub use table_height_limit::TableHeightLimit;
+
+/// Height is a abstract factory for height settings.
+///
+/// # Example
+///
+/// ```
+/// use tabled::{Table, settings::{Height, Settings}};
+///
+/// let data = vec![
+/// ("Some data", "here", "and here"),
+/// ("Some data on a next", "line", "right here"),
+/// ];
+///
+/// let table = Table::new(data)
+/// .with(Settings::new(Height::limit(10), Height::increase(10)))
+/// .to_string();
+///
+/// assert_eq!(
+/// table,
+/// "+---------------------+------+------------+\n\
+/// | &str | &str | &str |\n\
+/// | | | |\n\
+/// +---------------------+------+------------+\n\
+/// | Some data | here | and here |\n\
+/// | | | |\n\
+/// +---------------------+------+------------+\n\
+/// | Some data on a next | line | right here |\n\
+/// | | | |\n\
+/// +---------------------+------+------------+",
+/// )
+/// ```
+#[derive(Debug)]
+pub struct Height;
+
+impl Height {
+ /// Create [`CellHeightIncrease`] to set a table/cell height.
+ ///
+ /// # Example
+ ///
+ /// ## Cell height
+ ///
+ /// ```
+ /// use tabled::{Table, settings::{Height, Modify, object::Columns}};
+ ///
+ /// let data = vec![
+ /// ("Some data", "here", "and here"),
+ /// ("Some data on a next", "line", "right here"),
+ /// ];
+ ///
+ /// let table = Table::new(data)
+ /// .with(Modify::new(Columns::first()).with(Height::increase(5)))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+---------------------+------+------------+\n\
+ /// | &str | &str | &str |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// +---------------------+------+------------+\n\
+ /// | Some data | here | and here |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// +---------------------+------+------------+\n\
+ /// | Some data on a next | line | right here |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// | | | |\n\
+ /// +---------------------+------+------------+"
+ /// )
+ /// ```
+ ///
+ /// ## Table height
+ ///
+ /// ```
+ /// use tabled::{Table, settings::Height};
+ ///
+ /// let data = vec![
+ /// ("Some data", "here", "and here"),
+ /// ("Some data on a next", "line", "right here"),
+ /// ];
+ ///
+ /// let table = Table::new(data)
+ /// .with(Height::increase(10))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+---------------------+------+------------+\n\
+ /// | &str | &str | &str |\n\
+ /// | | | |\n\
+ /// +---------------------+------+------------+\n\
+ /// | Some data | here | and here |\n\
+ /// | | | |\n\
+ /// +---------------------+------+------------+\n\
+ /// | Some data on a next | line | right here |\n\
+ /// | | | |\n\
+ /// +---------------------+------+------------+",
+ /// )
+ /// ```
+ pub fn increase<W: Measurement<Height>>(width: W) -> CellHeightIncrease<W> {
+ CellHeightIncrease::new(width)
+ }
+
+ /// Create [`CellHeightLimit`] to set a table/cell height.
+ ///
+ /// # Example
+ ///
+ /// ## Cell height
+ ///
+ /// ```
+ /// use tabled::{Table, settings::{Height, Modify, object::Columns}};
+ ///
+ /// let data = vec![
+ /// ("Some\ndata", "here", "and here"),
+ /// ("Some\ndata on a next", "line", "right here"),
+ /// ];
+ ///
+ /// let table = Table::new(data)
+ /// .with(Modify::new(Columns::first()).with(Height::limit(1)))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+------+------+------------+\n\
+ /// | &str | &str | &str |\n\
+ /// +------+------+------------+\n\
+ /// | Some | here | and here |\n\
+ /// +------+------+------------+\n\
+ /// | Some | line | right here |\n\
+ /// +------+------+------------+"
+ /// )
+ /// ```
+ ///
+ /// ## Table height
+ ///
+ /// ```
+ /// use tabled::{Table, settings::Height};
+ ///
+ /// let data = vec![
+ /// ("Some\ndata", "here", "and here"),
+ /// ("Some\ndata on a next", "line", "right here"),
+ /// ];
+ ///
+ /// let table = Table::new(&data)
+ /// .with(Height::limit(6))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+------+------+------------+\n\
+ /// +------+------+------------+\n\
+ /// | Some | here | and here |\n\
+ /// +------+------+------------+\n\
+ /// | Some | line | right here |\n\
+ /// +------+------+------------+",
+ /// );
+ ///
+ /// let table = Table::new(&data)
+ /// .with(Height::limit(1))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+--+--+--+\n\
+ /// +--+--+--+\n\
+ /// +--+--+--+\n\
+ /// +--+--+--+",
+ /// );
+ /// ```
+ pub fn limit<W: Measurement<Height>>(width: W) -> CellHeightLimit<W> {
+ CellHeightLimit::new(width)
+ }
+
+ /// Create [`HeightList`] to set a table height to a constant list of row heights.
+ ///
+ /// Notice if you provide a list with `.len()` less than `Table::count_rows` then it will have no affect.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{Table, settings::{Height, Modify, object::Columns}};
+ ///
+ /// let data = vec![
+ /// ("Some\ndata", "here", "and here"),
+ /// ("Some\ndata on a next", "line", "right here"),
+ /// ];
+ ///
+ /// let table = Table::new(data)
+ /// .with(Height::list([1, 0, 2]))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+----------------+------+------------+\n\
+ /// | &str | &str | &str |\n\
+ /// +----------------+------+------------+\n\
+ /// +----------------+------+------------+\n\
+ /// | Some | line | right here |\n\
+ /// | data on a next | | |\n\
+ /// +----------------+------+------------+",
+ /// )
+ /// ```
+ pub fn list<I: IntoIterator<Item = usize>>(rows: I) -> HeightList {
+ HeightList::new(rows.into_iter().collect())
+ }
+}
diff --git a/vendor/tabled/src/settings/height/table_height_increase.rs b/vendor/tabled/src/settings/height/table_height_increase.rs
new file mode 100644
index 000000000..f727bf041
--- /dev/null
+++ b/vendor/tabled/src/settings/height/table_height_increase.rs
@@ -0,0 +1,90 @@
+use crate::{
+ grid::{
+ config::ColoredConfig,
+ dimension::CompleteDimensionVecRecords,
+ records::{ExactRecords, PeekableRecords, Records},
+ },
+ settings::{
+ measurement::Measurement,
+ peaker::{Peaker, PriorityNone},
+ Height, TableOption,
+ },
+};
+
+use super::util::get_table_height;
+
+/// A modification of a table to increase the table height.
+#[derive(Debug, Clone)]
+pub struct TableHeightIncrease<W = usize, P = PriorityNone> {
+ height: W,
+ priority: P,
+}
+
+impl<W> TableHeightIncrease<W, PriorityNone> {
+ /// Creates a new object.
+ pub fn new(height: W) -> Self
+ where
+ W: Measurement<Height>,
+ {
+ Self {
+ height,
+ priority: PriorityNone::default(),
+ }
+ }
+
+ /// Sets a different priority logic.
+ pub fn priority<P>(self) -> TableHeightIncrease<W, P>
+ where
+ P: Peaker,
+ {
+ TableHeightIncrease {
+ priority: P::create(),
+ height: self.height,
+ }
+ }
+}
+
+impl<R, W, P> TableOption<R, CompleteDimensionVecRecords<'static>, ColoredConfig>
+ for TableHeightIncrease<W, P>
+where
+ W: Measurement<Height>,
+ P: Peaker + Clone,
+ R: Records + ExactRecords + PeekableRecords,
+ for<'a> &'a R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return;
+ }
+
+ let height = self.height.measure(&*records, cfg);
+ let (total, mut heights) = get_table_height(&*records, cfg);
+ if total >= height {
+ return;
+ }
+
+ get_increase_list(&mut heights, height, total, self.priority);
+
+ let _ = dims.set_heights(heights);
+ }
+}
+
+fn get_increase_list<P>(list: &mut [usize], total: usize, mut current: usize, mut peaker: P)
+where
+ P: Peaker,
+{
+ while current != total {
+ let col = match peaker.peak(&[], list) {
+ Some(col) => col,
+ None => break,
+ };
+
+ list[col] += 1;
+ current += 1;
+ }
+}
diff --git a/vendor/tabled/src/settings/height/table_height_limit.rs b/vendor/tabled/src/settings/height/table_height_limit.rs
new file mode 100644
index 000000000..2a3d19bb7
--- /dev/null
+++ b/vendor/tabled/src/settings/height/table_height_limit.rs
@@ -0,0 +1,123 @@
+use crate::{
+ grid::{
+ config::ColoredConfig,
+ dimension::CompleteDimensionVecRecords,
+ records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ util::string::{count_lines, get_lines},
+ },
+ settings::{
+ measurement::Measurement,
+ peaker::{Peaker, PriorityNone},
+ Height, TableOption,
+ },
+};
+
+use super::util::get_table_height;
+
+/// A modification of a table to decrease the table height.
+#[derive(Debug)]
+pub struct TableHeightLimit<W = usize, P = PriorityNone> {
+ height: W,
+ priority: P,
+}
+
+impl<W> TableHeightLimit<W, PriorityNone> {
+ /// Creates a new object.
+ pub fn new(height: W) -> Self
+ where
+ W: Measurement<Height>,
+ {
+ Self {
+ height,
+ priority: PriorityNone::default(),
+ }
+ }
+
+ /// Sets a different priority logic.
+ pub fn priority<P>(self) -> TableHeightLimit<W, P>
+ where
+ P: Peaker,
+ {
+ TableHeightLimit {
+ priority: P::create(),
+ height: self.height,
+ }
+ }
+}
+
+impl<R, W, P> TableOption<R, CompleteDimensionVecRecords<'static>, ColoredConfig>
+ for TableHeightLimit<W, P>
+where
+ W: Measurement<Height>,
+ P: Peaker + Clone,
+ R: ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ let count_rows = records.count_rows();
+ let count_cols = (&*records).count_columns();
+
+ if count_rows == 0 || count_cols == 0 {
+ return;
+ }
+
+ let height = self.height.measure(&*records, cfg);
+ let (total, mut heights) = get_table_height(&*records, cfg);
+ if total <= height {
+ return;
+ }
+
+ decrease_list(&mut heights, total, height, self.priority);
+
+ for (row, &height) in heights.iter().enumerate() {
+ for col in 0..count_cols {
+ let text = records.get_text((row, col));
+ let count_lines = count_lines(text);
+
+ if count_lines <= height {
+ continue;
+ }
+
+ let text = limit_lines(text, height);
+
+ records.set((row, col), text);
+ }
+ }
+
+ let _ = dims.set_heights(heights);
+ }
+}
+
+fn decrease_list<P>(list: &mut [usize], total: usize, mut value: usize, mut peaker: P)
+where
+ P: Peaker,
+{
+ while value != total {
+ let p = peaker.peak(&[], list);
+ let row = match p {
+ Some(row) => row,
+ None => break,
+ };
+
+ list[row] -= 1;
+ value += 1;
+ }
+}
+
+fn limit_lines(s: &str, n: usize) -> String {
+ let mut text = String::new();
+ for (i, line) in get_lines(s).take(n).enumerate() {
+ if i > 0 {
+ text.push('\n');
+ }
+
+ text.push_str(&line);
+ }
+
+ text
+}
diff --git a/vendor/tabled/src/settings/height/util.rs b/vendor/tabled/src/settings/height/util.rs
new file mode 100644
index 000000000..c6917d925
--- /dev/null
+++ b/vendor/tabled/src/settings/height/util.rs
@@ -0,0 +1,22 @@
+use crate::grid::{
+ config::SpannedConfig,
+ dimension::SpannedGridDimension,
+ records::{ExactRecords, Records},
+};
+
+pub(crate) fn get_table_height<R: Records + ExactRecords>(
+ records: R,
+ cfg: &SpannedConfig,
+) -> (usize, Vec<usize>) {
+ let count_horizontals = cfg.count_horizontal(records.count_rows());
+
+ let margin = cfg.get_margin();
+ let margin_size = margin.top.size + margin.bottom.size;
+
+ let list = SpannedGridDimension::height(records, cfg);
+ let total = list.iter().sum::<usize>();
+
+ let total = total + count_horizontals + margin_size;
+
+ (total, list)
+}
diff --git a/vendor/tabled/src/settings/highlight/mod.rs b/vendor/tabled/src/settings/highlight/mod.rs
new file mode 100644
index 000000000..57df867b3
--- /dev/null
+++ b/vendor/tabled/src/settings/highlight/mod.rs
@@ -0,0 +1,452 @@
+//! This module contains a [`Highlight`] primitive, which helps
+//! changing a [`Border`] style of any segment on a [`Table`].
+//!
+//! [`Table`]: crate::Table
+
+use std::collections::HashSet;
+
+use crate::{
+ grid::{
+ config::{Border as GridBorder, ColoredConfig, Entity, Position, SpannedConfig},
+ records::{ExactRecords, Records},
+ },
+ settings::{object::Object, style::BorderColor, Border, TableOption},
+};
+
+/// Highlight modifies a table style by changing a border of a target [`Table`] segment.
+///
+/// # Example
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{Highlight, Border, Style, object::Segment}
+/// };
+///
+/// let data = [
+/// ("ELF", "Extensible Linking Format", true),
+/// ("DWARF", "", true),
+/// ("PE", "Portable Executable", false),
+/// ];
+///
+/// let table = Table::new(data.iter().enumerate())
+/// .with(Style::markdown())
+/// .with(Highlight::new(Segment::all(), Border::default().top('^').bottom('v')))
+/// .to_string();
+///
+/// assert_eq!(
+/// table,
+/// concat!(
+/// " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ \n",
+/// "| usize | &str | &str | bool |\n",
+/// "|-------|-------|---------------------------|-------|\n",
+/// "| 0 | ELF | Extensible Linking Format | true |\n",
+/// "| 1 | DWARF | | true |\n",
+/// "| 2 | PE | Portable Executable | false |\n",
+/// " vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ",
+/// ),
+/// );
+/// ```
+///
+/// It's possible to use [`Highlight`] for many kinds of figures.
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{
+/// Highlight, Border, Style,
+/// object::{Segment, Object}
+/// }
+/// };
+///
+/// let data = [
+/// ("ELF", "Extensible Linking Format", true),
+/// ("DWARF", "", true),
+/// ("PE", "Portable Executable", false),
+/// ];
+///
+/// let table = Table::new(data.iter().enumerate())
+/// .with(Style::markdown())
+/// .with(Highlight::new(Segment::all().not((0,0).and((1, 0)).and((0, 1)).and((0, 3))), Border::filled('*')))
+/// .to_string();
+///
+/// println!("{}", table);
+///
+/// assert_eq!(
+/// table,
+/// concat!(
+/// " ***************************** \n",
+/// "| usize | &str * &str * bool |\n",
+/// "|-------*********---------------------------*********\n",
+/// "| 0 * ELF | Extensible Linking Format | true *\n",
+/// "********* *\n",
+/// "* 1 | DWARF | | true *\n",
+/// "* *\n",
+/// "* 2 | PE | Portable Executable | false *\n",
+/// "*****************************************************",
+/// ),
+/// );
+/// ```
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Highlight<O> {
+ target: O,
+ border: Border,
+}
+
+// todo: Add BorderColor.
+
+impl<O> Highlight<O> {
+ /// Build a new instance of [`Highlight`]
+ ///
+ /// BE AWARE: if target exceeds boundaries it may panic.
+ pub fn new(target: O, border: Border) -> Self {
+ Self { target, border }
+ }
+}
+
+impl<O> Highlight<O> {
+ /// Build a new instance of [`HighlightColored`]
+ pub fn colored(target: O, border: BorderColor) -> HighlightColored<O> {
+ HighlightColored { target, border }
+ }
+}
+
+impl<O, R, D> TableOption<R, D, ColoredConfig> for Highlight<O>
+where
+ O: Object<R>,
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ let cells = self.target.cells(records);
+ let segments = split_segments(cells, count_rows, count_cols);
+
+ for sector in segments {
+ set_border(cfg, &sector, self.border);
+ }
+ }
+}
+
+/// A [`Highlight`] object which works with a [`BorderColored`]
+///
+/// [`BorderColored`]: crate::settings::style::BorderColor
+#[derive(Debug)]
+pub struct HighlightColored<O> {
+ target: O,
+ border: BorderColor,
+}
+
+impl<O, R, D> TableOption<R, D, ColoredConfig> for HighlightColored<O>
+where
+ O: Object<R>,
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ let cells = self.target.cells(records);
+ let segments = split_segments(cells, count_rows, count_cols);
+
+ for sector in segments {
+ set_border_color(cfg, sector, &self.border);
+ }
+ }
+}
+
+fn set_border_color(
+ cfg: &mut SpannedConfig,
+ sector: HashSet<(usize, usize)>,
+ border: &BorderColor,
+) {
+ if sector.is_empty() {
+ return;
+ }
+ let color = border.clone().into();
+ for &(row, col) in &sector {
+ let border = build_cell_border(&sector, (row, col), &color);
+ cfg.set_border_color((row, col), border);
+ }
+}
+
+fn split_segments(
+ cells: impl Iterator<Item = Entity>,
+ count_rows: usize,
+ count_cols: usize,
+) -> Vec<HashSet<(usize, usize)>> {
+ let mut segments: Vec<HashSet<(usize, usize)>> = Vec::new();
+ for entity in cells {
+ for cell in entity.iter(count_rows, count_cols) {
+ let found_segment = segments
+ .iter_mut()
+ .find(|s| s.iter().any(|&c| is_cell_connected(cell, c)));
+
+ match found_segment {
+ Some(segment) => {
+ let _ = segment.insert(cell);
+ }
+ None => {
+ let mut segment = HashSet::new();
+ let _ = segment.insert(cell);
+ segments.push(segment);
+ }
+ }
+ }
+ }
+
+ let mut squashed_segments: Vec<HashSet<(usize, usize)>> = Vec::new();
+ while !segments.is_empty() {
+ let mut segment = segments.remove(0);
+
+ let mut i = 0;
+ while i < segments.len() {
+ if is_segment_connected(&segment, &segments[i]) {
+ segment.extend(&segments[i]);
+ let _ = segments.remove(i);
+ } else {
+ i += 1;
+ }
+ }
+
+ squashed_segments.push(segment);
+ }
+
+ squashed_segments
+}
+
+fn is_cell_connected((row1, col1): (usize, usize), (row2, col2): (usize, usize)) -> bool {
+ if col1 == col2 && row1 == row2 + 1 {
+ return true;
+ }
+
+ if col1 == col2 && (row2 > 0 && row1 == row2 - 1) {
+ return true;
+ }
+
+ if row1 == row2 && col1 == col2 + 1 {
+ return true;
+ }
+
+ if row1 == row2 && (col2 > 0 && col1 == col2 - 1) {
+ return true;
+ }
+
+ false
+}
+
+fn is_segment_connected(
+ segment1: &HashSet<(usize, usize)>,
+ segment2: &HashSet<(usize, usize)>,
+) -> bool {
+ for &cell1 in segment1.iter() {
+ for &cell2 in segment2.iter() {
+ if is_cell_connected(cell1, cell2) {
+ return true;
+ }
+ }
+ }
+
+ false
+}
+
+fn set_border(cfg: &mut SpannedConfig, sector: &HashSet<(usize, usize)>, border: Border) {
+ if sector.is_empty() {
+ return;
+ }
+
+ let border = border.into();
+ for &pos in sector {
+ let border = build_cell_border(sector, pos, &border);
+ cfg.set_border(pos, border);
+ }
+}
+
+fn build_cell_border<T>(
+ sector: &HashSet<(usize, usize)>,
+ (row, col): Position,
+ border: &GridBorder<T>,
+) -> GridBorder<T>
+where
+ T: Default + Clone,
+{
+ let cell_has_top_neighbor = cell_has_top_neighbor(sector, row, col);
+ let cell_has_bottom_neighbor = cell_has_bottom_neighbor(sector, row, col);
+ let cell_has_left_neighbor = cell_has_left_neighbor(sector, row, col);
+ let cell_has_right_neighbor = cell_has_right_neighbor(sector, row, col);
+
+ let this_has_left_top_neighbor = is_there_left_top_cell(sector, row, col);
+ let this_has_right_top_neighbor = is_there_right_top_cell(sector, row, col);
+ let this_has_left_bottom_neighbor = is_there_left_bottom_cell(sector, row, col);
+ let this_has_right_bottom_neighbor = is_there_right_bottom_cell(sector, row, col);
+
+ let mut cell_border = GridBorder::default();
+ if let Some(c) = border.top.clone() {
+ if !cell_has_top_neighbor {
+ cell_border.top = Some(c.clone());
+
+ if cell_has_right_neighbor && !this_has_right_top_neighbor {
+ cell_border.right_top_corner = Some(c);
+ }
+ }
+ }
+ if let Some(c) = border.bottom.clone() {
+ if !cell_has_bottom_neighbor {
+ cell_border.bottom = Some(c.clone());
+
+ if cell_has_right_neighbor && !this_has_right_bottom_neighbor {
+ cell_border.right_bottom_corner = Some(c);
+ }
+ }
+ }
+ if let Some(c) = border.left.clone() {
+ if !cell_has_left_neighbor {
+ cell_border.left = Some(c.clone());
+
+ if cell_has_bottom_neighbor && !this_has_left_bottom_neighbor {
+ cell_border.left_bottom_corner = Some(c);
+ }
+ }
+ }
+ if let Some(c) = border.right.clone() {
+ if !cell_has_right_neighbor {
+ cell_border.right = Some(c.clone());
+
+ if cell_has_bottom_neighbor && !this_has_right_bottom_neighbor {
+ cell_border.right_bottom_corner = Some(c);
+ }
+ }
+ }
+ if let Some(c) = border.left_top_corner.clone() {
+ if !cell_has_left_neighbor && !cell_has_top_neighbor {
+ cell_border.left_top_corner = Some(c);
+ }
+ }
+ if let Some(c) = border.left_bottom_corner.clone() {
+ if !cell_has_left_neighbor && !cell_has_bottom_neighbor {
+ cell_border.left_bottom_corner = Some(c);
+ }
+ }
+ if let Some(c) = border.right_top_corner.clone() {
+ if !cell_has_right_neighbor && !cell_has_top_neighbor {
+ cell_border.right_top_corner = Some(c);
+ }
+ }
+ if let Some(c) = border.right_bottom_corner.clone() {
+ if !cell_has_right_neighbor && !cell_has_bottom_neighbor {
+ cell_border.right_bottom_corner = Some(c);
+ }
+ }
+ {
+ if !cell_has_bottom_neighbor {
+ if !cell_has_left_neighbor && this_has_left_top_neighbor {
+ if let Some(c) = border.right_top_corner.clone() {
+ cell_border.left_top_corner = Some(c);
+ }
+ }
+
+ if cell_has_left_neighbor && this_has_left_bottom_neighbor {
+ if let Some(c) = border.left_top_corner.clone() {
+ cell_border.left_bottom_corner = Some(c);
+ }
+ }
+
+ if !cell_has_right_neighbor && this_has_right_top_neighbor {
+ if let Some(c) = border.left_top_corner.clone() {
+ cell_border.right_top_corner = Some(c);
+ }
+ }
+
+ if cell_has_right_neighbor && this_has_right_bottom_neighbor {
+ if let Some(c) = border.right_top_corner.clone() {
+ cell_border.right_bottom_corner = Some(c);
+ }
+ }
+ }
+
+ if !cell_has_top_neighbor {
+ if !cell_has_left_neighbor && this_has_left_bottom_neighbor {
+ if let Some(c) = border.right_bottom_corner.clone() {
+ cell_border.left_bottom_corner = Some(c);
+ }
+ }
+
+ if cell_has_left_neighbor && this_has_left_top_neighbor {
+ if let Some(c) = border.left_bottom_corner.clone() {
+ cell_border.left_top_corner = Some(c);
+ }
+ }
+
+ if !cell_has_right_neighbor && this_has_right_bottom_neighbor {
+ if let Some(c) = border.left_bottom_corner.clone() {
+ cell_border.right_bottom_corner = Some(c);
+ }
+ }
+
+ if cell_has_right_neighbor && this_has_right_top_neighbor {
+ if let Some(c) = border.right_bottom_corner.clone() {
+ cell_border.right_top_corner = Some(c);
+ }
+ }
+ }
+ }
+
+ cell_border
+}
+
+fn cell_has_top_neighbor(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ row > 0 && sector.contains(&(row - 1, col))
+}
+
+fn cell_has_bottom_neighbor(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ sector.contains(&(row + 1, col))
+}
+
+fn cell_has_left_neighbor(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ col > 0 && sector.contains(&(row, col - 1))
+}
+
+fn cell_has_right_neighbor(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ sector.contains(&(row, col + 1))
+}
+
+fn is_there_left_top_cell(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ row > 0 && col > 0 && sector.contains(&(row - 1, col - 1))
+}
+
+fn is_there_right_top_cell(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ row > 0 && sector.contains(&(row - 1, col + 1))
+}
+
+fn is_there_left_bottom_cell(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ col > 0 && sector.contains(&(row + 1, col - 1))
+}
+
+fn is_there_right_bottom_cell(sector: &HashSet<(usize, usize)>, row: usize, col: usize) -> bool {
+ sector.contains(&(row + 1, col + 1))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_is_connected() {
+ assert!(is_cell_connected((0, 0), (0, 1)));
+ assert!(is_cell_connected((0, 0), (1, 0)));
+ assert!(!is_cell_connected((0, 0), (1, 1)));
+
+ assert!(is_cell_connected((0, 1), (0, 0)));
+ assert!(is_cell_connected((1, 0), (0, 0)));
+ assert!(!is_cell_connected((1, 1), (0, 0)));
+
+ assert!(is_cell_connected((1, 1), (0, 1)));
+ assert!(is_cell_connected((1, 1), (1, 0)));
+ assert!(is_cell_connected((1, 1), (2, 1)));
+ assert!(is_cell_connected((1, 1), (1, 2)));
+ assert!(!is_cell_connected((1, 1), (1, 1)));
+ }
+}
diff --git a/vendor/tabled/src/settings/locator/mod.rs b/vendor/tabled/src/settings/locator/mod.rs
new file mode 100644
index 000000000..b48391bb7
--- /dev/null
+++ b/vendor/tabled/src/settings/locator/mod.rs
@@ -0,0 +1,202 @@
+//! The module contains a [`Locator`] trait and implementations for it.
+
+use core::ops::Bound;
+use std::{
+ iter::{self, Once},
+ ops::{Range, RangeBounds},
+};
+
+use crate::{
+ grid::config::Entity,
+ grid::records::{ExactRecords, PeekableRecords, Records},
+ settings::object::{
+ Column, Columns, FirstColumn, FirstRow, LastColumn, LastRow, Object, Row, Rows,
+ },
+};
+
+/// Locator is an interface which searches for a particular thing in the [`Records`],
+/// and returns coordinate of the foundings if any.
+pub trait Locator<Records> {
+ /// A coordinate of the finding.
+ type Coordinate;
+ /// An iterator of the coordinates.
+ /// If it's empty it's considered that nothing is found.
+ type IntoIter: IntoIterator<Item = Self::Coordinate>;
+
+ /// Search for the thing in [`Records`], returning a list of coordinates.
+ fn locate(&mut self, records: Records) -> Self::IntoIter;
+}
+
+impl<B, R> Locator<R> for Columns<B>
+where
+ B: RangeBounds<usize>,
+ R: Records,
+{
+ type Coordinate = usize;
+ type IntoIter = Range<usize>;
+
+ fn locate(&mut self, records: R) -> Self::IntoIter {
+ let range = self.get_range();
+ let max = records.count_columns();
+ let (from, to) = bounds_to_usize(range.start_bound(), range.end_bound(), max);
+
+ from..to
+ }
+}
+
+impl<R> Locator<R> for Column {
+ type Coordinate = usize;
+ type IntoIter = Once<usize>;
+
+ fn locate(&mut self, _: R) -> Self::IntoIter {
+ iter::once((*self).into())
+ }
+}
+
+impl<R> Locator<R> for FirstColumn {
+ type Coordinate = usize;
+ type IntoIter = Once<usize>;
+
+ fn locate(&mut self, _: R) -> Self::IntoIter {
+ iter::once(0)
+ }
+}
+
+impl<R> Locator<R> for LastColumn
+where
+ R: Records,
+{
+ type Coordinate = usize;
+ type IntoIter = Once<usize>;
+
+ fn locate(&mut self, records: R) -> Self::IntoIter {
+ if records.count_columns() > 0 {
+ iter::once(records.count_columns() - 1)
+ } else {
+ iter::once(0)
+ }
+ }
+}
+
+impl<B, R> Locator<R> for Rows<B>
+where
+ R: Records,
+ B: RangeBounds<usize>,
+{
+ type Coordinate = usize;
+ type IntoIter = Range<usize>;
+
+ fn locate(&mut self, records: R) -> Self::IntoIter {
+ let (from, to) = bounds_to_usize(
+ self.get_range().start_bound(),
+ self.get_range().end_bound(),
+ records.count_columns(),
+ );
+
+ from..to
+ }
+}
+
+impl<R> Locator<R> for Row {
+ type Coordinate = usize;
+ type IntoIter = Once<usize>;
+
+ fn locate(&mut self, _: R) -> Self::IntoIter {
+ iter::once((*self).into())
+ }
+}
+
+impl<R> Locator<R> for FirstRow {
+ type Coordinate = usize;
+ type IntoIter = Once<usize>;
+
+ fn locate(&mut self, _: R) -> Self::IntoIter {
+ iter::once(0)
+ }
+}
+
+impl<R> Locator<R> for LastRow
+where
+ R: ExactRecords,
+{
+ type Coordinate = usize;
+ type IntoIter = Once<usize>;
+
+ fn locate(&mut self, records: R) -> Self::IntoIter {
+ if records.count_rows() > 0 {
+ iter::once(records.count_rows() - 1)
+ } else {
+ iter::once(0)
+ }
+ }
+}
+
+/// The structure is an implementation of [`Locator`] to search for a column by it's name.
+/// A name is considered be a value in a first row.
+///
+/// So even if in reality there's no header, the first row will be considered to be one.
+#[derive(Debug, Clone, Copy)]
+pub struct ByColumnName<S>(S);
+
+impl<S> ByColumnName<S> {
+ /// Constructs a new object of the structure.
+ pub fn new(text: S) -> Self
+ where
+ S: AsRef<str>,
+ {
+ Self(text)
+ }
+}
+
+impl<R, S> Locator<R> for ByColumnName<S>
+where
+ S: AsRef<str>,
+ R: Records + ExactRecords + PeekableRecords,
+{
+ type Coordinate = usize;
+ type IntoIter = Vec<usize>;
+
+ fn locate(&mut self, records: R) -> Self::IntoIter {
+ // todo: can be optimized by creating Iterator
+ (0..records.count_columns())
+ .filter(|col| records.get_text((0, *col)) == self.0.as_ref())
+ .collect::<Vec<_>>()
+ }
+}
+
+impl<S, R> Object<R> for ByColumnName<S>
+where
+ S: AsRef<str>,
+ R: Records + PeekableRecords + ExactRecords,
+{
+ type Iter = std::vec::IntoIter<Entity>;
+
+ fn cells(&self, records: &R) -> Self::Iter {
+ // todo: can be optimized by creating Iterator
+ (0..records.count_columns())
+ .filter(|col| records.get_text((0, *col)) == self.0.as_ref())
+ .map(Entity::Column)
+ .collect::<Vec<_>>()
+ .into_iter()
+ }
+}
+
+fn bounds_to_usize(
+ left: Bound<&usize>,
+ right: Bound<&usize>,
+ count_elements: usize,
+) -> (usize, usize) {
+ match (left, right) {
+ (Bound::Included(x), Bound::Included(y)) => (*x, y + 1),
+ (Bound::Included(x), Bound::Excluded(y)) => (*x, *y),
+ (Bound::Included(x), Bound::Unbounded) => (*x, count_elements),
+ (Bound::Unbounded, Bound::Unbounded) => (0, count_elements),
+ (Bound::Unbounded, Bound::Included(y)) => (0, y + 1),
+ (Bound::Unbounded, Bound::Excluded(y)) => (0, *y),
+ (Bound::Excluded(_), Bound::Unbounded)
+ | (Bound::Excluded(_), Bound::Included(_))
+ | (Bound::Excluded(_), Bound::Excluded(_)) => {
+ unreachable!("A start bound can't be excluded")
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/margin/mod.rs b/vendor/tabled/src/settings/margin/mod.rs
new file mode 100644
index 000000000..b86a1d3e2
--- /dev/null
+++ b/vendor/tabled/src/settings/margin/mod.rs
@@ -0,0 +1,137 @@
+//! This module contains a Margin settings of a [`Table`].
+//!
+//! # Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{settings::{Margin, Style}, Table};
+//!
+//! let data = vec!["Hello", "World", "!"];
+//!
+//! let mut table = Table::new(data);
+//! table.with(Style::markdown()).with(Margin::new(3, 3, 1, 0));
+//!
+//! assert_eq!(
+//! table.to_string(),
+//! concat!(
+//! " \n",
+//! " | &str | \n",
+//! " |-------| \n",
+//! " | Hello | \n",
+//! " | World | \n",
+//! " | ! | ",
+//! )
+//! );
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use crate::{
+ grid::{
+ color::StaticColor,
+ config::{CompactConfig, CompactMultilineConfig},
+ config::{Indent, Sides},
+ },
+ settings::TableOption,
+};
+
+#[cfg(feature = "std")]
+use crate::grid::{color::AnsiColor, config::ColoredConfig};
+
+/// Margin is responsible for a left/right/top/bottom outer indent of a grid.
+///
+#[cfg_attr(feature = "std", doc = "```")]
+#[cfg_attr(not(feature = "std"), doc = "```ignore")]
+/// # use tabled::{settings::Margin, Table};
+/// # let data: Vec<&'static str> = Vec::new();
+/// let table = Table::new(&data)
+/// .with(Margin::new(1, 1, 1, 1).fill('>', '<', 'V', '^'));
+/// ```
+#[derive(Debug, Clone)]
+pub struct Margin<C = StaticColor> {
+ indent: Sides<Indent>,
+ colors: Option<Sides<C>>,
+}
+
+impl Margin {
+ /// Construct's an Margin object.
+ ///
+ /// It uses space(' ') as a default fill character.
+ /// To set a custom character you can use [`Margin::fill`] function.
+ pub const fn new(left: usize, right: usize, top: usize, bottom: usize) -> Self {
+ Self {
+ indent: Sides::new(
+ Indent::spaced(left),
+ Indent::spaced(right),
+ Indent::spaced(top),
+ Indent::spaced(bottom),
+ ),
+ colors: None,
+ }
+ }
+}
+
+impl<Color> Margin<Color> {
+ /// The function, sets a characters for the margin on an each side.
+ pub const fn fill(mut self, left: char, right: char, top: char, bottom: char) -> Self {
+ self.indent.left.fill = left;
+ self.indent.right.fill = right;
+ self.indent.top.fill = top;
+ self.indent.bottom.fill = bottom;
+ self
+ }
+
+ /// The function, sets a characters for the margin on an each side.
+ pub fn colorize<C>(self, left: C, right: C, top: C, bottom: C) -> Margin<C> {
+ Margin {
+ indent: self.indent,
+ colors: Some(Sides::new(left, right, top, bottom)),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, D, C> TableOption<R, D, ColoredConfig> for Margin<C>
+where
+ C: Into<AnsiColor<'static>> + Clone,
+{
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let indent = self.indent;
+ let margin = Sides::new(indent.left, indent.right, indent.top, indent.bottom);
+ cfg.set_margin(margin);
+
+ if let Some(colors) = &self.colors {
+ let margin = Sides::new(
+ Some(colors.left.clone().into()),
+ Some(colors.right.clone().into()),
+ Some(colors.top.clone().into()),
+ Some(colors.bottom.clone().into()),
+ );
+ cfg.set_margin_color(margin);
+ }
+ }
+}
+
+impl<R, D, C> TableOption<R, D, CompactConfig> for Margin<C>
+where
+ C: Into<StaticColor> + Clone,
+{
+ fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
+ *cfg = cfg.set_margin(self.indent);
+
+ if let Some(c) = self.colors {
+ // todo: make a new method (BECAUSE INTO doesn't work) try_into();
+ let colors = Sides::new(c.left.into(), c.right.into(), c.top.into(), c.bottom.into());
+ *cfg = cfg.set_margin_color(colors);
+ }
+ }
+}
+
+impl<R, D, C> TableOption<R, D, CompactMultilineConfig> for Margin<C>
+where
+ C: Into<StaticColor> + Clone,
+{
+ fn change(self, records: &mut R, cfg: &mut CompactMultilineConfig, dimension: &mut D) {
+ self.change(records, cfg.as_mut(), dimension)
+ }
+}
diff --git a/vendor/tabled/src/settings/measurement/mod.rs b/vendor/tabled/src/settings/measurement/mod.rs
new file mode 100644
index 000000000..f93cc6b4e
--- /dev/null
+++ b/vendor/tabled/src/settings/measurement/mod.rs
@@ -0,0 +1,161 @@
+//! The module contains [`Measurement`] trait and its implementations to be used in [`Height`] and [`Width`].;
+
+use crate::{
+ grid::config::SpannedConfig,
+ grid::dimension::SpannedGridDimension,
+ grid::records::{ExactRecords, PeekableRecords, Records},
+ grid::util::string::{self, string_width_multiline},
+ settings::{Height, Width},
+};
+
+/// A width value which can be obtained on behalf of [`Table`].
+///
+/// [`Table`]: crate::Table
+pub trait Measurement<Attribute> {
+ /// Returns a measurement value.
+ fn measure<R: Records + ExactRecords + PeekableRecords>(
+ &self,
+ records: R,
+ cfg: &SpannedConfig,
+ ) -> usize;
+}
+
+impl<T> Measurement<T> for usize {
+ fn measure<R>(&self, _: R, _: &SpannedConfig) -> usize {
+ *self
+ }
+}
+
+/// Max width value.
+#[derive(Debug)]
+pub struct Max;
+
+impl Measurement<Width> for Max {
+ fn measure<R: Records + ExactRecords + PeekableRecords>(
+ &self,
+ records: R,
+ _: &SpannedConfig,
+ ) -> usize {
+ grid_widths(&records)
+ .map(|r| r.max().unwrap_or(0))
+ .max()
+ .unwrap_or(0)
+ }
+}
+
+impl Measurement<Height> for Max {
+ fn measure<R: Records + ExactRecords + PeekableRecords>(
+ &self,
+ records: R,
+ _: &SpannedConfig,
+ ) -> usize {
+ records_heights(&records)
+ .map(|r| r.max().unwrap_or(0))
+ .max()
+ .unwrap_or(0)
+ }
+}
+
+/// Min width value.
+#[derive(Debug)]
+pub struct Min;
+
+impl Measurement<Width> for Min {
+ fn measure<R: Records + ExactRecords + PeekableRecords>(
+ &self,
+ records: R,
+ _: &SpannedConfig,
+ ) -> usize {
+ grid_widths(&records)
+ .map(|r| r.min().unwrap_or(0))
+ .max()
+ .unwrap_or(0)
+ }
+}
+
+impl Measurement<Height> for Min {
+ fn measure<R: Records + ExactRecords + PeekableRecords>(
+ &self,
+ records: R,
+ _: &SpannedConfig,
+ ) -> usize {
+ records_heights(&records)
+ .map(|r| r.max().unwrap_or(0))
+ .min()
+ .unwrap_or(0)
+ }
+}
+
+/// Percent from a total table width.
+#[derive(Debug)]
+pub struct Percent(pub usize);
+
+impl Measurement<Width> for Percent {
+ fn measure<R>(&self, records: R, cfg: &SpannedConfig) -> usize
+ where
+ R: Records,
+ {
+ let (_, total) = get_table_widths_with_total(records, cfg);
+ (total * self.0) / 100
+ }
+}
+
+impl Measurement<Height> for Percent {
+ fn measure<R>(&self, records: R, cfg: &SpannedConfig) -> usize
+ where
+ R: Records + ExactRecords,
+ {
+ let (_, total) = get_table_heights_width_total(records, cfg);
+ (total * self.0) / 100
+ }
+}
+
+fn grid_widths<R: Records + ExactRecords + PeekableRecords>(
+ records: &R,
+) -> impl Iterator<Item = impl Iterator<Item = usize> + '_> + '_ {
+ let (count_rows, count_cols) = (records.count_rows(), records.count_columns());
+ (0..count_rows).map(move |row| {
+ (0..count_cols).map(move |col| string_width_multiline(records.get_text((row, col))))
+ })
+}
+
+fn get_table_widths_with_total<R>(records: R, cfg: &SpannedConfig) -> (Vec<usize>, usize)
+where
+ R: Records,
+{
+ let widths = SpannedGridDimension::width(records, cfg);
+ let total_width = get_table_total_width(&widths, cfg);
+ (widths, total_width)
+}
+
+fn get_table_total_width(list: &[usize], cfg: &SpannedConfig) -> usize {
+ let total = list.iter().sum::<usize>();
+
+ total + cfg.count_vertical(list.len())
+}
+
+fn records_heights<R>(records: &R) -> impl Iterator<Item = impl Iterator<Item = usize> + '_> + '_
+where
+ R: Records + ExactRecords + PeekableRecords,
+{
+ (0..records.count_rows()).map(move |row| {
+ (0..records.count_columns())
+ .map(move |col| string::count_lines(records.get_text((row, col))))
+ })
+}
+
+fn get_table_heights_width_total<R>(records: R, cfg: &SpannedConfig) -> (Vec<usize>, usize)
+where
+ R: Records,
+{
+ let list = SpannedGridDimension::height(records, cfg);
+ let total = get_table_total_height(&list, cfg);
+ (list, total)
+}
+
+fn get_table_total_height(list: &[usize], cfg: &SpannedConfig) -> usize {
+ let total = list.iter().sum::<usize>();
+ let counth = cfg.count_horizontal(list.len());
+
+ total + counth
+}
diff --git a/vendor/tabled/src/settings/merge/mod.rs b/vendor/tabled/src/settings/merge/mod.rs
new file mode 100644
index 000000000..51fa2bbf6
--- /dev/null
+++ b/vendor/tabled/src/settings/merge/mod.rs
@@ -0,0 +1,196 @@
+//! The module contains a set of methods to merge cells together via [`Span`]s.
+//!
+//! [`Span`]: crate::settings::span::Span
+
+use crate::{
+ grid::config::ColoredConfig,
+ grid::records::{ExactRecords, PeekableRecords, Records},
+ settings::TableOption,
+};
+
+/// Merge to combine duplicates together, using [`Span`].
+///
+/// [`Span`]: crate::settings::span::Span
+#[derive(Debug)]
+pub struct Merge;
+
+impl Merge {
+ /// Vertical merge.
+ pub fn vertical() -> MergeDuplicatesVertical {
+ MergeDuplicatesVertical
+ }
+
+ /// Horizontal merge.
+ pub fn horizontal() -> MergeDuplicatesHorizontal {
+ MergeDuplicatesHorizontal
+ }
+}
+
+/// A modificator for [`Table`] which looks up for duplicates in columns and
+/// in case of duplicate merges the cells together using [`Span`].
+///
+/// [`Table`]: crate::Table
+/// [`Span`]: crate::settings::span::Span
+#[derive(Debug)]
+pub struct MergeDuplicatesVertical;
+
+impl<R, D> TableOption<R, D, ColoredConfig> for MergeDuplicatesVertical
+where
+ R: Records + PeekableRecords + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ if count_rows == 0 || count_cols == 0 {
+ return;
+ }
+
+ for column in 0..count_cols {
+ let mut repeat_length = 0;
+ let mut repeat_value = String::new();
+ let mut repeat_is_set = false;
+ let mut last_is_row_span = false;
+ for row in (0..count_rows).rev() {
+ if last_is_row_span {
+ last_is_row_span = false;
+ continue;
+ }
+
+ // we need to mitigate messing existing spans
+ let is_cell_visible = cfg.is_cell_visible((row, column));
+ let is_row_span_cell = cfg.get_column_span((row, column)).is_some();
+
+ if !repeat_is_set {
+ if !is_cell_visible {
+ continue;
+ }
+
+ if is_row_span_cell {
+ continue;
+ }
+
+ repeat_length = 1;
+ repeat_value = records.get_text((row, column)).to_owned();
+ repeat_is_set = true;
+ continue;
+ }
+
+ if is_row_span_cell {
+ repeat_is_set = false;
+ last_is_row_span = true;
+ continue;
+ }
+
+ if !is_cell_visible {
+ repeat_is_set = false;
+ continue;
+ }
+
+ let text = records.get_text((row, column));
+ let is_duplicate = text == repeat_value;
+
+ if is_duplicate {
+ repeat_length += 1;
+ continue;
+ }
+
+ if repeat_length > 1 {
+ cfg.set_row_span((row + 1, column), repeat_length);
+ }
+
+ repeat_length = 1;
+ repeat_value = records.get_text((row, column)).to_owned();
+ }
+
+ if repeat_length > 1 {
+ cfg.set_row_span((0, column), repeat_length);
+ }
+ }
+ }
+}
+
+/// A modificator for [`Table`] which looks up for duplicates in rows and
+/// in case of duplicate merges the cells together using [`Span`].
+///
+/// [`Table`]: crate::Table
+/// [`Span`]: crate::settings::span::Span
+#[derive(Debug)]
+pub struct MergeDuplicatesHorizontal;
+
+impl<R, D> TableOption<R, D, ColoredConfig> for MergeDuplicatesHorizontal
+where
+ R: Records + PeekableRecords + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ if count_rows == 0 || count_cols == 0 {
+ return;
+ }
+
+ for row in 0..count_rows {
+ let mut repeat_length = 0;
+ let mut repeat_value = String::new();
+ let mut repeat_is_set = false;
+ let mut last_is_col_span = false;
+
+ for column in (0..count_cols).rev() {
+ if last_is_col_span {
+ last_is_col_span = false;
+ continue;
+ }
+
+ // we need to mitigate messing existing spans
+ let is_cell_visible = cfg.is_cell_visible((row, column));
+ let is_col_span_cell = cfg.get_row_span((row, column)).is_some();
+
+ if !repeat_is_set {
+ if !is_cell_visible {
+ continue;
+ }
+
+ if is_col_span_cell {
+ continue;
+ }
+
+ repeat_length = 1;
+ repeat_value = records.get_text((row, column)).to_owned();
+ repeat_is_set = true;
+ continue;
+ }
+
+ if is_col_span_cell {
+ repeat_is_set = false;
+ last_is_col_span = true;
+ continue;
+ }
+
+ if !is_cell_visible {
+ repeat_is_set = false;
+ continue;
+ }
+
+ let text = records.get_text((row, column));
+ let is_duplicate = text == repeat_value;
+
+ if is_duplicate {
+ repeat_length += 1;
+ continue;
+ }
+
+ if repeat_length > 1 {
+ cfg.set_column_span((row, column + 1), repeat_length);
+ }
+
+ repeat_length = 1;
+ repeat_value = records.get_text((row, column)).to_owned();
+ }
+
+ if repeat_length > 1 {
+ cfg.set_column_span((row, 0), repeat_length);
+ }
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/mod.rs b/vendor/tabled/src/settings/mod.rs
new file mode 100644
index 000000000..a89037cbd
--- /dev/null
+++ b/vendor/tabled/src/settings/mod.rs
@@ -0,0 +1,135 @@
+//! Module contains various table configuration settings.
+//!
+//! There 2 types of settings;
+//!
+//! - [`CellOption`] which can modify only a cell.
+//! - [`TableOption`] which can modify table as a whole.
+//!
+//! [`CellOption`] works on behave of [`Modify`] which is actually a [`TableOption`].
+//!
+//! Notice that it's possble to combine settings together by the help of [`Settings`].
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::{Settings, Style, Padding}};
+//!
+//! let table_config = Settings::default()
+//! .with(Padding::new(2, 2, 1, 1))
+//! .with(Style::rounded());
+//!
+//! let data = [[2023;9]; 3];
+//!
+//! let table = Table::new(data).with(table_config).to_string();
+//!
+//! assert_eq!(
+//! table,
+//! "╭────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────╮\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! ├────────┼────────┼────────┼────────┼────────┼────────┼────────┼────────┼────────┤\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │ 2023 │\n\
+//! │ │ │ │ │ │ │ │ │ │\n\
+//! ╰────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────╯"
+//! )
+//! ```
+
+mod cell_option;
+mod settings_list;
+mod table_option;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod object;
+
+#[cfg(feature = "std")]
+mod modify;
+
+mod alignment;
+mod extract;
+mod margin;
+mod padding;
+mod rotate;
+
+#[cfg(feature = "std")]
+mod color;
+#[cfg(feature = "std")]
+mod concat;
+#[cfg(feature = "std")]
+mod duplicate;
+
+pub mod style;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod disable;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod format;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod formatting;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod height;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod highlight;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod locator;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod measurement;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod merge;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod panel;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod peaker;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+mod shadow;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod span;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod split;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod themes;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod width;
+
+pub use cell_option::CellOption;
+pub use settings_list::{EmptySettings, Settings};
+pub use table_option::TableOption;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use modify::{Modify, ModifyList};
+
+pub use self::{
+ alignment::Alignment, extract::Extract, margin::Margin, padding::Padding, rotate::Rotate,
+ style::Style,
+};
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use self::{
+ color::Color, concat::Concat, disable::Disable, duplicate::Dup, format::Format, height::Height,
+ highlight::Highlight, merge::Merge, panel::Panel, shadow::Shadow, span::Span, style::Border,
+ width::Width,
+};
diff --git a/vendor/tabled/src/settings/modify.rs b/vendor/tabled/src/settings/modify.rs
new file mode 100644
index 000000000..c712a255e
--- /dev/null
+++ b/vendor/tabled/src/settings/modify.rs
@@ -0,0 +1,81 @@
+use crate::{
+ grid::records::{ExactRecords, Records},
+ settings::{object::Object, CellOption, Settings, TableOption},
+};
+
+/// Modify structure provide an abstraction, to be able to apply
+/// a set of [`CellOption`]s to the same object.
+///
+/// Be aware that the settings are applied all to a cell at a time.
+/// So sometimes you may need to make a several calls of [`Modify`] in order to achieve the desired affect.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Modify<O> {
+ obj: O,
+}
+
+impl<O> Modify<O> {
+ /// Creates a new [`Modify`] without any options.
+ pub const fn new(obj: O) -> Self {
+ Self { obj }
+ }
+
+ /// A function which combines together [`Modify::new`] and [`Modify::with`] calls.
+ pub const fn list<M>(obj: O, next: M) -> ModifyList<O, M> {
+ ModifyList {
+ obj,
+ modifiers: next,
+ }
+ }
+
+ /// It's a generic function which stores a [`CellOption`].
+ ///
+ /// IMPORTANT:
+ /// The function *doesn't* changes a [`Table`].
+ /// [`Table`] will be changed only after passing [`Modify`] object to [`Table::with`].
+ ///
+ /// [`Table`]: crate::Table
+ /// [`Table::with`]: crate::Table::with
+ pub fn with<M>(self, next: M) -> ModifyList<O, M> {
+ ModifyList {
+ obj: self.obj,
+ modifiers: next,
+ }
+ }
+}
+
+/// This is a container of [`CellOption`]s which are applied to a set [`Object`].
+#[derive(Debug)]
+pub struct ModifyList<O, S> {
+ obj: O,
+ modifiers: S,
+}
+
+impl<O, M1> ModifyList<O, M1> {
+ /// With a generic function which stores a [`CellOption`].
+ ///
+ /// IMPORTANT:
+ /// The function *doesn't* changes a [`Table`].
+ /// [`Table`] will be changed only after passing [`Modify`] object to [`Table::with`].
+ ///
+ /// [`Table`]: crate::Table
+ /// [`Table::with`]: crate::Table::with
+ pub fn with<M2>(self, next: M2) -> ModifyList<O, Settings<M1, M2>> {
+ ModifyList {
+ obj: self.obj,
+ modifiers: Settings::new(self.modifiers, next),
+ }
+ }
+}
+
+impl<O, M, R, D, C> TableOption<R, D, C> for ModifyList<O, M>
+where
+ O: Object<R>,
+ M: CellOption<R, C> + Clone,
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut C, _: &mut D) {
+ for entity in self.obj.cells(records) {
+ self.modifiers.clone().change(records, cfg, entity);
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/object/cell.rs b/vendor/tabled/src/settings/object/cell.rs
new file mode 100644
index 000000000..0b0463c9a
--- /dev/null
+++ b/vendor/tabled/src/settings/object/cell.rs
@@ -0,0 +1,70 @@
+use crate::{
+ grid::config::{Entity, Position},
+ settings::object::Object,
+};
+
+/// Cell denotes a particular cell on a [`Table`].
+///
+/// For example such table has 4 cells.
+/// Which indexes are (0, 0), (0, 1), (1, 0), (1, 1).
+///
+/// ```text
+/// ┌───┬───┐
+/// │ 0 │ 1 │
+/// ├───┼───┤
+/// │ 1 │ 2 │
+/// └───┴───┘
+/// ```
+///
+/// [`Table`]: crate::Table
+#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Cell(usize, usize);
+
+impl Cell {
+ /// Create new cell structure.
+ pub fn new(row: usize, col: usize) -> Self {
+ Self(row, col)
+ }
+}
+
+impl From<Position> for Cell {
+ fn from((row, col): Position) -> Self {
+ Self(row, col)
+ }
+}
+
+impl<I> Object<I> for Cell {
+ type Iter = EntityOnce;
+
+ fn cells(&self, _: &I) -> Self::Iter {
+ EntityOnce::new(Some(Entity::Cell(self.0, self.1)))
+ }
+}
+
+impl<I> Object<I> for Position {
+ type Iter = EntityOnce;
+
+ fn cells(&self, _: &I) -> Self::Iter {
+ EntityOnce::new(Some(Entity::Cell(self.0, self.1)))
+ }
+}
+
+/// An [`Iterator`] which returns an entity once.
+#[derive(Debug)]
+pub struct EntityOnce {
+ entity: Option<Entity>,
+}
+
+impl EntityOnce {
+ pub(crate) const fn new(entity: Option<Entity>) -> Self {
+ Self { entity }
+ }
+}
+
+impl Iterator for EntityOnce {
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.entity.take()
+ }
+}
diff --git a/vendor/tabled/src/settings/object/columns.rs b/vendor/tabled/src/settings/object/columns.rs
new file mode 100644
index 000000000..bcbe2acf5
--- /dev/null
+++ b/vendor/tabled/src/settings/object/columns.rs
@@ -0,0 +1,209 @@
+use std::ops::{Add, RangeBounds, Sub};
+
+use crate::{
+ grid::config::Entity,
+ grid::records::{ExactRecords, Records},
+ settings::object::{cell::EntityOnce, Object},
+};
+
+use super::util::bounds_to_usize;
+
+/// Column denotes a set of cells on given columns on a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Columns<R> {
+ range: R,
+}
+
+impl<R> Columns<R> {
+ /// Returns a new instance of [`Columns`] for a range of columns.
+ ///
+ /// If the boundaries are exceeded it may panic.
+ pub fn new(range: R) -> Self
+ where
+ R: RangeBounds<usize>,
+ {
+ Self { range }
+ }
+
+ pub(crate) fn get_range(&self) -> &R {
+ &self.range
+ }
+}
+
+impl Columns<()> {
+ /// Returns a new instance of [`Columns`] for a single column.
+ ///
+ /// If the boundaries are exceeded it may panic.
+ pub fn single(index: usize) -> Column {
+ Column(index)
+ }
+
+ /// Returns a new instance of [`Columns`] for a first column.
+ ///
+ /// If the boundaries are exceeded the object will produce no cells.
+ pub fn first() -> FirstColumn {
+ FirstColumn
+ }
+
+ /// Returns a new instance of [`Columns`] for a last column.
+ ///
+ /// If the boundaries are exceeded the object will produce no cells.
+ pub fn last() -> LastColumn {
+ LastColumn
+ }
+}
+
+impl<I, R> Object<I> for Columns<R>
+where
+ R: RangeBounds<usize>,
+ I: Records,
+{
+ type Iter = ColumnsIter;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let max = records.count_columns();
+ let start = self.range.start_bound();
+ let end = self.range.end_bound();
+ let (x, y) = bounds_to_usize(start, end, max);
+
+ ColumnsIter::new(x, y)
+ }
+}
+
+/// `FirstColumn` represents the first column on a grid.
+#[derive(Debug)]
+pub struct FirstColumn;
+
+impl<I> Object<I> for FirstColumn
+where
+ I: Records + ExactRecords,
+{
+ type Iter = EntityOnce;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return EntityOnce::new(None);
+ }
+
+ EntityOnce::new(Some(Entity::Column(0)))
+ }
+}
+
+impl Add<usize> for FirstColumn {
+ type Output = Column;
+
+ fn add(self, rhs: usize) -> Self::Output {
+ Column(rhs)
+ }
+}
+
+/// `LastColumn` represents the last column on a grid.
+#[derive(Debug)]
+pub struct LastColumn;
+
+impl<I> Object<I> for LastColumn
+where
+ I: Records + ExactRecords,
+{
+ type Iter = EntityOnce;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return EntityOnce::new(None);
+ }
+
+ let col = records.count_columns().saturating_sub(1);
+ EntityOnce::new(Some(Entity::Column(col)))
+ }
+}
+
+impl Sub<usize> for LastColumn {
+ type Output = LastColumnOffset;
+
+ fn sub(self, rhs: usize) -> Self::Output {
+ LastColumnOffset { offset: rhs }
+ }
+}
+
+/// Column represents a single column on a grid.
+#[derive(Debug, Clone, Copy)]
+pub struct Column(usize);
+
+impl<I> Object<I> for Column {
+ type Iter = EntityOnce;
+
+ fn cells(&self, _: &I) -> Self::Iter {
+ EntityOnce::new(Some(Entity::Column(self.0)))
+ }
+}
+
+impl From<usize> for Column {
+ fn from(i: usize) -> Self {
+ Self(i)
+ }
+}
+
+impl From<Column> for usize {
+ fn from(val: Column) -> Self {
+ val.0
+ }
+}
+
+/// `LastColumnOffset` represents a single column on a grid indexed via offset from the last column.
+#[derive(Debug)]
+pub struct LastColumnOffset {
+ offset: usize,
+}
+
+impl<I> Object<I> for LastColumnOffset
+where
+ I: Records + ExactRecords,
+{
+ type Iter = EntityOnce;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return EntityOnce::new(None);
+ }
+
+ let col = records.count_columns().saturating_sub(1);
+ if self.offset > col {
+ return EntityOnce::new(None);
+ }
+
+ let col = col - self.offset;
+ EntityOnce::new(Some(Entity::Column(col)))
+ }
+}
+
+/// An [`Iterator`] which goes goes over columns of a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct ColumnsIter {
+ start: usize,
+ end: usize,
+}
+
+impl ColumnsIter {
+ const fn new(start: usize, end: usize) -> Self {
+ Self { start, end }
+ }
+}
+
+impl Iterator for ColumnsIter {
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.start >= self.end {
+ return None;
+ }
+
+ let col = self.start;
+ self.start += 1;
+
+ Some(Entity::Column(col))
+ }
+}
diff --git a/vendor/tabled/src/settings/object/frame.rs b/vendor/tabled/src/settings/object/frame.rs
new file mode 100644
index 000000000..caeb10640
--- /dev/null
+++ b/vendor/tabled/src/settings/object/frame.rs
@@ -0,0 +1,69 @@
+use crate::{
+ grid::config::Entity,
+ grid::records::{ExactRecords, Records},
+ settings::object::Object,
+};
+
+/// Frame includes cells which are on the edges of each side.
+/// Therefore it's [`Object`] implementation returns a subset of cells which are present in frame.
+#[derive(Debug)]
+pub struct Frame;
+
+impl<I> Object<I> for Frame
+where
+ I: Records + ExactRecords,
+{
+ type Iter = FrameIter;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ FrameIter::new(records.count_rows(), records.count_columns())
+ }
+}
+
+/// An [`Iterator`] which goes goes over all cell on a frame of a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct FrameIter {
+ rows: usize,
+ cols: usize,
+ row: usize,
+ col: usize,
+}
+
+impl FrameIter {
+ const fn new(count_rows: usize, count_columns: usize) -> Self {
+ Self {
+ rows: count_rows,
+ cols: count_columns,
+ row: 0,
+ col: 0,
+ }
+ }
+}
+
+impl Iterator for FrameIter {
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.cols == 0 || self.rows == 0 {
+ return None;
+ }
+
+ if self.row == self.rows {
+ return None;
+ }
+
+ let row = self.row;
+ let col = self.col;
+
+ self.col += 1;
+
+ if self.col == self.cols {
+ self.row += 1;
+ self.col = 0;
+ }
+
+ Some(Entity::Cell(row, col))
+ }
+}
diff --git a/vendor/tabled/src/settings/object/mod.rs b/vendor/tabled/src/settings/object/mod.rs
new file mode 100644
index 000000000..46893c71d
--- /dev/null
+++ b/vendor/tabled/src/settings/object/mod.rs
@@ -0,0 +1,765 @@
+//! This module contains a list of primitives that implement a [`Object`] trait.
+//! They help to locate a necessary segment on a [`Table`].
+//!
+//! [`Table`]: crate::Table
+
+mod cell;
+mod columns;
+mod frame;
+mod rows;
+mod segment;
+pub(crate) mod util;
+
+use std::{collections::HashSet, marker::PhantomData};
+
+use self::segment::SectorCellsIter;
+
+use crate::{
+ grid::config::{Entity, EntityIterator},
+ grid::records::{ExactRecords, Records},
+};
+
+pub use cell::{Cell, EntityOnce};
+pub use columns::{Column, Columns, ColumnsIter, FirstColumn, LastColumn, LastColumnOffset};
+pub use frame::{Frame, FrameIter};
+pub use rows::{FirstRow, LastRow, LastRowOffset, Row, Rows, RowsIter};
+pub use segment::{SectorIter, Segment, SegmentAll};
+
+/// Object helps to locate a necessary part of a [`Table`].
+///
+/// [`Table`]: crate::Table
+pub trait Object<R> {
+ /// An [`Iterator`] which returns a list of cells.
+ type Iter: Iterator<Item = Entity>;
+
+ /// Cells returns a set of coordinates of cells.
+ fn cells(&self, records: &R) -> Self::Iter;
+
+ /// Combines cells.
+ /// It doesn't repeat cells.
+ fn and<O>(self, rhs: O) -> UnionCombination<Self, O, R>
+ where
+ Self: Sized,
+ {
+ UnionCombination::new(self, rhs)
+ }
+
+ /// Excludes rhs cells from this cells.
+ fn not<O>(self, rhs: O) -> DiffCombination<Self, O, R>
+ where
+ Self: Sized,
+ {
+ DiffCombination::new(self, rhs)
+ }
+
+ /// Returns cells which are present in both [`Object`]s only.
+ fn intersect<O>(self, rhs: O) -> IntersectionCombination<Self, O, R>
+ where
+ Self: Sized,
+ {
+ IntersectionCombination::new(self, rhs)
+ }
+
+ /// Returns cells which are not present in target [`Object`].
+ fn inverse(self) -> InversionCombination<Self, R>
+ where
+ Self: Sized,
+ {
+ InversionCombination::new(self)
+ }
+}
+
+/// Combination struct used for chaining [`Object`]'s.
+///
+/// Combines 2 sets of cells into one.
+///
+/// Duplicates are removed from the output set.
+#[derive(Debug)]
+pub struct UnionCombination<L, R, I> {
+ lhs: L,
+ rhs: R,
+ _records: PhantomData<I>,
+}
+
+impl<L, R, I> UnionCombination<L, R, I> {
+ fn new(lhs: L, rhs: R) -> Self {
+ Self {
+ lhs,
+ rhs,
+ _records: PhantomData,
+ }
+ }
+}
+
+impl<I, L, R> Object<I> for UnionCombination<L, R, I>
+where
+ L: Object<I>,
+ R: Object<I>,
+ I: Records + ExactRecords,
+{
+ type Iter = UnionIter<L::Iter, R::Iter>;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let lhs = self.lhs.cells(records);
+ let rhs = self.rhs.cells(records);
+
+ UnionIter::new(lhs, rhs, records.count_rows(), records.count_columns())
+ }
+}
+
+/// Difference struct used for chaining [`Object`]'s.
+///
+/// Returns cells from 1st set with removed ones from the 2nd set.
+#[derive(Debug)]
+pub struct DiffCombination<L, R, I> {
+ lhs: L,
+ rhs: R,
+ _records: PhantomData<I>,
+}
+
+impl<L, R, I> DiffCombination<L, R, I> {
+ fn new(lhs: L, rhs: R) -> Self {
+ Self {
+ lhs,
+ rhs,
+ _records: PhantomData,
+ }
+ }
+}
+
+impl<I, L, R> Object<I> for DiffCombination<L, R, I>
+where
+ L: Object<I>,
+ R: Object<I>,
+ I: Records + ExactRecords,
+{
+ type Iter = DiffIter<L::Iter>;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let lhs = self.lhs.cells(records);
+ let rhs = self.rhs.cells(records);
+
+ DiffIter::new(lhs, rhs, records.count_rows(), records.count_columns())
+ }
+}
+
+/// Intersection struct used for chaining [`Object`]'s.
+///
+/// Returns cells which are present in 2 sets.
+/// But not in one of them
+#[derive(Debug)]
+pub struct IntersectionCombination<L, R, I> {
+ lhs: L,
+ rhs: R,
+ _records: PhantomData<I>,
+}
+
+impl<L, R, I> IntersectionCombination<L, R, I> {
+ fn new(lhs: L, rhs: R) -> Self {
+ Self {
+ lhs,
+ rhs,
+ _records: PhantomData,
+ }
+ }
+}
+
+impl<I, L, R> Object<I> for IntersectionCombination<L, R, I>
+where
+ L: Object<I>,
+ R: Object<I>,
+ I: Records + ExactRecords,
+{
+ type Iter = IntersectIter<L::Iter>;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let lhs = self.lhs.cells(records);
+ let rhs = self.rhs.cells(records);
+
+ IntersectIter::new(lhs, rhs, records.count_rows(), records.count_columns())
+ }
+}
+
+/// Inversion struct used for chaining [`Object`]'s.
+///
+/// Returns cells which are present in 2 sets.
+/// But not in one of them
+#[derive(Debug)]
+pub struct InversionCombination<O, I> {
+ obj: O,
+ _records: PhantomData<I>,
+}
+
+impl<O, I> InversionCombination<O, I> {
+ fn new(obj: O) -> Self {
+ Self {
+ obj,
+ _records: PhantomData,
+ }
+ }
+}
+
+impl<I, O> Object<I> for InversionCombination<O, I>
+where
+ O: Object<I>,
+ I: Records + ExactRecords,
+{
+ type Iter = InversionIter;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let obj = self.obj.cells(records);
+
+ InversionIter::new(obj, records.count_rows(), records.count_columns())
+ }
+}
+
+/// An [`Iterator`] which goes over a combination [`Object::Iter`].
+#[derive(Debug)]
+pub struct UnionIter<L, R> {
+ lhs: Option<L>,
+ rhs: R,
+ seen: HashSet<(usize, usize)>,
+ current: Option<EntityIterator>,
+ count_rows: usize,
+ count_cols: usize,
+}
+
+impl<L, R> UnionIter<L, R>
+where
+ L: Iterator<Item = Entity>,
+ R: Iterator<Item = Entity>,
+{
+ fn new(lhs: L, rhs: R, count_rows: usize, count_cols: usize) -> Self {
+ let size = match lhs.size_hint() {
+ (s1, Some(s2)) if s1 == s2 => s1,
+ _ => 0,
+ };
+
+ Self {
+ lhs: Some(lhs),
+ rhs,
+ seen: HashSet::with_capacity(size),
+ current: None,
+ count_rows,
+ count_cols,
+ }
+ }
+}
+
+impl<L, R> Iterator for UnionIter<L, R>
+where
+ L: Iterator<Item = Entity>,
+ R: Iterator<Item = Entity>,
+{
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if let Some(iter) = self.current.as_mut() {
+ for p in iter.by_ref() {
+ if self.lhs.is_none() && self.seen.contains(&p) {
+ continue;
+ }
+
+ let _ = self.seen.insert(p);
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+
+ if let Some(lhs) = self.lhs.as_mut() {
+ for entity in lhs.by_ref() {
+ let mut iter = entity.iter(self.count_rows, self.count_cols);
+ if let Some(p) = iter.by_ref().next() {
+ let _ = self.seen.insert(p);
+ self.current = Some(iter);
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+
+ self.lhs = None;
+ }
+
+ for entity in self.rhs.by_ref() {
+ let mut iter = entity.iter(self.count_rows, self.count_cols);
+
+ for p in iter.by_ref() {
+ if !self.seen.contains(&p) {
+ let _ = self.seen.insert(p);
+ self.current = Some(iter);
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+ }
+
+ None
+ }
+}
+
+/// An [`Iterator`] which goes over only cells which are present in first [`Object::Iter`] but not second.
+#[derive(Debug)]
+pub struct DiffIter<L> {
+ lhs: L,
+ seen: HashSet<(usize, usize)>,
+ count_rows: usize,
+ count_cols: usize,
+ current: Option<EntityIterator>,
+}
+
+impl<L> DiffIter<L>
+where
+ L: Iterator<Item = Entity>,
+{
+ fn new<R>(lhs: L, rhs: R, count_rows: usize, count_cols: usize) -> Self
+ where
+ R: Iterator<Item = Entity>,
+ {
+ let size = match rhs.size_hint() {
+ (s1, Some(s2)) if s1 == s2 => s1,
+ _ => 0,
+ };
+
+ let mut seen = HashSet::with_capacity(size);
+ for entity in rhs {
+ seen.extend(entity.iter(count_rows, count_cols));
+ }
+
+ Self {
+ lhs,
+ seen,
+ count_rows,
+ count_cols,
+ current: None,
+ }
+ }
+}
+
+impl<L> Iterator for DiffIter<L>
+where
+ L: Iterator<Item = Entity>,
+{
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if let Some(iter) = self.current.as_mut() {
+ for p in iter.by_ref() {
+ if !self.seen.contains(&p) {
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+ }
+
+ for entity in self.lhs.by_ref() {
+ let mut iter = entity.iter(self.count_rows, self.count_cols);
+
+ for p in iter.by_ref() {
+ if !self.seen.contains(&p) {
+ self.current = Some(iter);
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+ }
+
+ None
+ }
+}
+
+/// An [`Iterator`] which goes goes over cells which are present in both [`Object::Iter`]ators.
+#[derive(Debug)]
+pub struct IntersectIter<L> {
+ lhs: L,
+ seen: HashSet<(usize, usize)>,
+ count_rows: usize,
+ count_cols: usize,
+ current: Option<EntityIterator>,
+}
+
+impl<L> IntersectIter<L>
+where
+ L: Iterator<Item = Entity>,
+{
+ fn new<R>(lhs: L, rhs: R, count_rows: usize, count_cols: usize) -> Self
+ where
+ R: Iterator<Item = Entity>,
+ {
+ let size = match rhs.size_hint() {
+ (s1, Some(s2)) if s1 == s2 => s1,
+ _ => 0,
+ };
+
+ let mut seen = HashSet::with_capacity(size);
+ for entity in rhs {
+ seen.extend(entity.iter(count_rows, count_cols));
+ }
+
+ Self {
+ lhs,
+ seen,
+ count_rows,
+ count_cols,
+ current: None,
+ }
+ }
+}
+
+impl<L> Iterator for IntersectIter<L>
+where
+ L: Iterator<Item = Entity>,
+{
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if let Some(iter) = self.current.as_mut() {
+ for p in iter.by_ref() {
+ if self.seen.contains(&p) {
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+ }
+
+ for entity in self.lhs.by_ref() {
+ let mut iter = entity.iter(self.count_rows, self.count_cols);
+
+ for p in iter.by_ref() {
+ if self.seen.contains(&p) {
+ self.current = Some(iter);
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+ }
+
+ None
+ }
+}
+
+/// An [`Iterator`] which goes goes over cells which are not present an [`Object::Iter`]ator.
+#[derive(Debug)]
+pub struct InversionIter {
+ all: SectorCellsIter,
+ seen: HashSet<(usize, usize)>,
+}
+
+impl InversionIter {
+ fn new<O>(obj: O, count_rows: usize, count_columns: usize) -> Self
+ where
+ O: Iterator<Item = Entity>,
+ {
+ let size = match obj.size_hint() {
+ (s1, Some(s2)) if s1 == s2 => s1,
+ _ => 0,
+ };
+
+ let mut seen = HashSet::with_capacity(size);
+ for entity in obj {
+ seen.extend(entity.iter(count_rows, count_columns));
+ }
+
+ let all = SectorCellsIter::new(0, count_rows, 0, count_columns);
+
+ Self { all, seen }
+ }
+}
+
+impl Iterator for InversionIter {
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ for p in self.all.by_ref() {
+ if !self.seen.contains(&p) {
+ return Some(Entity::Cell(p.0, p.1));
+ }
+ }
+
+ None
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::grid::records::vec_records::VecRecords;
+
+ use super::*;
+
+ #[test]
+ fn cell_test() {
+ assert_eq!(vec_cells((0, 0), 2, 3), [Entity::Cell(0, 0)]);
+ assert_eq!(vec_cells((1, 1), 2, 3), [Entity::Cell(1, 1)]);
+ assert_eq!(vec_cells((1, 1), 0, 0), [Entity::Cell(1, 1)]);
+ assert_eq!(vec_cells((1, 100), 2, 3), [Entity::Cell(1, 100)]);
+ assert_eq!(vec_cells((100, 1), 2, 3), [Entity::Cell(100, 1)]);
+ }
+
+ #[test]
+ fn columns_test() {
+ assert_eq!(
+ vec_cells(Columns::new(..), 2, 3),
+ [Entity::Column(0), Entity::Column(1), Entity::Column(2)]
+ );
+ assert_eq!(
+ vec_cells(Columns::new(1..), 2, 3),
+ [Entity::Column(1), Entity::Column(2)]
+ );
+ assert_eq!(vec_cells(Columns::new(2..), 2, 3), [Entity::Column(2)]);
+ assert_eq!(vec_cells(Columns::new(3..), 2, 3), []);
+ assert_eq!(vec_cells(Columns::new(3..), 0, 0), []);
+ assert_eq!(vec_cells(Columns::new(0..1), 2, 3), [Entity::Column(0)]);
+ assert_eq!(vec_cells(Columns::new(1..2), 2, 3), [Entity::Column(1)]);
+ assert_eq!(vec_cells(Columns::new(2..3), 2, 3), [Entity::Column(2)]);
+ assert_eq!(vec_cells(Columns::new(..), 0, 0), []);
+ assert_eq!(vec_cells(Columns::new(..), 2, 0), []);
+ assert_eq!(vec_cells(Columns::new(..), 0, 3), []);
+ }
+
+ #[test]
+ fn first_column_test() {
+ assert_eq!(vec_cells(Columns::first(), 5, 2), [Entity::Column(0)]);
+ assert_eq!(vec_cells(Columns::first(), 0, 0), []);
+ assert_eq!(vec_cells(Columns::first(), 10, 0), []);
+ assert_eq!(vec_cells(Columns::first(), 0, 10), []);
+ }
+
+ #[test]
+ fn last_column_test() {
+ assert_eq!(vec_cells(Columns::last(), 5, 2), [Entity::Column(1)]);
+ assert_eq!(vec_cells(Columns::last(), 5, 29), [Entity::Column(28)]);
+ assert_eq!(vec_cells(Columns::last(), 0, 0), []);
+ assert_eq!(vec_cells(Columns::last(), 10, 0), []);
+ assert_eq!(vec_cells(Columns::last(), 0, 10), []);
+ }
+
+ #[test]
+ fn last_column_sub_test() {
+ assert_eq!(vec_cells(Columns::last(), 5, 2), [Entity::Column(1)]);
+ assert_eq!(vec_cells(Columns::last() - 0, 5, 2), [Entity::Column(1)]);
+ assert_eq!(vec_cells(Columns::last() - 1, 5, 2), [Entity::Column(0)]);
+ assert_eq!(vec_cells(Columns::last() - 2, 5, 2), []);
+ assert_eq!(vec_cells(Columns::last() - 100, 5, 2), []);
+ }
+
+ #[test]
+ fn first_column_add_test() {
+ assert_eq!(vec_cells(Columns::first(), 5, 2), [Entity::Column(0)]);
+ assert_eq!(vec_cells(Columns::first() + 0, 5, 2), [Entity::Column(0)]);
+ assert_eq!(vec_cells(Columns::first() + 1, 5, 2), [Entity::Column(1)]);
+ assert_eq!(vec_cells(Columns::first() + 2, 5, 2), [Entity::Column(2)]);
+ assert_eq!(
+ vec_cells(Columns::first() + 100, 5, 2),
+ [Entity::Column(100)]
+ );
+ }
+
+ #[test]
+ fn rows_test() {
+ assert_eq!(
+ vec_cells(Rows::new(..), 2, 3),
+ [Entity::Row(0), Entity::Row(1)]
+ );
+ assert_eq!(vec_cells(Rows::new(1..), 2, 3), [Entity::Row(1)]);
+ assert_eq!(vec_cells(Rows::new(2..), 2, 3), []);
+ assert_eq!(vec_cells(Rows::new(2..), 0, 0), []);
+ assert_eq!(vec_cells(Rows::new(0..1), 2, 3), [Entity::Row(0)],);
+ assert_eq!(vec_cells(Rows::new(1..2), 2, 3), [Entity::Row(1)],);
+ assert_eq!(vec_cells(Rows::new(..), 0, 0), []);
+ assert_eq!(vec_cells(Rows::new(..), 0, 3), []);
+ assert_eq!(
+ vec_cells(Rows::new(..), 2, 0),
+ [Entity::Row(0), Entity::Row(1)]
+ );
+ }
+
+ #[test]
+ fn last_row_test() {
+ assert_eq!(vec_cells(Rows::last(), 5, 2), [Entity::Row(4)]);
+ assert_eq!(vec_cells(Rows::last(), 100, 2), [Entity::Row(99)]);
+ assert_eq!(vec_cells(Rows::last(), 0, 0), []);
+ assert_eq!(vec_cells(Rows::last(), 5, 0), []);
+ assert_eq!(vec_cells(Rows::last(), 0, 2), []);
+ }
+
+ #[test]
+ fn first_row_test() {
+ assert_eq!(vec_cells(Rows::first(), 5, 2), [Entity::Row(0)]);
+ assert_eq!(vec_cells(Rows::first(), 100, 2), [Entity::Row(0)]);
+ assert_eq!(vec_cells(Rows::first(), 0, 0), []);
+ assert_eq!(vec_cells(Rows::first(), 5, 0), []);
+ assert_eq!(vec_cells(Rows::first(), 0, 2), []);
+ }
+
+ #[test]
+ fn last_row_sub_test() {
+ assert_eq!(vec_cells(Rows::last(), 5, 2), [Entity::Row(4)]);
+ assert_eq!(vec_cells(Rows::last() - 0, 5, 2), [Entity::Row(4)]);
+ assert_eq!(vec_cells(Rows::last() - 1, 5, 2), [Entity::Row(3)]);
+ assert_eq!(vec_cells(Rows::last() - 2, 5, 2), [Entity::Row(2)]);
+ assert_eq!(vec_cells(Rows::last() - 3, 5, 2), [Entity::Row(1)]);
+ assert_eq!(vec_cells(Rows::last() - 4, 5, 2), [Entity::Row(0)]);
+ assert_eq!(vec_cells(Rows::last() - 5, 5, 2), []);
+ assert_eq!(vec_cells(Rows::last() - 100, 5, 2), []);
+ assert_eq!(vec_cells(Rows::last() - 1, 0, 0), []);
+ assert_eq!(vec_cells(Rows::last() - 1, 5, 0), []);
+ assert_eq!(vec_cells(Rows::last() - 1, 0, 2), []);
+ }
+
+ #[test]
+ fn first_row_add_test() {
+ assert_eq!(vec_cells(Rows::first(), 5, 2), [Entity::Row(0)]);
+ assert_eq!(vec_cells(Rows::first() + 0, 5, 2), [Entity::Row(0)]);
+ assert_eq!(vec_cells(Rows::first() + 1, 5, 2), [Entity::Row(1)]);
+ assert_eq!(vec_cells(Rows::first() + 2, 5, 2), [Entity::Row(2)]);
+ assert_eq!(vec_cells(Rows::first() + 3, 5, 2), [Entity::Row(3)]);
+ assert_eq!(vec_cells(Rows::first() + 4, 5, 2), [Entity::Row(4)]);
+ assert_eq!(vec_cells(Rows::first() + 5, 5, 2), [Entity::Row(5)]);
+ assert_eq!(vec_cells(Rows::first() + 100, 5, 2), [Entity::Row(100)]);
+ assert_eq!(vec_cells(Rows::first() + 1, 0, 0), [Entity::Row(1)]);
+ assert_eq!(vec_cells(Rows::first() + 1, 5, 0), [Entity::Row(1)]);
+ assert_eq!(vec_cells(Rows::first() + 1, 0, 2), [Entity::Row(1)]);
+ }
+
+ #[test]
+ fn frame_test() {
+ assert_eq!(
+ vec_cells(Frame, 2, 3),
+ [
+ Entity::Cell(0, 0),
+ Entity::Cell(0, 1),
+ Entity::Cell(0, 2),
+ Entity::Cell(1, 0),
+ Entity::Cell(1, 1),
+ Entity::Cell(1, 2)
+ ]
+ );
+ assert_eq!(vec_cells(Frame, 0, 0), []);
+ assert_eq!(vec_cells(Frame, 2, 0), []);
+ assert_eq!(vec_cells(Frame, 0, 2), []);
+ }
+
+ #[test]
+ fn segment_test() {
+ assert_eq!(
+ vec_cells(Segment::new(.., ..), 2, 3),
+ [
+ Entity::Cell(0, 0),
+ Entity::Cell(0, 1),
+ Entity::Cell(0, 2),
+ Entity::Cell(1, 0),
+ Entity::Cell(1, 1),
+ Entity::Cell(1, 2)
+ ]
+ );
+ assert_eq!(
+ vec_cells(Segment::new(1.., ..), 2, 3),
+ [Entity::Cell(1, 0), Entity::Cell(1, 1), Entity::Cell(1, 2)]
+ );
+ assert_eq!(vec_cells(Segment::new(2.., ..), 2, 3), []);
+
+ assert_eq!(
+ vec_cells(Segment::new(.., 1..), 2, 3),
+ [
+ Entity::Cell(0, 1),
+ Entity::Cell(0, 2),
+ Entity::Cell(1, 1),
+ Entity::Cell(1, 2)
+ ]
+ );
+ assert_eq!(
+ vec_cells(Segment::new(.., 2..), 2, 3),
+ [Entity::Cell(0, 2), Entity::Cell(1, 2)]
+ );
+ assert_eq!(vec_cells(Segment::new(.., 3..), 2, 3), []);
+
+ assert_eq!(
+ vec_cells(Segment::new(1.., 1..), 2, 3),
+ [Entity::Cell(1, 1), Entity::Cell(1, 2)]
+ );
+ assert_eq!(
+ vec_cells(Segment::new(1..2, 1..2), 2, 3),
+ [Entity::Cell(1, 1)]
+ );
+
+ assert_eq!(vec_cells(Segment::new(5.., 5..), 2, 3), []);
+ }
+
+ #[test]
+ fn object_and_test() {
+ assert_eq!(
+ vec_cells(Cell::new(0, 0).and(Cell::new(0, 0)), 2, 3),
+ [Entity::Cell(0, 0)]
+ );
+ assert_eq!(
+ vec_cells(Cell::new(0, 0).and(Cell::new(1, 2)), 2, 3),
+ [Entity::Cell(0, 0), Entity::Cell(1, 2)]
+ );
+ assert_eq!(vec_cells(Cell::new(0, 0).and(Cell::new(1, 2)), 0, 0), []);
+ }
+
+ #[test]
+ fn object_not_test() {
+ assert_eq!(vec_cells(Rows::first().not(Cell::new(0, 0)), 0, 0), []);
+ assert_eq!(vec_cells(Cell::new(0, 0).not(Cell::new(0, 0)), 2, 3), []);
+ assert_eq!(
+ vec_cells(Rows::first().not(Cell::new(0, 0)), 2, 3),
+ [Entity::Cell(0, 1), Entity::Cell(0, 2)]
+ );
+ assert_eq!(
+ vec_cells(Columns::single(1).not(Rows::single(1)), 3, 3),
+ [Entity::Cell(0, 1), Entity::Cell(2, 1)]
+ );
+ assert_eq!(
+ vec_cells(Rows::single(1).not(Columns::single(1)), 3, 3),
+ [Entity::Cell(1, 0), Entity::Cell(1, 2)]
+ );
+ }
+
+ #[test]
+ fn object_intersect_test() {
+ assert_eq!(
+ vec_cells(Rows::first().intersect(Cell::new(0, 0)), 0, 0),
+ []
+ );
+ assert_eq!(
+ vec_cells(Segment::all().intersect(Rows::single(1)), 2, 3),
+ [Entity::Cell(1, 0), Entity::Cell(1, 1), Entity::Cell(1, 2)]
+ );
+ assert_eq!(
+ vec_cells(Cell::new(0, 0).intersect(Cell::new(0, 0)), 2, 3),
+ [Entity::Cell(0, 0)]
+ );
+ assert_eq!(
+ vec_cells(Rows::first().intersect(Cell::new(0, 0)), 2, 3),
+ [Entity::Cell(0, 0)]
+ );
+ // maybe we somehow shall not limit the rows/columns by the max count?
+ assert_eq!(
+ vec_cells(Rows::single(1).intersect(Columns::single(1)), 2, 1),
+ []
+ );
+ }
+
+ #[test]
+ fn object_inverse_test() {
+ assert_eq!(vec_cells(Segment::all().inverse(), 2, 3), []);
+ assert_eq!(
+ vec_cells(Cell::new(0, 0).inverse(), 2, 3),
+ [
+ Entity::Cell(0, 1),
+ Entity::Cell(0, 2),
+ Entity::Cell(1, 0),
+ Entity::Cell(1, 1),
+ Entity::Cell(1, 2)
+ ]
+ );
+ assert_eq!(
+ vec_cells(Rows::first().inverse(), 2, 3),
+ [Entity::Cell(1, 0), Entity::Cell(1, 1), Entity::Cell(1, 2)]
+ );
+ assert_eq!(vec_cells(Rows::first().inverse(), 0, 0), []);
+ }
+
+ fn vec_cells<O: Object<VecRecords<String>>>(
+ o: O,
+ count_rows: usize,
+ count_cols: usize,
+ ) -> Vec<Entity> {
+ let data = vec![vec![String::default(); count_cols]; count_rows];
+ let records = VecRecords::new(data);
+ o.cells(&records).collect::<Vec<_>>()
+ }
+}
diff --git a/vendor/tabled/src/settings/object/rows.rs b/vendor/tabled/src/settings/object/rows.rs
new file mode 100644
index 000000000..b32e79ad5
--- /dev/null
+++ b/vendor/tabled/src/settings/object/rows.rs
@@ -0,0 +1,213 @@
+use std::ops::{Add, RangeBounds, Sub};
+
+use crate::{
+ grid::config::Entity,
+ grid::records::{ExactRecords, Records},
+ settings::object::{cell::EntityOnce, Object},
+};
+
+use super::util::bounds_to_usize;
+
+/// Row denotes a set of cells on given rows on a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Rows<R> {
+ range: R,
+}
+
+impl<R> Rows<R> {
+ /// Returns a new instance of [`Rows`] for a range of rows.
+ ///
+ /// If the boundaries are exceeded it may panic.
+ pub fn new(range: R) -> Self
+ where
+ R: RangeBounds<usize>,
+ {
+ Self { range }
+ }
+
+ pub(crate) const fn get_range(&self) -> &R {
+ &self.range
+ }
+}
+
+impl Rows<()> {
+ /// Returns a new instance of [`Rows`] with a single row.
+ ///
+ /// If the boundaries are exceeded it may panic.
+ pub const fn single(index: usize) -> Row {
+ Row { index }
+ }
+
+ /// Returns a first row [`Object`].
+ ///
+ /// If the table has 0 rows returns an empty set of cells.
+ pub const fn first() -> FirstRow {
+ FirstRow
+ }
+
+ /// Returns a last row [`Object`].
+ ///
+ /// If the table has 0 rows returns an empty set of cells.
+ pub const fn last() -> LastRow {
+ LastRow
+ }
+}
+
+impl<I, R> Object<I> for Rows<R>
+where
+ R: RangeBounds<usize>,
+ I: ExactRecords,
+{
+ type Iter = RowsIter;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let start = self.range.start_bound();
+ let end = self.range.end_bound();
+ let max = records.count_rows();
+ let (x, y) = bounds_to_usize(start, end, max);
+
+ RowsIter::new(x, y)
+ }
+}
+
+/// A row which is located by an offset from the first row.
+#[derive(Debug, Clone, Copy)]
+pub struct Row {
+ index: usize,
+}
+
+impl<I> Object<I> for Row {
+ type Iter = EntityOnce;
+
+ fn cells(&self, _: &I) -> Self::Iter {
+ EntityOnce::new(Some(Entity::Row(self.index)))
+ }
+}
+
+impl From<Row> for usize {
+ fn from(val: Row) -> Self {
+ val.index
+ }
+}
+
+/// This structure represents the first row of a [`Table`].
+/// It's often contains headers data.
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct FirstRow;
+
+impl<I> Object<I> for FirstRow
+where
+ I: Records + ExactRecords,
+{
+ type Iter = EntityOnce;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ if records.count_columns() == 0 || records.count_rows() == 0 {
+ return EntityOnce::new(None);
+ }
+
+ EntityOnce::new(Some(Entity::Row(0)))
+ }
+}
+
+impl Add<usize> for FirstRow {
+ type Output = Row;
+
+ fn add(self, rhs: usize) -> Self::Output {
+ Row { index: rhs }
+ }
+}
+
+/// This structure represents the last row of a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct LastRow;
+
+impl<I> Object<I> for LastRow
+where
+ I: Records + ExactRecords,
+{
+ type Iter = EntityOnce;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let count_rows = records.count_rows();
+ if records.count_columns() == 0 || count_rows == 0 {
+ return EntityOnce::new(None);
+ }
+
+ let row = if count_rows == 0 { 0 } else { count_rows - 1 };
+
+ EntityOnce::new(Some(Entity::Row(row)))
+ }
+}
+
+impl Sub<usize> for LastRow {
+ type Output = LastRowOffset;
+
+ fn sub(self, rhs: usize) -> Self::Output {
+ LastRowOffset { offset: rhs }
+ }
+}
+
+/// A row which is located by an offset from the last row.
+#[derive(Debug)]
+pub struct LastRowOffset {
+ offset: usize,
+}
+
+impl<I> Object<I> for LastRowOffset
+where
+ I: Records + ExactRecords,
+{
+ type Iter = EntityOnce;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let count_rows = records.count_rows();
+ if records.count_columns() == 0 || count_rows == 0 {
+ return EntityOnce::new(None);
+ }
+
+ let row = if count_rows == 0 { 0 } else { count_rows - 1 };
+ if self.offset > row {
+ return EntityOnce::new(None);
+ }
+
+ let row = row - self.offset;
+ EntityOnce::new(Some(Entity::Row(row)))
+ }
+}
+
+/// An [`Iterator`] which goes goes over all rows of a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct RowsIter {
+ start: usize,
+ end: usize,
+}
+
+impl RowsIter {
+ const fn new(start: usize, end: usize) -> Self {
+ Self { start, end }
+ }
+}
+
+impl Iterator for RowsIter {
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.start >= self.end {
+ return None;
+ }
+
+ let col = self.start;
+ self.start += 1;
+
+ Some(Entity::Row(col))
+ }
+}
diff --git a/vendor/tabled/src/settings/object/segment.rs b/vendor/tabled/src/settings/object/segment.rs
new file mode 100644
index 000000000..9b59ada47
--- /dev/null
+++ b/vendor/tabled/src/settings/object/segment.rs
@@ -0,0 +1,151 @@
+use std::ops::{RangeBounds, RangeFull};
+
+use crate::{
+ grid::config::Entity,
+ grid::records::{ExactRecords, Records},
+ settings::object::{cell::EntityOnce, Object},
+};
+
+use super::util::bounds_to_usize;
+
+/// This structure represents a sub table of [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Segment<C, R> {
+ columns: C,
+ rows: R,
+}
+
+impl Segment<RangeFull, RangeFull> {
+ /// Returns a table segment on which are present all cells.
+ pub fn all() -> SegmentAll {
+ SegmentAll
+ }
+}
+
+impl<C, R> Segment<C, R>
+where
+ C: RangeBounds<usize>,
+ R: RangeBounds<usize>,
+{
+ /// This function builds a [`Segment`].
+ pub fn new(rows: R, columns: C) -> Self {
+ Self { columns, rows }
+ }
+}
+
+impl<I, C, R> Object<I> for Segment<C, R>
+where
+ C: RangeBounds<usize>,
+ R: RangeBounds<usize>,
+ I: Records + ExactRecords,
+{
+ type Iter = SectorIter;
+
+ fn cells(&self, records: &I) -> Self::Iter {
+ let start = self.rows.start_bound();
+ let end = self.rows.end_bound();
+ let max = records.count_rows();
+ let (rows_start, rows_end) = bounds_to_usize(start, end, max);
+
+ let start = self.columns.start_bound();
+ let end = self.columns.end_bound();
+ let max = records.count_columns();
+ let (cols_start, cols_end) = bounds_to_usize(start, end, max);
+
+ SectorIter::new(rows_start, rows_end, cols_start, cols_end)
+ }
+}
+
+/// This is a segment which contains all cells on the table.
+///
+/// Can be created from [`Segment::all`].
+#[derive(Debug)]
+pub struct SegmentAll;
+
+impl<I> Object<I> for SegmentAll {
+ type Iter = EntityOnce;
+
+ fn cells(&self, _: &I) -> Self::Iter {
+ EntityOnce::new(Some(Entity::Global))
+ }
+}
+
+/// An [`Iterator`] which goes goes over all cell in a sector in a [`Table`].
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct SectorIter {
+ iter: SectorCellsIter,
+}
+
+impl SectorIter {
+ const fn new(rows_start: usize, rows_end: usize, cols_start: usize, cols_end: usize) -> Self {
+ Self {
+ iter: SectorCellsIter::new(rows_start, rows_end, cols_start, cols_end),
+ }
+ }
+}
+
+impl Iterator for SectorIter {
+ type Item = Entity;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let (row, col) = self.iter.next()?;
+ Some(Entity::Cell(row, col))
+ }
+}
+
+#[derive(Debug)]
+pub(crate) struct SectorCellsIter {
+ rows_end: usize,
+ cols_start: usize,
+ cols_end: usize,
+ row: usize,
+ col: usize,
+}
+
+impl SectorCellsIter {
+ /// Create an iterator from 1st row to last from 1st col to last.
+ pub(crate) const fn new(
+ rows_start: usize,
+ rows_end: usize,
+ cols_start: usize,
+ cols_end: usize,
+ ) -> Self {
+ Self {
+ rows_end,
+ cols_start,
+ cols_end,
+ row: rows_start,
+ col: cols_start,
+ }
+ }
+}
+
+impl Iterator for SectorCellsIter {
+ type Item = (usize, usize);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.row >= self.rows_end {
+ return None;
+ }
+
+ if self.col >= self.cols_end {
+ return None;
+ }
+
+ let row = self.row;
+ let col = self.col;
+
+ self.col += 1;
+
+ if self.col == self.cols_end {
+ self.row += 1;
+ self.col = self.cols_start;
+ }
+
+ Some((row, col))
+ }
+}
diff --git a/vendor/tabled/src/settings/object/util.rs b/vendor/tabled/src/settings/object/util.rs
new file mode 100644
index 000000000..94ca20e86
--- /dev/null
+++ b/vendor/tabled/src/settings/object/util.rs
@@ -0,0 +1,22 @@
+use std::ops::Bound;
+
+/// Converts a range bound to its indexes.
+pub(super) fn bounds_to_usize(
+ left: Bound<&usize>,
+ right: Bound<&usize>,
+ count_elements: usize,
+) -> (usize, usize) {
+ match (left, right) {
+ (Bound::Included(x), Bound::Included(y)) => (*x, y + 1),
+ (Bound::Included(x), Bound::Excluded(y)) => (*x, *y),
+ (Bound::Included(x), Bound::Unbounded) => (*x, count_elements),
+ (Bound::Unbounded, Bound::Unbounded) => (0, count_elements),
+ (Bound::Unbounded, Bound::Included(y)) => (0, y + 1),
+ (Bound::Unbounded, Bound::Excluded(y)) => (0, *y),
+ (Bound::Excluded(_), Bound::Unbounded)
+ | (Bound::Excluded(_), Bound::Included(_))
+ | (Bound::Excluded(_), Bound::Excluded(_)) => {
+ unreachable!("A start bound can't be excluded")
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/padding/mod.rs b/vendor/tabled/src/settings/padding/mod.rs
new file mode 100644
index 000000000..af343b3cb
--- /dev/null
+++ b/vendor/tabled/src/settings/padding/mod.rs
@@ -0,0 +1,164 @@
+//! This module contains a [`Padding`] setting of a cell on a [`Table`].
+//!
+//! # Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::{Padding, Style, Modify, object::Cell}};
+//!
+//! let table = Table::new("2022".chars())
+//! .with(Style::modern())
+//! .with(Modify::new((2, 0)).with(Padding::new(1, 1, 2, 2)))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "┌──────┐\n",
+//! "│ char │\n",
+//! "├──────┤\n",
+//! "│ 2 │\n",
+//! "├──────┤\n",
+//! "│ │\n",
+//! "│ │\n",
+//! "│ 0 │\n",
+//! "│ │\n",
+//! "│ │\n",
+//! "├──────┤\n",
+//! "│ 2 │\n",
+//! "├──────┤\n",
+//! "│ 2 │\n",
+//! "└──────┘",
+//! ),
+//! );
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use crate::{
+ grid::{
+ color::StaticColor,
+ config::{CompactConfig, CompactMultilineConfig},
+ config::{Indent, Sides},
+ },
+ settings::TableOption,
+};
+
+#[cfg(feature = "std")]
+use crate::grid::{color::AnsiColor, config::ColoredConfig, config::Entity};
+#[cfg(feature = "std")]
+use crate::settings::CellOption;
+
+/// Padding is responsible for a left/right/top/bottom inner indent of a particular cell.
+///
+#[cfg_attr(feature = "std", doc = "```")]
+#[cfg_attr(not(feature = "std"), doc = "```ignore")]
+/// # use tabled::{settings::{Style, Padding, object::Rows, Modify}, Table};
+/// # let data: Vec<&'static str> = Vec::new();
+/// let table = Table::new(&data).with(Modify::new(Rows::single(0)).with(Padding::new(0, 0, 1, 1).fill('>', '<', '^', 'V')));
+/// ```
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Padding<C = StaticColor> {
+ indent: Sides<Indent>,
+ colors: Option<Sides<C>>,
+}
+
+impl Padding {
+ /// Construct's an Padding object.
+ ///
+ /// It uses space(' ') as a default fill character.
+ /// To set a custom character you can use [`Padding::fill`] function.
+ pub const fn new(left: usize, right: usize, top: usize, bottom: usize) -> Self {
+ Self {
+ indent: Sides::new(
+ Indent::spaced(left),
+ Indent::spaced(right),
+ Indent::spaced(top),
+ Indent::spaced(bottom),
+ ),
+ colors: None,
+ }
+ }
+
+ /// Construct's an Padding object with all sides set to 0.
+ ///
+ /// It uses space(' ') as a default fill character.
+ /// To set a custom character you can use [`Padding::fill`] function.
+ pub const fn zero() -> Self {
+ Self::new(0, 0, 0, 0)
+ }
+}
+
+impl<Color> Padding<Color> {
+ /// The function, sets a characters for the padding on an each side.
+ pub const fn fill(mut self, left: char, right: char, top: char, bottom: char) -> Self {
+ self.indent.left.fill = left;
+ self.indent.right.fill = right;
+ self.indent.top.fill = top;
+ self.indent.bottom.fill = bottom;
+ self
+ }
+
+ /// The function, sets a characters for the padding on an each side.
+ pub fn colorize<C>(self, left: C, right: C, top: C, bottom: C) -> Padding<C> {
+ Padding {
+ indent: self.indent,
+ colors: Some(Sides::new(left, right, top, bottom)),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, C> CellOption<R, ColoredConfig> for Padding<C>
+where
+ C: Into<AnsiColor<'static>> + Clone,
+{
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let indent = self.indent;
+ let pad = Sides::new(indent.left, indent.right, indent.top, indent.bottom);
+ cfg.set_padding(entity, pad);
+
+ if let Some(colors) = &self.colors {
+ let pad = Sides::new(
+ Some(colors.left.clone().into()),
+ Some(colors.right.clone().into()),
+ Some(colors.top.clone().into()),
+ Some(colors.bottom.clone().into()),
+ );
+ cfg.set_padding_color(entity, pad);
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, D, C> TableOption<R, D, ColoredConfig> for Padding<C>
+where
+ C: Into<AnsiColor<'static>> + Clone,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ <Self as CellOption<R, ColoredConfig>>::change(self, records, cfg, Entity::Global)
+ }
+}
+
+impl<R, D, C> TableOption<R, D, CompactConfig> for Padding<C>
+where
+ C: Into<StaticColor> + Clone,
+{
+ fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
+ *cfg = cfg.set_padding(self.indent);
+
+ if let Some(c) = self.colors {
+ let colors = Sides::new(c.left.into(), c.right.into(), c.top.into(), c.bottom.into());
+ *cfg = cfg.set_padding_color(colors);
+ }
+ }
+}
+
+impl<R, D, C> TableOption<R, D, CompactMultilineConfig> for Padding<C>
+where
+ C: Into<StaticColor> + Clone,
+{
+ fn change(self, records: &mut R, cfg: &mut CompactMultilineConfig, dimension: &mut D) {
+ self.change(records, cfg.as_mut(), dimension)
+ }
+}
diff --git a/vendor/tabled/src/settings/panel/footer.rs b/vendor/tabled/src/settings/panel/footer.rs
new file mode 100644
index 000000000..8d16481cf
--- /dev/null
+++ b/vendor/tabled/src/settings/panel/footer.rs
@@ -0,0 +1,32 @@
+use crate::{
+ grid::config::ColoredConfig,
+ grid::records::{ExactRecords, Records, RecordsMut, Resizable},
+ settings::TableOption,
+};
+
+use super::Panel;
+
+/// Footer renders a [`Panel`] at the bottom.
+/// See [`Panel`].
+#[derive(Debug)]
+pub struct Footer<S>(S);
+
+impl<S> Footer<S> {
+ /// Creates a new object.
+ pub fn new(text: S) -> Self
+ where
+ S: AsRef<str>,
+ {
+ Self(text)
+ }
+}
+
+impl<S, R, D> TableOption<R, D, ColoredConfig> for Footer<S>
+where
+ S: AsRef<str>,
+ R: Records + ExactRecords + Resizable + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dimension: &mut D) {
+ Panel::horizontal(records.count_rows(), self.0.as_ref()).change(records, cfg, dimension);
+ }
+}
diff --git a/vendor/tabled/src/settings/panel/header.rs b/vendor/tabled/src/settings/panel/header.rs
new file mode 100644
index 000000000..e9d398cb5
--- /dev/null
+++ b/vendor/tabled/src/settings/panel/header.rs
@@ -0,0 +1,32 @@
+use crate::{
+ grid::config::ColoredConfig,
+ grid::records::{ExactRecords, Records, RecordsMut, Resizable},
+ settings::TableOption,
+};
+
+use super::Panel;
+
+/// Header inserts a [`Panel`] at the top.
+/// See [`Panel`].
+#[derive(Debug)]
+pub struct Header<S>(S);
+
+impl<S> Header<S> {
+ /// Creates a new object.
+ pub fn new(text: S) -> Self
+ where
+ S: AsRef<str>,
+ {
+ Self(text)
+ }
+}
+
+impl<S, R, D> TableOption<R, D, ColoredConfig> for Header<S>
+where
+ S: AsRef<str>,
+ R: Records + ExactRecords + Resizable + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dimension: &mut D) {
+ Panel::horizontal(0, self.0.as_ref()).change(records, cfg, dimension);
+ }
+}
diff --git a/vendor/tabled/src/settings/panel/horizontal_panel.rs b/vendor/tabled/src/settings/panel/horizontal_panel.rs
new file mode 100644
index 000000000..d5871d720
--- /dev/null
+++ b/vendor/tabled/src/settings/panel/horizontal_panel.rs
@@ -0,0 +1,80 @@
+use crate::{
+ grid::config::{ColoredConfig, SpannedConfig},
+ grid::records::{ExactRecords, Records, RecordsMut, Resizable},
+ settings::TableOption,
+};
+
+/// A horizontal/column span from 0 to a count rows.
+#[derive(Debug)]
+pub struct HorizontalPanel<S> {
+ text: S,
+ row: usize,
+}
+
+impl<S> HorizontalPanel<S> {
+ /// Creates a new horizontal panel.
+ pub fn new(row: usize, text: S) -> Self {
+ Self { row, text }
+ }
+}
+
+impl<S, R, D> TableOption<R, D, ColoredConfig> for HorizontalPanel<S>
+where
+ S: AsRef<str>,
+ R: Records + ExactRecords + Resizable + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ if self.row > count_rows {
+ return;
+ }
+
+ let is_intersect_vertical_span = (0..records.count_columns())
+ .any(|col| cfg.is_cell_covered_by_row_span((self.row, col)));
+ if is_intersect_vertical_span {
+ return;
+ }
+
+ move_rows_aside(records, self.row);
+ move_row_spans(cfg, self.row);
+
+ let text = self.text.as_ref().to_owned();
+ records.set((self.row, 0), text);
+
+ cfg.set_column_span((self.row, 0), count_cols);
+ }
+}
+
+fn move_rows_aside<R: ExactRecords + Resizable>(records: &mut R, row: usize) {
+ records.push_row();
+
+ let count_rows = records.count_rows();
+
+ let shift_count = count_rows - row;
+ for i in 1..shift_count {
+ let row = count_rows - i;
+ records.swap_row(row, row - 1);
+ }
+}
+
+fn move_row_spans(cfg: &mut SpannedConfig, target_row: usize) {
+ for ((row, col), span) in cfg.get_column_spans() {
+ if row < target_row {
+ continue;
+ }
+
+ cfg.set_column_span((row, col), 1);
+ cfg.set_column_span((row + 1, col), span);
+ }
+
+ for ((row, col), span) in cfg.get_row_spans() {
+ if row < target_row {
+ continue;
+ }
+
+ cfg.set_row_span((row, col), 1);
+ cfg.set_row_span((row + 1, col), span);
+ }
+}
diff --git a/vendor/tabled/src/settings/panel/mod.rs b/vendor/tabled/src/settings/panel/mod.rs
new file mode 100644
index 000000000..e4e819b6c
--- /dev/null
+++ b/vendor/tabled/src/settings/panel/mod.rs
@@ -0,0 +1,127 @@
+//! This module contains primitives to create a spread row.
+//! Ultimately it is a cell with a span set to a number of columns on the [`Table`].
+//!
+//! You can use a [`Span`] to set a custom span.
+//!
+//! # Example
+//!
+//! ```
+//! use tabled::{Table, settings::Panel};
+//!
+//! let data = [[1, 2, 3], [4, 5, 6]];
+//!
+//! let table = Table::new(data)
+//! .with(Panel::vertical(1, "S\np\nl\ni\nt"))
+//! .with(Panel::header("Numbers"))
+//! .to_string();
+//!
+//! println!("{}", table);
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "+---+---+---+---+\n",
+//! "| Numbers |\n",
+//! "+---+---+---+---+\n",
+//! "| 0 | S | 1 | 2 |\n",
+//! "+---+ p +---+---+\n",
+//! "| 1 | l | 2 | 3 |\n",
+//! "+---+ i +---+---+\n",
+//! "| 4 | t | 5 | 6 |\n",
+//! "+---+---+---+---+",
+//! )
+//! )
+//! ```
+//!
+//! [`Table`]: crate::Table
+//! [`Span`]: crate::settings::span::Span
+
+mod footer;
+mod header;
+mod horizontal_panel;
+mod vertical_panel;
+
+pub use footer::Footer;
+pub use header::Header;
+pub use horizontal_panel::HorizontalPanel;
+pub use vertical_panel::VerticalPanel;
+
+/// Panel allows to add a Row which has 1 continues Cell to a [`Table`].
+///
+/// See `examples/panel.rs`.
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Panel;
+
+impl Panel {
+ /// Creates an empty vertical row at given index.
+ ///
+ /// ```
+ /// use tabled::{settings::Panel, Table};
+ ///
+ /// let data = [[1, 2, 3], [4, 5, 6]];
+ ///
+ /// let table = Table::new(data)
+ /// .with(Panel::vertical(1, "Tabled Releases"))
+ /// .to_string();
+ ///
+ /// println!("{}", table);
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "+---+-----------------+---+---+\n",
+ /// "| 0 | Tabled Releases | 1 | 2 |\n",
+ /// "+---+ +---+---+\n",
+ /// "| 1 | | 2 | 3 |\n",
+ /// "+---+ +---+---+\n",
+ /// "| 4 | | 5 | 6 |\n",
+ /// "+---+-----------------+---+---+",
+ /// )
+ /// )
+ /// ```
+ pub fn vertical<S: AsRef<str>>(column: usize, text: S) -> VerticalPanel<S> {
+ VerticalPanel::new(column, text)
+ }
+
+ /// Creates an empty horizontal row at given index.
+ ///
+ /// ```
+ /// use tabled::{Table, settings::Panel};
+ ///
+ /// let data = [[1, 2, 3], [4, 5, 6]];
+ ///
+ /// let table = Table::new(data)
+ /// .with(Panel::vertical(1, ""))
+ /// .to_string();
+ ///
+ /// println!("{}", table);
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "+---+--+---+---+\n",
+ /// "| 0 | | 1 | 2 |\n",
+ /// "+---+ +---+---+\n",
+ /// "| 1 | | 2 | 3 |\n",
+ /// "+---+ +---+---+\n",
+ /// "| 4 | | 5 | 6 |\n",
+ /// "+---+--+---+---+",
+ /// )
+ /// )
+ /// ```
+ pub fn horizontal<S: AsRef<str>>(row: usize, text: S) -> HorizontalPanel<S> {
+ HorizontalPanel::new(row, text)
+ }
+
+ /// Creates an horizontal row at first row.
+ pub fn header<S: AsRef<str>>(text: S) -> Header<S> {
+ Header::new(text)
+ }
+
+ /// Creates an horizontal row at last row.
+ pub fn footer<S: AsRef<str>>(text: S) -> Footer<S> {
+ Footer::new(text)
+ }
+}
diff --git a/vendor/tabled/src/settings/panel/vertical_panel.rs b/vendor/tabled/src/settings/panel/vertical_panel.rs
new file mode 100644
index 000000000..ddc0a562b
--- /dev/null
+++ b/vendor/tabled/src/settings/panel/vertical_panel.rs
@@ -0,0 +1,83 @@
+use crate::{
+ grid::config::{ColoredConfig, SpannedConfig},
+ grid::records::{ExactRecords, Records, RecordsMut, Resizable},
+ settings::TableOption,
+};
+
+/// A vertical/row span from 0 to a count columns.
+#[derive(Debug)]
+pub struct VerticalPanel<S> {
+ text: S,
+ col: usize,
+}
+
+impl<S> VerticalPanel<S> {
+ /// Creates a new vertical panel.
+ pub fn new(col: usize, text: S) -> Self
+ where
+ S: AsRef<str>,
+ {
+ Self { text, col }
+ }
+}
+
+impl<S, R, D> TableOption<R, D, ColoredConfig> for VerticalPanel<S>
+where
+ S: AsRef<str>,
+ R: Records + ExactRecords + Resizable + RecordsMut<String>,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ if self.col > count_cols {
+ return;
+ }
+
+ let is_intersect_horizontal_span = (0..=records.count_rows())
+ .any(|row| cfg.is_cell_covered_by_column_span((row, self.col)));
+
+ if is_intersect_horizontal_span {
+ return;
+ }
+
+ move_columns_aside(records, self.col);
+ move_column_spans(cfg, self.col);
+
+ let text = self.text.as_ref().to_owned();
+ records.set((0, self.col), text);
+
+ cfg.set_row_span((0, self.col), count_rows);
+ }
+}
+
+fn move_columns_aside<R: Records + Resizable>(records: &mut R, column: usize) {
+ records.push_column();
+
+ let count_columns = records.count_columns();
+ let shift_count = count_columns - column;
+ for i in 1..shift_count {
+ let col = count_columns - i;
+ records.swap_column(col, col - 1);
+ }
+}
+
+fn move_column_spans(cfg: &mut SpannedConfig, target_column: usize) {
+ for ((row, col), span) in cfg.get_column_spans() {
+ if col < target_column {
+ continue;
+ }
+
+ cfg.set_column_span((row, col), 1);
+ cfg.set_column_span((row, col + 1), span);
+ }
+
+ for ((row, col), span) in cfg.get_row_spans() {
+ if col < target_column {
+ continue;
+ }
+
+ cfg.set_row_span((row, col), 1);
+ cfg.set_row_span((row, col + 1), span);
+ }
+}
diff --git a/vendor/tabled/src/settings/peaker/mod.rs b/vendor/tabled/src/settings/peaker/mod.rs
new file mode 100644
index 000000000..a49796b02
--- /dev/null
+++ b/vendor/tabled/src/settings/peaker/mod.rs
@@ -0,0 +1,94 @@
+//! The module contains [`Peaker`] trait and its implementations to be used in [`Height`] and [`Width`].
+//!
+//! [`Width`]: crate::settings::width::Width
+//! [`Height`]: crate::settings::height::Height
+
+/// A strategy of width function.
+/// It determines the order how the function is applied.
+pub trait Peaker {
+ /// Creates a new instance.
+ fn create() -> Self;
+ /// This function returns a column index which will be changed.
+ /// Or `None` if no changes are necessary.
+ fn peak(&mut self, min_widths: &[usize], widths: &[usize]) -> Option<usize>;
+}
+
+/// A Peaker which goes over column 1 by 1.
+#[derive(Debug, Default, Clone)]
+pub struct PriorityNone {
+ i: usize,
+}
+
+impl Peaker for PriorityNone {
+ fn create() -> Self {
+ Self { i: 0 }
+ }
+
+ fn peak(&mut self, _: &[usize], widths: &[usize]) -> Option<usize> {
+ let mut i = self.i;
+ let mut count_empty = 0;
+ while widths[i] == 0 {
+ i += 1;
+ if i >= widths.len() {
+ i = 0;
+ }
+
+ count_empty += 1;
+ if count_empty == widths.len() {
+ return None;
+ }
+ }
+
+ let col = i;
+
+ i += 1;
+ if i >= widths.len() {
+ i = 0;
+ }
+
+ self.i = i;
+
+ Some(col)
+ }
+}
+
+/// A Peaker which goes over the biggest column first.
+#[derive(Debug, Default, Clone)]
+pub struct PriorityMax;
+
+impl Peaker for PriorityMax {
+ fn create() -> Self {
+ Self
+ }
+
+ fn peak(&mut self, _: &[usize], widths: &[usize]) -> Option<usize> {
+ let col = (0..widths.len()).max_by_key(|&i| widths[i]).unwrap();
+ if widths[col] == 0 {
+ None
+ } else {
+ Some(col)
+ }
+ }
+}
+
+/// A Peaker which goes over the smallest column first.
+#[derive(Debug, Default, Clone)]
+pub struct PriorityMin;
+
+impl Peaker for PriorityMin {
+ fn create() -> Self {
+ Self
+ }
+
+ fn peak(&mut self, min_widths: &[usize], widths: &[usize]) -> Option<usize> {
+ let col = (0..widths.len())
+ .filter(|&i| min_widths.is_empty() || widths[i] > min_widths[i])
+ .min_by_key(|&i| widths[i])
+ .unwrap();
+ if widths[col] == 0 {
+ None
+ } else {
+ Some(col)
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/rotate/mod.rs b/vendor/tabled/src/settings/rotate/mod.rs
new file mode 100644
index 000000000..426417190
--- /dev/null
+++ b/vendor/tabled/src/settings/rotate/mod.rs
@@ -0,0 +1,155 @@
+//! This module contains a [`Rotate`] primitive which can be used in order to rotate [`Table`].
+//!
+//! It's also possible to transpose the table at the point of construction.
+//! See [`Builder::index`].
+//!
+//! # Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::Rotate};
+//!
+//! let data = [[1, 2, 3], [4, 5, 6]];
+//!
+//! let table = Table::new(data).with(Rotate::Left).to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "+---+---+---+\n",
+//! "| 2 | 3 | 6 |\n",
+//! "+---+---+---+\n",
+//! "| 1 | 2 | 5 |\n",
+//! "+---+---+---+\n",
+//! "| 0 | 1 | 4 |\n",
+//! "+---+---+---+",
+//! )
+//! );
+//! ```
+//!
+//! [`Table`]: crate::Table
+//! [`Builder::index`]: crate::builder::Builder::index
+
+// use core::cmp::max;
+use core::cmp::max;
+
+use crate::{
+ grid::records::{ExactRecords, Records, Resizable},
+ settings::TableOption,
+};
+
+/// Rotate can be used to rotate a table by 90 degrees.
+#[derive(Debug)]
+pub enum Rotate {
+ /// Rotate [`Table`] to the left.
+ ///
+ /// [`Table`]: crate::Table
+ Left,
+ /// Rotate [`Table`] to the right.
+ ///
+ /// [`Table`]: crate::Table
+ Right,
+ /// Rotate [`Table`] to the top.
+ ///
+ /// So the top becomes the bottom.
+ ///
+ /// [`Table`]: crate::Table
+ Top,
+ /// Rotate [`Table`] to the bottom.
+ ///
+ /// So the top becomes the bottom.
+ ///
+ /// [`Table`]: crate::Table
+ Bottom,
+}
+
+impl<R, D, C> TableOption<R, D, C> for Rotate
+where
+ R: Records + ExactRecords + Resizable,
+{
+ fn change(self, records: &mut R, _: &mut C, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ match self {
+ Self::Left => {
+ let size = max(count_rows, count_cols);
+
+ {
+ for _ in count_rows..size {
+ records.push_row();
+ }
+
+ for _ in count_cols..size {
+ records.push_column();
+ }
+ }
+
+ for col in 0..size {
+ for row in col..size {
+ records.swap((col, row), (row, col));
+ }
+ }
+
+ for row in 0..count_cols / 2 {
+ records.swap_row(row, count_cols - row - 1);
+ }
+
+ {
+ for (shift, row) in (count_rows..size).enumerate() {
+ let row = row - shift;
+ records.remove_column(row);
+ }
+
+ for (shift, col) in (count_cols..size).enumerate() {
+ let col = col - shift;
+ records.remove_row(col);
+ }
+ }
+ }
+ Self::Right => {
+ let size = max(count_rows, count_cols);
+
+ {
+ for _ in count_rows..size {
+ records.push_row();
+ }
+
+ for _ in count_cols..size {
+ records.push_column();
+ }
+ }
+
+ for col in 0..size {
+ for row in col..size {
+ records.swap((col, row), (row, col));
+ }
+ }
+
+ for col in 0..count_rows / 2 {
+ records.swap_column(col, count_rows - col - 1);
+ }
+
+ {
+ for (shift, row) in (count_rows..size).enumerate() {
+ let row = row - shift;
+ records.remove_column(row);
+ }
+
+ for (shift, col) in (count_cols..size).enumerate() {
+ let col = col - shift;
+ records.remove_row(col);
+ }
+ }
+ }
+ Self::Bottom | Self::Top => {
+ for row in 0..count_rows / 2 {
+ for col in 0..count_cols {
+ let last_row = count_rows - row - 1;
+ records.swap((last_row, col), (row, col));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/settings_list.rs b/vendor/tabled/src/settings/settings_list.rs
new file mode 100644
index 000000000..dc8aec179
--- /dev/null
+++ b/vendor/tabled/src/settings/settings_list.rs
@@ -0,0 +1,71 @@
+use crate::settings::TableOption;
+
+#[cfg(feature = "std")]
+use crate::grid::config::Entity;
+#[cfg(feature = "std")]
+use crate::settings::CellOption;
+
+/// Settings is a combinator of [`TableOption`]s.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Settings<A = EmptySettings, B = EmptySettings>(A, B);
+
+impl Default for Settings<EmptySettings, EmptySettings> {
+ fn default() -> Self {
+ Self(EmptySettings, EmptySettings)
+ }
+}
+
+impl Settings<(), ()> {
+ /// Creates an empty list.
+ pub const fn empty() -> Settings<EmptySettings, EmptySettings> {
+ Settings(EmptySettings, EmptySettings)
+ }
+}
+
+impl<A, B> Settings<A, B> {
+ /// Creates a new combinator.
+ pub const fn new(settings1: A, settings2: B) -> Settings<A, B> {
+ Settings(settings1, settings2)
+ }
+
+ /// Add an option to a combinator.
+ pub const fn with<C>(self, settings: C) -> Settings<Self, C> {
+ Settings(self, settings)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, C, A, B> CellOption<R, C> for Settings<A, B>
+where
+ A: CellOption<R, C>,
+ B: CellOption<R, C>,
+{
+ fn change(self, records: &mut R, cfg: &mut C, entity: Entity) {
+ self.0.change(records, cfg, entity);
+ self.1.change(records, cfg, entity);
+ }
+}
+
+impl<R, D, C, A, B> TableOption<R, D, C> for Settings<A, B>
+where
+ A: TableOption<R, D, C>,
+ B: TableOption<R, D, C>,
+{
+ fn change(self, records: &mut R, cfg: &mut C, dims: &mut D) {
+ self.0.change(records, cfg, dims);
+ self.1.change(records, cfg, dims);
+ }
+}
+
+/// A marker structure to be able to create an empty [`Settings`].
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct EmptySettings;
+
+#[cfg(feature = "std")]
+impl<R, C> CellOption<R, C> for EmptySettings {
+ fn change(self, _: &mut R, _: &mut C, _: Entity) {}
+}
+
+impl<R, D, C> TableOption<R, D, C> for EmptySettings {
+ fn change(self, _: &mut R, _: &mut C, _: &mut D) {}
+}
diff --git a/vendor/tabled/src/settings/shadow/mod.rs b/vendor/tabled/src/settings/shadow/mod.rs
new file mode 100644
index 000000000..6b8ff4861
--- /dev/null
+++ b/vendor/tabled/src/settings/shadow/mod.rs
@@ -0,0 +1,195 @@
+//! This module contains a [`Shadow`] option for a [`Table`].
+//!
+//! # Example
+//!
+//! ```
+//! use tabled::{Table, settings::{Shadow, Style}};
+//!
+//! let data = vec!["Hello", "World", "!"];
+//!
+//! let table = Table::new(data)
+//! .with(Style::markdown())
+//! .with(Shadow::new(1))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "| &str | \n",
+//! "|-------|▒\n",
+//! "| Hello |▒\n",
+//! "| World |▒\n",
+//! "| ! |▒\n",
+//! " ▒▒▒▒▒▒▒▒▒",
+//! )
+//! );
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use crate::{
+ grid::color::AnsiColor,
+ grid::config::{ColoredConfig, Indent, Offset, Sides},
+ settings::{color::Color, TableOption},
+};
+
+/// The structure represents a shadow of a table.
+///
+/// NOTICE: It uses [`Margin`] therefore it often can't be combined.
+///
+/// [`Margin`]: crate::settings::Margin
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Shadow {
+ c: char,
+ size: usize,
+ size_offset: usize,
+ direction: Sides<bool>,
+ color: Option<Color>,
+}
+
+impl Shadow {
+ /// A default fill character to be used.
+ pub const DEFAULT_FILL: char = '▒';
+
+ /// Construct's an [`Shadow`] object with default fill [`Shadow::DEFAULT_FILL`].
+ ///
+ /// It uses space(' ') as a default fill character.
+ /// To set a custom character you can use [`Self::set_fill`] function.
+ pub fn new(size: usize) -> Self {
+ Self {
+ c: Self::DEFAULT_FILL,
+ size,
+ size_offset: 1,
+ direction: Sides::new(false, true, false, true),
+ color: None,
+ }
+ }
+
+ /// The function, sets a characters for the [`Shadow`] to be used.
+ pub fn set_fill(mut self, c: char) -> Self {
+ self.c = c;
+ self
+ }
+
+ /// Set an offset value (default is '1').
+ pub fn set_offset(mut self, size: usize) -> Self {
+ self.size_offset = size;
+ self
+ }
+
+ /// Switch shadow to top.
+ pub fn set_top(mut self) -> Self {
+ self.direction.top = true;
+ self.direction.bottom = false;
+ self
+ }
+
+ /// Switch shadow to bottom.
+ pub fn set_bottom(mut self) -> Self {
+ self.direction.bottom = true;
+ self.direction.top = false;
+ self
+ }
+
+ /// Switch shadow to left.
+ pub fn set_left(mut self) -> Self {
+ self.direction.left = true;
+ self.direction.right = false;
+ self
+ }
+
+ /// Switch shadow to right.
+ pub fn set_right(mut self) -> Self {
+ self.direction.right = true;
+ self.direction.left = false;
+ self
+ }
+
+ /// Sets a color for a shadow.
+ pub fn set_color(mut self, color: Color) -> Self {
+ self.color = Some(color);
+ self
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for Shadow {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ set_margin(cfg, self.size, self.c, &self.direction);
+ set_margin_offset(cfg, self.size_offset, &self.direction);
+
+ if let Some(color) = &self.color {
+ set_margin_color(cfg, color.clone().into(), &self.direction);
+ }
+ }
+}
+
+fn set_margin(cfg: &mut ColoredConfig, size: usize, c: char, direction: &Sides<bool>) {
+ let mut margin: Sides<Indent> = Sides::default();
+ if direction.top {
+ margin.top.size = size;
+ margin.top.fill = c;
+ }
+
+ if direction.bottom {
+ margin.bottom.size = size;
+ margin.bottom.fill = c;
+ }
+
+ if direction.left {
+ margin.left.size = size;
+ margin.left.fill = c;
+ }
+
+ if direction.right {
+ margin.right.size = size;
+ margin.right.fill = c;
+ }
+
+ cfg.set_margin(margin);
+}
+
+fn set_margin_offset(cfg: &mut ColoredConfig, size: usize, direction: &Sides<bool>) {
+ let mut margin = Sides::filled(Offset::Begin(0));
+ if direction.right && direction.bottom {
+ margin.bottom = Offset::Begin(size);
+ margin.right = Offset::Begin(size);
+ }
+
+ if direction.right && direction.top {
+ margin.top = Offset::Begin(size);
+ margin.right = Offset::End(size);
+ }
+
+ if direction.left && direction.bottom {
+ margin.bottom = Offset::End(size);
+ margin.left = Offset::Begin(size);
+ }
+
+ if direction.left && direction.top {
+ margin.top = Offset::End(size);
+ margin.left = Offset::End(size);
+ }
+
+ cfg.set_margin_offset(margin);
+}
+
+fn set_margin_color(cfg: &mut ColoredConfig, color: AnsiColor<'static>, direction: &Sides<bool>) {
+ let mut margin: Sides<Option<AnsiColor<'static>>> = Sides::default();
+ if direction.right {
+ margin.right = Some(color.clone());
+ }
+
+ if direction.top {
+ margin.top = Some(color.clone());
+ }
+
+ if direction.left {
+ margin.left = Some(color.clone());
+ }
+
+ if direction.bottom {
+ margin.bottom = Some(color.clone());
+ }
+
+ cfg.set_margin_color(margin);
+}
diff --git a/vendor/tabled/src/settings/span/column.rs b/vendor/tabled/src/settings/span/column.rs
new file mode 100644
index 000000000..50af64c23
--- /dev/null
+++ b/vendor/tabled/src/settings/span/column.rs
@@ -0,0 +1,125 @@
+use crate::{
+ grid::{
+ config::{ColoredConfig, Entity, Position, SpannedConfig},
+ records::{ExactRecords, Records},
+ },
+ settings::CellOption,
+};
+
+/// Columns (Vertical) span.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct ColumnSpan {
+ size: usize,
+}
+
+impl ColumnSpan {
+ /// Creates a new column (vertical) span.
+ pub fn new(size: usize) -> Self {
+ Self { size }
+ }
+
+ /// Creates a new column (vertical) span with a maximux value possible.
+ pub fn max() -> Self {
+ Self::new(usize::MAX)
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for ColumnSpan
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ set_col_spans(cfg, self.size, entity, (count_rows, count_cols));
+ remove_false_spans(cfg);
+ }
+}
+
+fn set_col_spans(cfg: &mut SpannedConfig, span: usize, entity: Entity, shape: (usize, usize)) {
+ for pos in entity.iter(shape.0, shape.1) {
+ if !is_valid_pos(pos, shape) {
+ continue;
+ }
+
+ let mut span = span;
+ if !is_column_span_valid(pos.1, span, shape.1) {
+ span = shape.1 - pos.1;
+ }
+
+ if span_has_intersections(cfg, pos, span) {
+ continue;
+ }
+
+ set_span_column(cfg, pos, span);
+ }
+}
+
+fn set_span_column(cfg: &mut SpannedConfig, pos: (usize, usize), span: usize) {
+ if span == 0 {
+ let (row, col) = pos;
+ if col == 0 {
+ return;
+ }
+
+ if let Some(closecol) = closest_visible(cfg, (row, col - 1)) {
+ let span = col + 1 - closecol;
+ cfg.set_column_span((row, closecol), span);
+ }
+ }
+
+ cfg.set_column_span(pos, span);
+}
+
+fn closest_visible(cfg: &SpannedConfig, mut pos: Position) -> Option<usize> {
+ loop {
+ if cfg.is_cell_visible(pos) {
+ return Some(pos.1);
+ }
+
+ if pos.1 == 0 {
+ return None;
+ }
+
+ pos.1 -= 1;
+ }
+}
+
+fn is_column_span_valid(col: usize, span: usize, count_cols: usize) -> bool {
+ span + col <= count_cols
+}
+
+fn is_valid_pos((row, col): Position, (count_rows, count_cols): (usize, usize)) -> bool {
+ row < count_rows && col < count_cols
+}
+
+fn span_has_intersections(cfg: &SpannedConfig, (row, col): Position, span: usize) -> bool {
+ for col in col..col + span {
+ if !cfg.is_cell_visible((row, col)) {
+ return true;
+ }
+ }
+
+ false
+}
+
+fn remove_false_spans(cfg: &mut SpannedConfig) {
+ for (pos, _) in cfg.get_column_spans() {
+ if cfg.is_cell_visible(pos) {
+ continue;
+ }
+
+ cfg.set_row_span(pos, 1);
+ cfg.set_column_span(pos, 1);
+ }
+
+ for (pos, _) in cfg.get_row_spans() {
+ if cfg.is_cell_visible(pos) {
+ continue;
+ }
+
+ cfg.set_row_span(pos, 1);
+ cfg.set_column_span(pos, 1);
+ }
+}
diff --git a/vendor/tabled/src/settings/span/mod.rs b/vendor/tabled/src/settings/span/mod.rs
new file mode 100644
index 000000000..1139a2b99
--- /dev/null
+++ b/vendor/tabled/src/settings/span/mod.rs
@@ -0,0 +1,68 @@
+//! This module contains a [`Span`] settings, it helps to
+//! make a cell take more space then it generally takes.
+//!
+//! # Example
+//!
+//! ```
+//! use tabled::{settings::{Span, Modify}, Table};
+//!
+//! let data = [[1, 2, 3], [4, 5, 6]];
+//!
+//! let table = Table::new(data)
+//! .with(Modify::new((2, 0)).with(Span::column(2)))
+//! .with(Modify::new((0, 1)).with(Span::column(2)))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "+---+---+---+\n",
+//! "| 0 | 1 |\n",
+//! "+---+---+---+\n",
+//! "| 1 | 2 | 3 |\n",
+//! "+---+---+---+\n",
+//! "| 4 | 6 |\n",
+//! "+---+---+---+",
+//! )
+//! )
+//! ```
+
+mod column;
+mod row;
+
+pub use column::ColumnSpan;
+pub use row::RowSpan;
+
+/// Span represent a horizontal/column span setting for any cell on a [`Table`].
+///
+/// It will be ignored if:
+/// - cell position is out of scope
+/// - size is bigger then the total number of columns.
+/// - size is bigger then the total number of rows.
+///
+/// ```rust,no_run
+/// # use tabled::{Table, settings::{Style, Span, Modify, object::Columns}};
+/// # let data: Vec<&'static str> = Vec::new();
+/// let table = Table::new(&data)
+/// .with(Modify::new(Columns::single(0)).with(Span::column(2)));
+/// ```
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Span;
+
+impl Span {
+ /// New constructs a horizontal/column [`Span`].
+ ///
+ /// If size is bigger then the total number of columns it will be ignored.
+ pub fn column(size: usize) -> ColumnSpan {
+ ColumnSpan::new(size)
+ }
+
+ /// New constructs a vertical/row [`Span`].
+ ///
+ /// If size is bigger then the total number of rows it will be ignored.
+ pub fn row(size: usize) -> RowSpan {
+ RowSpan::new(size)
+ }
+}
diff --git a/vendor/tabled/src/settings/span/row.rs b/vendor/tabled/src/settings/span/row.rs
new file mode 100644
index 000000000..68673f7a2
--- /dev/null
+++ b/vendor/tabled/src/settings/span/row.rs
@@ -0,0 +1,126 @@
+use crate::{
+ grid::{
+ config::{ColoredConfig, Entity, Position, SpannedConfig},
+ records::{ExactRecords, Records},
+ },
+ settings::CellOption,
+};
+
+/// Row (vertical) span.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct RowSpan {
+ size: usize,
+}
+
+impl RowSpan {
+ /// Creates a new row (vertical) span.
+ pub const fn new(size: usize) -> Self {
+ Self { size }
+ }
+
+ /// Creates a new row (vertical) span with a maximux value possible.
+ pub const fn max() -> Self {
+ Self::new(usize::MAX)
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for RowSpan
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_cols = records.count_columns();
+
+ set_row_spans(cfg, self.size, entity, (count_rows, count_cols));
+ remove_false_spans(cfg);
+ }
+}
+
+fn set_row_spans(cfg: &mut SpannedConfig, span: usize, entity: Entity, shape: (usize, usize)) {
+ for pos in entity.iter(shape.0, shape.1) {
+ if !is_valid_pos(pos, shape) {
+ continue;
+ }
+
+ let mut span = span;
+ if !is_row_span_valid(pos.0, span, shape.0) {
+ span = shape.0 - pos.0;
+ }
+
+ if span_has_intersections(cfg, pos, span) {
+ continue;
+ }
+
+ set_span_row(cfg, pos, span);
+ }
+}
+
+fn set_span_row(cfg: &mut SpannedConfig, pos: (usize, usize), span: usize) {
+ if span == 0 {
+ let (row, col) = pos;
+ if row == 0 {
+ return;
+ }
+
+ if let Some(closerow) = closest_visible_row(cfg, (row - 1, col)) {
+ let span = row + 1 - closerow;
+ cfg.set_row_span((closerow, col), span);
+ }
+ }
+
+ cfg.set_row_span(pos, span);
+}
+
+fn closest_visible_row(cfg: &SpannedConfig, mut pos: Position) -> Option<usize> {
+ loop {
+ if cfg.is_cell_visible(pos) {
+ return Some(pos.0);
+ }
+
+ if pos.0 == 0 {
+ // can happen if we have a above horizontal spanned cell
+ return None;
+ }
+
+ pos.0 -= 1;
+ }
+}
+
+fn is_row_span_valid(row: usize, span: usize, count_rows: usize) -> bool {
+ span + row <= count_rows
+}
+
+fn is_valid_pos((row, col): Position, (count_rows, count_cols): (usize, usize)) -> bool {
+ row < count_rows && col < count_cols
+}
+
+fn span_has_intersections(cfg: &SpannedConfig, (row, col): Position, span: usize) -> bool {
+ for row in row..row + span {
+ if !cfg.is_cell_visible((row, col)) {
+ return true;
+ }
+ }
+
+ false
+}
+
+fn remove_false_spans(cfg: &mut SpannedConfig) {
+ for (pos, _) in cfg.get_column_spans() {
+ if cfg.is_cell_visible(pos) {
+ continue;
+ }
+
+ cfg.set_row_span(pos, 1);
+ cfg.set_column_span(pos, 1);
+ }
+
+ for (pos, _) in cfg.get_row_spans() {
+ if cfg.is_cell_visible(pos) {
+ continue;
+ }
+
+ cfg.set_row_span(pos, 1);
+ cfg.set_column_span(pos, 1);
+ }
+}
diff --git a/vendor/tabled/src/settings/split/mod.rs b/vendor/tabled/src/settings/split/mod.rs
new file mode 100644
index 000000000..32f820aef
--- /dev/null
+++ b/vendor/tabled/src/settings/split/mod.rs
@@ -0,0 +1,420 @@
+//! This module contains a [`Split`] setting which is used to
+//! format the cells of a [`Table`] by a provided index, direction, behavior, and display preference.
+//!
+//! [`Table`]: crate::Table
+
+use core::ops::Range;
+
+use papergrid::{config::Position, records::PeekableRecords};
+
+use crate::grid::records::{ExactRecords, Records, Resizable};
+
+use super::TableOption;
+
+#[derive(Debug, Clone, Copy)]
+enum Direction {
+ Column,
+ Row,
+}
+
+#[derive(Debug, Clone, Copy)]
+enum Behavior {
+ Concat,
+ Zip,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+enum Display {
+ Clean,
+ Retain,
+}
+
+/// Returns a new [`Table`] formatted with several optional parameters.
+///
+/// The required index parameter determines how many columns/rows a table will be redistributed into.
+///
+/// - index
+/// - direction
+/// - behavior
+/// - display
+///
+/// # Example
+///
+/// ```rust,no_run
+/// use std::iter::FromIterator;
+/// use tabled::{
+/// settings::split::Split,
+/// Table,
+/// };
+///
+/// let mut table = Table::from_iter(['a'..='z']);
+/// let table = table.with(Split::column(4)).to_string();
+///
+/// assert_eq!(table, "+---+---+---+---+\n\
+/// | a | b | c | d |\n\
+/// +---+---+---+---+\n\
+/// | e | f | g | h |\n\
+/// +---+---+---+---+\n\
+/// | i | j | k | l |\n\
+/// +---+---+---+---+\n\
+/// | m | n | o | p |\n\
+/// +---+---+---+---+\n\
+/// | q | r | s | t |\n\
+/// +---+---+---+---+\n\
+/// | u | v | w | x |\n\
+/// +---+---+---+---+\n\
+/// | y | z | | |\n\
+/// +---+---+---+---+")
+/// ```
+///
+/// [`Table`]: crate::Table
+#[derive(Debug, Clone, Copy)]
+pub struct Split {
+ direction: Direction,
+ behavior: Behavior,
+ display: Display,
+ index: usize,
+}
+
+impl Split {
+ /// Returns a new [`Table`] split on the column at the provided index.
+ ///
+ /// The column found at that index becomes the new right-most column in the returned table.
+ /// Columns found beyond the index are redistributed into the table based on other defined
+ /// parameters.
+ ///
+ /// ```rust,no_run
+ /// # use tabled::settings::split::Split;
+ /// Split::column(4);
+ /// ```
+ ///
+ /// [`Table`]: crate::Table
+ pub fn column(index: usize) -> Self {
+ Split {
+ direction: Direction::Column,
+ behavior: Behavior::Zip,
+ display: Display::Clean,
+ index,
+ }
+ }
+
+ /// Returns a new [`Table`] split on the row at the provided index.
+ ///
+ /// The row found at that index becomes the new bottom row in the returned table.
+ /// Rows found beyond the index are redistributed into the table based on other defined
+ /// parameters.
+ ///
+ /// ```rust,no_run
+ /// # use tabled::settings::split::Split;
+ /// Split::row(4);
+ /// ```
+ ///
+ /// [`Table`]: crate::Table
+ pub fn row(index: usize) -> Self {
+ Split {
+ direction: Direction::Row,
+ behavior: Behavior::Zip,
+ display: Display::Clean,
+ index,
+ }
+ }
+
+ /// Returns a split [`Table`] with the redistributed cells pushed to the back of the new shape.
+ ///
+ /// ```text
+ /// +---+---+
+ /// | a | b |
+ /// +---+---+
+ /// +---+---+---+---+---+ | f | g |
+ /// | a | b | c | d | e | Split::column(2).concat() +---+---+
+ /// +---+---+---+---+---+ => | c | d |
+ /// | f | g | h | i | j | +---+---+
+ /// +---+---+---+---+---+ | h | i |
+ /// +---+---+
+ /// | e | |
+ /// +---+---+
+ /// | j | |
+ /// +---+---+
+ /// ```
+ ///
+ /// [`Table`]: crate::Table
+ pub fn concat(self) -> Self {
+ Self {
+ behavior: Behavior::Concat,
+ ..self
+ }
+ }
+
+ /// Returns a split [`Table`] with the redistributed cells inserted behind
+ /// the first correlating column/row one after another.
+ ///
+ /// ```text
+ /// +---+---+
+ /// | a | b |
+ /// +---+---+
+ /// +---+---+---+---+---+ | c | d |
+ /// | a | b | c | d | e | Split::column(2).zip() +---+---+
+ /// +---+---+---+---+---+ => | e | |
+ /// | f | g | h | i | j | +---+---+
+ /// +---+---+---+---+---+ | f | g |
+ /// +---+---+
+ /// | h | i |
+ /// +---+---+
+ /// | j | |
+ /// +---+---+
+ /// ```
+ ///
+ /// [`Table`]: crate::Table
+ pub fn zip(self) -> Self {
+ Self {
+ behavior: Behavior::Zip,
+ ..self
+ }
+ }
+
+ /// Returns a split [`Table`] with the empty columns/rows filtered out.
+ ///
+ /// ```text
+ ///
+ ///
+ /// +---+---+---+
+ /// +---+---+---+---+---+ | a | b | c |
+ /// | a | b | c | d | e | Split::column(3).clean() +---+---+---+
+ /// +---+---+---+---+---+ => | d | e | |
+ /// | f | g | h | | | +---+---+---+
+ /// +---+---+---+---+---+ | f | g | h |
+ /// ^ ^ +---+---+---+
+ /// these cells are filtered
+ /// from the resulting table
+ /// ```
+ ///
+ /// ## Notes
+ ///
+ /// This is apart of the default configuration for Split.
+ ///
+ /// See [`retain`] for an alternative display option.
+ ///
+ /// [`Table`]: crate::Table
+ /// [`retain`]: crate::settings::split::Split::retain
+ pub fn clean(self) -> Self {
+ Self {
+ display: Display::Clean,
+ ..self
+ }
+ }
+
+ /// Returns a split [`Table`] with all cells retained.
+ ///
+ /// ```text
+ /// +---+---+---+
+ /// | a | b | c |
+ /// +---+---+---+
+ /// +---+---+---+---+---+ | d | e | |
+ /// | a | b | c | d | e | Split::column(3).retain() +---+---+---+
+ /// +---+---+---+---+---+ => | f | g | h |
+ /// | f | g | h | | | +---+---+---+
+ /// +---+---+---+---+---+ |-----------> | | | |
+ /// ^ ^ | +---+---+---+
+ /// |___|_____cells are kept!
+ /// ```
+ ///
+ /// ## Notes
+ ///
+ /// See [`clean`] for an alternative display option.
+ ///
+ /// [`Table`]: crate::Table
+ /// [`clean`]: crate::settings::split::Split::clean
+ pub fn retain(self) -> Self {
+ Self {
+ display: Display::Retain,
+ ..self
+ }
+ }
+}
+
+impl<R, D, Cfg> TableOption<R, D, Cfg> for Split
+where
+ R: Records + ExactRecords + Resizable + PeekableRecords,
+{
+ fn change(self, records: &mut R, _: &mut Cfg, _: &mut D) {
+ // variables
+ let Split {
+ direction,
+ behavior,
+ display,
+ index: section_length,
+ } = self;
+ let mut filtered_sections = 0;
+
+ // early return check
+ if records.count_columns() == 0 || records.count_rows() == 0 || section_length == 0 {
+ return;
+ }
+
+ // computed variables
+ let (primary_length, secondary_length) = compute_length_arrangement(records, direction);
+ let sections_per_direction = ceil_div(primary_length, section_length);
+ let (outer_range, inner_range) =
+ compute_range_order(secondary_length, sections_per_direction, behavior);
+
+ // work
+ for outer_index in outer_range {
+ let from_secondary_index = outer_index * sections_per_direction - filtered_sections;
+ for inner_index in inner_range.clone() {
+ let (section_index, from_secondary_index, to_secondary_index) =
+ compute_range_variables(
+ outer_index,
+ inner_index,
+ secondary_length,
+ from_secondary_index,
+ sections_per_direction,
+ filtered_sections,
+ behavior,
+ );
+
+ match (direction, behavior) {
+ (Direction::Column, Behavior::Concat) => records.push_row(),
+ (Direction::Column, Behavior::Zip) => records.insert_row(to_secondary_index),
+ (Direction::Row, Behavior::Concat) => records.push_column(),
+ (Direction::Row, Behavior::Zip) => records.insert_column(to_secondary_index),
+ }
+
+ let section_is_empty = copy_section(
+ records,
+ section_length,
+ section_index,
+ primary_length,
+ from_secondary_index,
+ to_secondary_index,
+ direction,
+ );
+
+ if section_is_empty && display == Display::Clean {
+ delete(records, to_secondary_index, direction);
+ filtered_sections += 1;
+ }
+ }
+ }
+
+ cleanup(records, section_length, primary_length, direction);
+ }
+}
+
+/// Determine which direction should be considered the primary, and which the secondary based on direction
+fn compute_length_arrangement<R>(records: &mut R, direction: Direction) -> (usize, usize)
+where
+ R: Records + ExactRecords,
+{
+ match direction {
+ Direction::Column => (records.count_columns(), records.count_rows()),
+ Direction::Row => (records.count_rows(), records.count_columns()),
+ }
+}
+
+/// reduce the table size to the length of the index in the specified direction
+fn cleanup<R>(records: &mut R, section_length: usize, primary_length: usize, direction: Direction)
+where
+ R: Resizable,
+{
+ for segment in (section_length..primary_length).rev() {
+ match direction {
+ Direction::Column => records.remove_column(segment),
+ Direction::Row => records.remove_row(segment),
+ }
+ }
+}
+
+/// Delete target index row or column
+fn delete<R>(records: &mut R, target_index: usize, direction: Direction)
+where
+ R: Resizable,
+{
+ match direction {
+ Direction::Column => records.remove_row(target_index),
+ Direction::Row => records.remove_column(target_index),
+ }
+}
+
+/// copy cells to new location
+///
+/// returns if the copied section was entirely blank
+fn copy_section<R>(
+ records: &mut R,
+ section_length: usize,
+ section_index: usize,
+ primary_length: usize,
+ from_secondary_index: usize,
+ to_secondary_index: usize,
+ direction: Direction,
+) -> bool
+where
+ R: ExactRecords + Resizable + PeekableRecords,
+{
+ let mut section_is_empty = true;
+ for to_primary_index in 0..section_length {
+ let from_primary_index = to_primary_index + section_index * section_length;
+
+ if from_primary_index < primary_length {
+ let from_position =
+ format_position(direction, from_primary_index, from_secondary_index);
+ if records.get_text(from_position) != "" {
+ section_is_empty = false;
+ }
+ records.swap(
+ from_position,
+ format_position(direction, to_primary_index, to_secondary_index),
+ );
+ }
+ }
+ section_is_empty
+}
+
+/// determine section over direction or vice versa based on behavior
+fn compute_range_order(
+ direction_length: usize,
+ sections_per_direction: usize,
+ behavior: Behavior,
+) -> (Range<usize>, Range<usize>) {
+ match behavior {
+ Behavior::Concat => (1..sections_per_direction, 0..direction_length),
+ Behavior::Zip => (0..direction_length, 1..sections_per_direction),
+ }
+}
+
+/// helper function for shimming both behaviors to work within a single nested loop
+fn compute_range_variables(
+ outer_index: usize,
+ inner_index: usize,
+ direction_length: usize,
+ from_secondary_index: usize,
+ sections_per_direction: usize,
+ filtered_sections: usize,
+ behavior: Behavior,
+) -> (usize, usize, usize) {
+ match behavior {
+ Behavior::Concat => (
+ outer_index,
+ inner_index,
+ inner_index + outer_index * direction_length - filtered_sections,
+ ),
+ Behavior::Zip => (
+ inner_index,
+ from_secondary_index,
+ outer_index * sections_per_direction + inner_index - filtered_sections,
+ ),
+ }
+}
+
+/// utility for arguments of a position easily
+fn format_position(direction: Direction, primary_index: usize, secondary_index: usize) -> Position {
+ match direction {
+ Direction::Column => (secondary_index, primary_index),
+ Direction::Row => (primary_index, secondary_index),
+ }
+}
+
+/// ceil division utility because the std lib ceil_div isn't stable yet
+fn ceil_div(x: usize, y: usize) -> usize {
+ debug_assert!(x != 0);
+ 1 + ((x - 1) / y)
+}
diff --git a/vendor/tabled/src/settings/style/border.rs b/vendor/tabled/src/settings/style/border.rs
new file mode 100644
index 000000000..a8cbefb5d
--- /dev/null
+++ b/vendor/tabled/src/settings/style/border.rs
@@ -0,0 +1,159 @@
+use crate::{
+ grid::{
+ config::{Border as GBorder, ColoredConfig, Entity},
+ records::{ExactRecords, Records},
+ },
+ settings::CellOption,
+};
+
+/// Border represents a border of a Cell.
+///
+/// ```text
+/// top border
+/// |
+/// V
+/// corner top left ------> +_______+ <---- corner top left
+/// | |
+/// left border ----------> | cell | <---- right border
+/// | |
+/// corner bottom right --> +_______+ <---- corner bottom right
+/// ^
+/// |
+/// bottom border
+/// ```
+///
+/// ```rust,no_run
+/// # use tabled::{Table, settings::{Modify, style::{Style, Border}, object::Rows}};
+/// # let data: Vec<&'static str> = Vec::new();
+/// let table = Table::new(&data)
+/// .with(Style::ascii())
+/// .with(Modify::new(Rows::single(0)).with(Border::default().top('x')));
+/// ```
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Border(GBorder<char>);
+
+impl Border {
+ /// This function constructs a cell borders with all sides set.
+ #[allow(clippy::too_many_arguments)]
+ pub const fn full(
+ top: char,
+ bottom: char,
+ left: char,
+ right: char,
+ top_left: char,
+ top_right: char,
+ bottom_left: char,
+ bottom_right: char,
+ ) -> Self {
+ Self(GBorder::full(
+ top,
+ bottom,
+ left,
+ right,
+ top_left,
+ top_right,
+ bottom_left,
+ bottom_right,
+ ))
+ }
+
+ /// This function constructs a cell borders with all sides's char set to a given character.
+ /// It behaves like [`Border::full`] with the same character set to each side.
+ pub const fn filled(c: char) -> Self {
+ Self::full(c, c, c, c, c, c, c, c)
+ }
+
+ /// Using this function you deconstruct the existing borders.
+ pub const fn empty() -> EmptyBorder {
+ EmptyBorder
+ }
+
+ /// Set a top border character.
+ pub const fn top(mut self, c: char) -> Self {
+ self.0.top = Some(c);
+ self
+ }
+
+ /// Set a bottom border character.
+ pub const fn bottom(mut self, c: char) -> Self {
+ self.0.bottom = Some(c);
+ self
+ }
+
+ /// Set a left border character.
+ pub const fn left(mut self, c: char) -> Self {
+ self.0.left = Some(c);
+ self
+ }
+
+ /// Set a right border character.
+ pub const fn right(mut self, c: char) -> Self {
+ self.0.right = Some(c);
+ self
+ }
+
+ /// Set a top left intersection character.
+ pub const fn corner_top_left(mut self, c: char) -> Self {
+ self.0.left_top_corner = Some(c);
+ self
+ }
+
+ /// Set a top right intersection character.
+ pub const fn corner_top_right(mut self, c: char) -> Self {
+ self.0.right_top_corner = Some(c);
+ self
+ }
+
+ /// Set a bottom left intersection character.
+ pub const fn corner_bottom_left(mut self, c: char) -> Self {
+ self.0.left_bottom_corner = Some(c);
+ self
+ }
+
+ /// Set a bottom right intersection character.
+ pub const fn corner_bottom_right(mut self, c: char) -> Self {
+ self.0.right_bottom_corner = Some(c);
+ self
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for Border
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let shape = (records.count_rows(), records.count_columns());
+
+ for pos in entity.iter(shape.0, shape.1) {
+ cfg.set_border(pos, self.0);
+ }
+ }
+}
+
+impl From<GBorder<char>> for Border {
+ fn from(b: GBorder<char>) -> Border {
+ Border(b)
+ }
+}
+
+impl From<Border> for GBorder<char> {
+ fn from(value: Border) -> Self {
+ value.0
+ }
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct EmptyBorder;
+
+impl<R> CellOption<R, ColoredConfig> for EmptyBorder
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let shape = (records.count_rows(), records.count_columns());
+
+ for pos in entity.iter(shape.0, shape.1) {
+ cfg.remove_border(pos, shape);
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/style/border_char.rs b/vendor/tabled/src/settings/style/border_char.rs
new file mode 100644
index 000000000..b1e04c579
--- /dev/null
+++ b/vendor/tabled/src/settings/style/border_char.rs
@@ -0,0 +1,99 @@
+use crate::{
+ grid::config::{ColoredConfig, Entity, Position, SpannedConfig},
+ grid::records::{ExactRecords, Records},
+ settings::CellOption,
+};
+
+use super::Offset;
+
+/// [`BorderChar`] sets a char to a specific location on a horizontal line.
+///
+/// # Example
+///
+/// ```rust
+/// use tabled::{Table, settings::{style::{Style, BorderChar, Offset}, Modify, object::{Object, Rows, Columns}}};
+///
+/// let mut table = Table::new(["Hello World"]);
+/// table
+/// .with(Style::markdown())
+/// .with(Modify::new(Rows::single(1))
+/// .with(BorderChar::horizontal(':', Offset::Begin(0)))
+/// .with(BorderChar::horizontal(':', Offset::End(0)))
+/// )
+/// .with(Modify::new((1, 0).and((1, 1))).with(BorderChar::vertical('#', Offset::Begin(0))));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// concat!(
+/// "| &str |\n",
+/// "|:-----------:|\n",
+/// "# Hello World #",
+/// ),
+/// );
+/// ```
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct BorderChar {
+ c: char,
+ offset: Offset,
+ horizontal: bool,
+}
+
+impl BorderChar {
+ /// Creates a [`BorderChar`] which overrides horizontal line.
+ pub fn horizontal(c: char, offset: Offset) -> Self {
+ Self {
+ c,
+ offset,
+ horizontal: true,
+ }
+ }
+
+ /// Creates a [`BorderChar`] which overrides vertical line.
+ pub fn vertical(c: char, offset: Offset) -> Self {
+ Self {
+ c,
+ offset,
+ horizontal: false,
+ }
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for BorderChar
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let cells = entity.iter(records.count_rows(), records.count_columns());
+
+ match self.horizontal {
+ true => add_char_horizontal(cfg, self.c, self.offset, cells),
+ false => add_char_vertical(cfg, self.c, self.offset, cells),
+ }
+ }
+}
+
+fn add_char_vertical<I: Iterator<Item = Position>>(
+ cfg: &mut SpannedConfig,
+ c: char,
+ offset: Offset,
+ cells: I,
+) {
+ let offset = offset.into();
+
+ for pos in cells {
+ cfg.set_vertical_char(pos, c, offset);
+ }
+}
+
+fn add_char_horizontal<I: Iterator<Item = Position>>(
+ cfg: &mut SpannedConfig,
+ c: char,
+ offset: Offset,
+ cells: I,
+) {
+ let offset = offset.into();
+
+ for pos in cells {
+ cfg.set_horizontal_char(pos, c, offset);
+ }
+}
diff --git a/vendor/tabled/src/settings/style/border_color.rs b/vendor/tabled/src/settings/style/border_color.rs
new file mode 100644
index 000000000..6b3fa2b1a
--- /dev/null
+++ b/vendor/tabled/src/settings/style/border_color.rs
@@ -0,0 +1,155 @@
+//! This module contains a configuration of a Border to set its color via [`BorderColor`].
+
+use crate::{
+ grid::{
+ color::AnsiColor,
+ config::{Border, ColoredConfig, Entity},
+ records::{ExactRecords, Records},
+ },
+ settings::{color::Color, CellOption, TableOption},
+};
+
+/// BorderColored represents a colored border of a Cell.
+///
+/// ```rust,no_run
+/// # use tabled::{settings::{style::BorderColor, Style, Color, object::Rows, Modify}, Table};
+/// #
+/// # let data: Vec<&'static str> = Vec::new();
+/// #
+/// let table = Table::new(&data)
+/// .with(Style::ascii())
+/// .with(Modify::new(Rows::single(0)).with(BorderColor::default().top(Color::FG_RED)));
+/// ```
+#[derive(Debug, Clone, Default, Eq, PartialEq)]
+pub struct BorderColor(Border<AnsiColor<'static>>);
+
+impl BorderColor {
+ /// This function constructs a cell borders with all sides set.
+ #[allow(clippy::too_many_arguments)]
+ pub fn full(
+ top: Color,
+ bottom: Color,
+ left: Color,
+ right: Color,
+ top_left: Color,
+ top_right: Color,
+ bottom_left: Color,
+ bottom_right: Color,
+ ) -> Self {
+ Self(Border::full(
+ top.into(),
+ bottom.into(),
+ left.into(),
+ right.into(),
+ top_left.into(),
+ top_right.into(),
+ bottom_left.into(),
+ bottom_right.into(),
+ ))
+ }
+
+ /// This function constructs a cell borders with all sides's char set to a given character.
+ /// It behaves like [`Border::full`] with the same character set to each side.
+ pub fn filled(c: Color) -> Self {
+ let c: AnsiColor<'_> = c.into();
+
+ Self(Border {
+ top: Some(c.clone()),
+ bottom: Some(c.clone()),
+ left: Some(c.clone()),
+ right: Some(c.clone()),
+ left_bottom_corner: Some(c.clone()),
+ left_top_corner: Some(c.clone()),
+ right_bottom_corner: Some(c.clone()),
+ right_top_corner: Some(c),
+ })
+ }
+
+ /// Set a top border character.
+ pub fn top(mut self, c: Color) -> Self {
+ self.0.top = Some(c.into());
+ self
+ }
+
+ /// Set a bottom border character.
+ pub fn bottom(mut self, c: Color) -> Self {
+ self.0.bottom = Some(c.into());
+ self
+ }
+
+ /// Set a left border character.
+ pub fn left(mut self, c: Color) -> Self {
+ self.0.left = Some(c.into());
+ self
+ }
+
+ /// Set a right border character.
+ pub fn right(mut self, c: Color) -> Self {
+ self.0.right = Some(c.into());
+ self
+ }
+
+ /// Set a top left intersection character.
+ pub fn corner_top_left(mut self, c: Color) -> Self {
+ self.0.left_top_corner = Some(c.into());
+ self
+ }
+
+ /// Set a top right intersection character.
+ pub fn corner_top_right(mut self, c: Color) -> Self {
+ self.0.right_top_corner = Some(c.into());
+ self
+ }
+
+ /// Set a bottom left intersection character.
+ pub fn corner_bottom_left(mut self, c: Color) -> Self {
+ self.0.left_bottom_corner = Some(c.into());
+ self
+ }
+
+ /// Set a bottom right intersection character.
+ pub fn corner_bottom_right(mut self, c: Color) -> Self {
+ self.0.right_bottom_corner = Some(c.into());
+ self
+ }
+}
+
+impl<R> CellOption<R, ColoredConfig> for BorderColor
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ let border_color = &self.0;
+
+ for pos in entity.iter(count_rows, count_columns) {
+ cfg.set_border_color(pos, border_color.clone());
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderColor
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ let border_color = &self.0;
+
+ for row in 0..count_rows {
+ for col in 0..count_columns {
+ cfg.set_border_color((row, col), border_color.clone());
+ }
+ }
+ }
+}
+
+impl From<BorderColor> for Border<AnsiColor<'static>> {
+ fn from(val: BorderColor) -> Self {
+ val.0
+ }
+}
diff --git a/vendor/tabled/src/settings/style/border_text.rs b/vendor/tabled/src/settings/style/border_text.rs
new file mode 100644
index 000000000..163a19821
--- /dev/null
+++ b/vendor/tabled/src/settings/style/border_text.rs
@@ -0,0 +1,253 @@
+use crate::{
+ grid::{
+ color::AnsiColor,
+ config::{self, ColoredConfig, SpannedConfig},
+ dimension::{Dimension, Estimate},
+ records::{ExactRecords, Records},
+ },
+ settings::{
+ object::{FirstRow, LastRow},
+ Color, TableOption,
+ },
+};
+
+use super::Offset;
+
+/// [`BorderText`] writes a custom text on a border.
+///
+/// # Example
+///
+/// ```rust
+/// use tabled::{Table, settings::style::BorderText, settings::object::Rows};
+///
+/// let mut table = Table::new(["Hello World"]);
+/// table
+/// .with(BorderText::new("+-.table").horizontal(Rows::first()));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+-.table------+\n\
+/// | &str |\n\
+/// +-------------+\n\
+/// | Hello World |\n\
+/// +-------------+"
+/// );
+/// ```
+#[derive(Debug)]
+pub struct BorderText<L> {
+ text: String,
+ offset: Offset,
+ color: Option<AnsiColor<'static>>,
+ line: L,
+}
+
+impl BorderText<()> {
+ /// Creates a [`BorderText`] instance.
+ ///
+ /// Lines are numbered from 0 to the `count_rows` included
+ /// (`line >= 0 && line <= count_rows`).
+ pub fn new<S: Into<String>>(text: S) -> Self {
+ BorderText {
+ text: text.into(),
+ line: (),
+ offset: Offset::Begin(0),
+ color: None,
+ }
+ }
+}
+
+impl<Line> BorderText<Line> {
+ /// Set a line on which we will set the text.
+ pub fn horizontal<L>(self, line: L) -> BorderText<L> {
+ BorderText {
+ line,
+ text: self.text,
+ offset: self.offset,
+ color: self.color,
+ }
+ }
+
+ /// Set an offset from which the text will be started.
+ pub fn offset(self, offset: Offset) -> Self {
+ BorderText {
+ offset,
+ text: self.text,
+ line: self.line,
+ color: self.color,
+ }
+ }
+
+ /// Set a color of the text.
+ pub fn color(self, color: Color) -> Self {
+ BorderText {
+ color: Some(color.into()),
+ text: self.text,
+ line: self.line,
+ offset: self.offset,
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderText<usize>
+where
+ R: Records + ExactRecords,
+ for<'a> &'a R: Records,
+ for<'a> D: Estimate<&'a R, ColoredConfig>,
+ D: Dimension,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dims: &mut D) {
+ dims.estimate(records, cfg);
+ let shape = (records.count_rows(), records.count_columns());
+ let line = self.line;
+ set_horizontal_chars(cfg, dims, self.offset, line, &self.text, &self.color, shape);
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderText<FirstRow>
+where
+ R: Records + ExactRecords,
+ for<'a> &'a R: Records,
+ for<'a> D: Estimate<&'a R, ColoredConfig>,
+ D: Dimension,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dims: &mut D) {
+ dims.estimate(records, cfg);
+ let shape = (records.count_rows(), records.count_columns());
+ let line = 0;
+ set_horizontal_chars(cfg, dims, self.offset, line, &self.text, &self.color, shape);
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderText<LastRow>
+where
+ R: Records + ExactRecords,
+ for<'a> &'a R: Records,
+ for<'a> D: Estimate<&'a R, ColoredConfig>,
+ D: Dimension,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dims: &mut D) {
+ dims.estimate(records, cfg);
+ let shape = (records.count_rows(), records.count_columns());
+ let line = records.count_rows();
+ set_horizontal_chars(cfg, dims, self.offset, line, &self.text, &self.color, shape);
+ }
+}
+
+fn set_horizontal_chars<D: Dimension>(
+ cfg: &mut SpannedConfig,
+ dims: &D,
+ offset: Offset,
+ line: usize,
+ text: &str,
+ color: &Option<AnsiColor<'static>>,
+ shape: (usize, usize),
+) {
+ let (_, count_columns) = shape;
+ let pos = get_start_pos(cfg, dims, offset, count_columns);
+ let pos = match pos {
+ Some(pos) => pos,
+ None => return,
+ };
+
+ let mut chars = text.chars();
+ let mut i = cfg.has_vertical(0, count_columns) as usize;
+ if i == 1 && pos == 0 {
+ let c = match chars.next() {
+ Some(c) => c,
+ None => return,
+ };
+
+ let mut b = cfg.get_border((line, 0), shape);
+ b.left_top_corner = b.left_top_corner.map(|_| c);
+ cfg.set_border((line, 0), b);
+
+ if let Some(color) = color.as_ref() {
+ let mut b = cfg.get_border_color((line, 0), shape).cloned();
+ b.left_top_corner = Some(color.clone());
+ cfg.set_border_color((line, 0), b);
+ }
+ }
+
+ for col in 0..count_columns {
+ let w = dims.get_width(col);
+ if i + w > pos {
+ for off in 0..w {
+ if i + off < pos {
+ continue;
+ }
+
+ let c = match chars.next() {
+ Some(c) => c,
+ None => return,
+ };
+
+ cfg.set_horizontal_char((line, col), c, config::Offset::Begin(off));
+ if let Some(color) = color.as_ref() {
+ cfg.set_horizontal_color(
+ (line, col),
+ color.clone(),
+ config::Offset::Begin(off),
+ );
+ }
+ }
+ }
+
+ i += w;
+
+ if cfg.has_vertical(col + 1, count_columns) {
+ i += 1;
+
+ if i > pos {
+ let c = match chars.next() {
+ Some(c) => c,
+ None => return,
+ };
+
+ let mut b = cfg.get_border((line, col), shape);
+ b.right_top_corner = b.right_top_corner.map(|_| c);
+ cfg.set_border((line, col), b);
+
+ if let Some(color) = color.as_ref() {
+ let mut b = cfg.get_border_color((line, col), shape).cloned();
+ b.right_top_corner = Some(color.clone());
+ cfg.set_border_color((line, col), b);
+ }
+ }
+ }
+ }
+}
+
+fn get_start_pos<D: Dimension>(
+ cfg: &SpannedConfig,
+ dims: &D,
+ offset: Offset,
+ count_columns: usize,
+) -> Option<usize> {
+ let totalw = total_width(cfg, dims, count_columns);
+ match offset {
+ Offset::Begin(i) => {
+ if i > totalw {
+ None
+ } else {
+ Some(i)
+ }
+ }
+ Offset::End(i) => {
+ if i > totalw {
+ None
+ } else {
+ Some(totalw - i)
+ }
+ }
+ }
+}
+
+fn total_width<D: Dimension>(cfg: &SpannedConfig, dims: &D, count_columns: usize) -> usize {
+ let mut totalw = cfg.has_vertical(0, count_columns) as usize;
+ for col in 0..count_columns {
+ totalw += dims.get_width(col);
+ totalw += cfg.has_vertical(col + 1, count_columns) as usize;
+ }
+
+ totalw
+}
diff --git a/vendor/tabled/src/settings/style/builder.rs b/vendor/tabled/src/settings/style/builder.rs
new file mode 100644
index 000000000..d55c400cc
--- /dev/null
+++ b/vendor/tabled/src/settings/style/builder.rs
@@ -0,0 +1,1208 @@
+//! This module contains a compile time style builder [`Style`].
+
+use core::marker::PhantomData;
+
+use crate::{
+ grid::config::{Borders, CompactConfig, CompactMultilineConfig},
+ settings::TableOption,
+};
+
+#[cfg(feature = "std")]
+use crate::grid::config::ColoredConfig;
+
+use super::{HorizontalLine, Line, VerticalLine};
+
+/// Style is represents a theme of a [`Table`].
+///
+/// ```text
+/// corner top left top intersection corner top right
+/// . | .
+/// . V .
+/// ╭───┬───┬───┬───┬───┬───┬────┬────┬────╮
+/// │ i │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │
+/// ├───┼───┼───┼───┼───┼───┼────┼────┼────┤ <- this horizontal line is custom 'horizontals'
+/// │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ other lines horizontal lines are not set they called 'horizontal'
+/// │ 1 │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │
+/// │ 2 │ 0 │ 2 │ 4 │ 6 │ 8 │ 10 │ 12 │ 14 │
+/// ╰───┴───┴───┴───┴───┴───┴────┴────┴────╯
+/// . ^ ^ .
+/// . | | .
+/// corner bottom left | bottom intersection corner bottom right
+/// |
+/// |
+/// all this vertical lines are called 'vertical'
+/// ```
+///
+///
+/// ```text
+/// ┌───┬───┬───┬───┬───┐
+/// │ 0 │ 1 │ 2 │ 3 │ 4 │
+/// intersection left ->├───X───X───X───X───┤ <- all this horizontal lines are called 'horizontal'
+/// │ 1 │ 2 │ 3 │ 4 │ 5 │
+/// ├───X───X───X───X───┤ <- intersection right
+/// │ 2 │ 3 │ 4 │ 5 │ 6 │
+/// └───┴───┴───┴───┴───┘
+///
+/// All 'X' positions are called 'intersection'.
+/// It's a place where 'vertical' and 'horizontal' lines intersect.
+/// ```
+///
+/// It tries to limit an controlling a valid state of it.
+/// For example, it won't allow to call method [`Style::corner_top_left`] unless [`Style::left`] and [`Style::top`] is set.
+///
+/// You can turn [`Style`] into [`RawStyle`] to have more control using [`Into`] implementation.
+///
+/// # Example
+///
+#[cfg_attr(feature = "std", doc = "```")]
+#[cfg_attr(not(feature = "std"), doc = "```ignore")]
+/// use tabled::{Table, settings::Style};
+///
+/// let style = Style::ascii()
+/// .bottom('*')
+/// .intersection(' ');
+///
+/// let data = vec!["Hello", "2021"];
+/// let table = Table::new(&data).with(style).to_string();
+///
+/// println!("{}", table);
+/// ```
+///
+/// [`Table`]: crate::Table
+/// [`RawStyle`]: crate::settings::style::RawStyle
+/// [`Style::corner_top_left`]: Style::corner_top_left
+/// [`Style::left`]: Style.left
+/// [`Style::top`]: Style.function.top
+#[derive(Debug, Clone)]
+pub struct Style<T, B, L, R, H, V, HLines = HLineArray<0>, VLines = VLineArray<0>> {
+ borders: Borders<char>,
+ horizontals: HLines,
+ verticals: VLines,
+ _top: PhantomData<T>,
+ _bottom: PhantomData<B>,
+ _left: PhantomData<L>,
+ _right: PhantomData<R>,
+ _horizontal: PhantomData<H>,
+ _vertical: PhantomData<V>,
+}
+
+type HLineArray<const N: usize> = [HorizontalLine; N];
+
+type VLineArray<const N: usize> = [VerticalLine; N];
+
+/// A marker struct which is used in [`Style`].
+#[derive(Debug, Clone)]
+pub struct On;
+
+impl Style<(), (), (), (), (), (), (), ()> {
+ /// This style is a style with no styling options on,
+ ///
+ /// ```text
+ /// id destribution link
+ /// 0 Fedora https://getfedora.org/
+ /// 2 OpenSUSE https://www.opensuse.org/
+ /// 3 Endeavouros https://endeavouros.com/
+ /// ```
+ ///
+ /// Note: The cells in the example have 1-left and 1-right indent.
+ ///
+ /// This style can be used as a base style to build a custom one.
+ ///
+ /// ```rust,no_run
+ /// # use tabled::settings::Style;
+ /// let style = Style::empty()
+ /// .top('*')
+ /// .bottom('*')
+ /// .vertical('#')
+ /// .intersection_top('*');
+ /// ```
+ pub const fn empty() -> Style<(), (), (), (), (), ()> {
+ Style::new(
+ create_borders(
+ Line::empty(),
+ Line::empty(),
+ Line::empty(),
+ None,
+ None,
+ None,
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This style is analog of `empty` but with a vertical space(' ') line.
+ ///
+ /// ```text
+ /// id destribution link
+ /// 0 Fedora https://getfedora.org/
+ /// 2 OpenSUSE https://www.opensuse.org/
+ /// 3 Endeavouros https://endeavouros.com/
+ /// ```
+ pub const fn blank() -> Style<(), (), (), (), (), On> {
+ Style::new(
+ create_borders(
+ Line::empty(),
+ Line::empty(),
+ Line::empty(),
+ None,
+ None,
+ Some(' '),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This is a style which relays only on ASCII charset.
+ ///
+ /// It has horizontal and vertical lines.
+ ///
+ /// ```text
+ /// +----+--------------+---------------------------+
+ /// | id | destribution | link |
+ /// +----+--------------+---------------------------+
+ /// | 0 | Fedora | https://getfedora.org/ |
+ /// +----+--------------+---------------------------+
+ /// | 2 | OpenSUSE | https://www.opensuse.org/ |
+ /// +----+--------------+---------------------------+
+ /// | 3 | Endeavouros | https://endeavouros.com/ |
+ /// +----+--------------+---------------------------+
+ /// ```
+ pub const fn ascii() -> Style<On, On, On, On, On, On> {
+ Style::new(
+ create_borders(
+ Line::full('-', '+', '+', '+'),
+ Line::full('-', '+', '+', '+'),
+ Line::full('-', '+', '+', '+'),
+ Some('|'),
+ Some('|'),
+ Some('|'),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// `psql` style looks like a table style `PostgreSQL` uses.
+ ///
+ /// It has only 1 horizontal line which splits header.
+ /// And no left and right vertical lines.
+ ///
+ /// ```text
+ /// id | destribution | link
+ /// ----+--------------+---------------------------
+ /// 0 | Fedora | https://getfedora.org/
+ /// 2 | OpenSUSE | https://www.opensuse.org/
+ /// 3 | Endeavouros | https://endeavouros.com/
+ /// ```
+ pub const fn psql() -> Style<(), (), (), (), (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::empty(),
+ Line::empty(),
+ Line::empty(),
+ None,
+ None,
+ Some('|'),
+ ),
+ [HorizontalLine::new(1, Line::empty())
+ .main(Some('-'))
+ .intersection(Some('+'))],
+ [],
+ )
+ }
+
+ /// `markdown` style mimics a `Markdown` table style.
+ ///
+ /// ```text
+ /// | id | destribution | link |
+ /// |----|--------------|---------------------------|
+ /// | 0 | Fedora | https://getfedora.org/ |
+ /// | 2 | OpenSUSE | https://www.opensuse.org/ |
+ /// | 3 | Endeavouros | https://endeavouros.com/ |
+ /// ```
+ pub const fn markdown() -> Style<(), (), On, On, (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::empty(),
+ Line::empty(),
+ Line::empty(),
+ Some('|'),
+ Some('|'),
+ Some('|'),
+ ),
+ [HorizontalLine::new(1, Line::full('-', '|', '|', '|'))],
+ [],
+ )
+ }
+
+ /// This style is analog of [`Style::ascii`] which uses UTF-8 charset.
+ ///
+ /// It has vertical and horizontal split lines.
+ ///
+ /// ```text
+ /// ┌────┬──────────────┬───────────────────────────┐
+ /// │ id │ destribution │ link │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 0 │ Fedora │ https://getfedora.org/ │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 2 │ OpenSUSE │ https://www.opensuse.org/ │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 3 │ Endeavouros │ https://endeavouros.com/ │
+ /// └────┴──────────────┴───────────────────────────┘
+ /// ```
+ pub const fn modern() -> Style<On, On, On, On, On, On> {
+ Style::new(
+ create_borders(
+ Line::full('─', '┬', '┌', '┐'),
+ Line::full('─', '┴', '└', '┘'),
+ Line::full('─', '┼', '├', '┤'),
+ Some('│'),
+ Some('│'),
+ Some('│'),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This style looks like a [`Style::modern`] but without horozizontal lines except a header.
+ ///
+ /// Beware: It uses UTF-8 characters.
+ ///
+ /// ```text
+ /// ┌────┬──────────────┬───────────────────────────┐
+ /// │ id │ destribution │ link │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 0 │ Fedora │ https://getfedora.org/ │
+ /// │ 2 │ OpenSUSE │ https://www.opensuse.org/ │
+ /// │ 3 │ Endeavouros │ https://endeavouros.com/ │
+ /// └────┴──────────────┴───────────────────────────┘
+ /// ```
+ pub const fn sharp() -> Style<On, On, On, On, (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::full('─', '┬', '┌', '┐'),
+ Line::full('─', '┴', '└', '┘'),
+ Line::empty(),
+ Some('│'),
+ Some('│'),
+ Some('│'),
+ ),
+ [HorizontalLine::new(1, Line::full('─', '┼', '├', '┤'))],
+ [],
+ )
+ }
+
+ /// This style looks like a [`Style::sharp`] but with rounded corners.
+ ///
+ /// Beware: It uses UTF-8 characters.
+ ///
+ /// ```text
+ /// ╭────┬──────────────┬───────────────────────────╮
+ /// │ id │ destribution │ link │
+ /// ├────┼──────────────┼───────────────────────────┤
+ /// │ 0 │ Fedora │ https://getfedora.org/ │
+ /// │ 2 │ OpenSUSE │ https://www.opensuse.org/ │
+ /// │ 3 │ Endeavouros │ https://endeavouros.com/ │
+ /// ╰────┴──────────────┴───────────────────────────╯
+ /// ```
+ pub const fn rounded() -> Style<On, On, On, On, (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::full('─', '┬', '╭', '╮'),
+ Line::full('─', '┴', '╰', '╯'),
+ Line::empty(),
+ Some('│'),
+ Some('│'),
+ Some('│'),
+ ),
+ [HorizontalLine::new(1, Line::full('─', '┼', '├', '┤'))],
+ [],
+ )
+ }
+
+ /// This style uses a chars which resembles '2 lines'.
+ ///
+ /// Beware: It uses UTF8 characters.
+ ///
+ /// ```text
+ /// ╔════╦══════════════╦═══════════════════════════╗
+ /// ║ id ║ destribution ║ link ║
+ /// ╠════╬══════════════╬═══════════════════════════╣
+ /// ║ 0 ║ Fedora ║ https://getfedora.org/ ║
+ /// ╠════╬══════════════╬═══════════════════════════╣
+ /// ║ 2 ║ OpenSUSE ║ https://www.opensuse.org/ ║
+ /// ╠════╬══════════════╬═══════════════════════════╣
+ /// ║ 3 ║ Endeavouros ║ https://endeavouros.com/ ║
+ /// ╚════╩══════════════╩═══════════════════════════╝
+ /// ```
+ pub const fn extended() -> Style<On, On, On, On, On, On> {
+ Style::new(
+ create_borders(
+ Line::full('═', '╦', '╔', '╗'),
+ Line::full('═', '╩', '╚', '╝'),
+ Line::full('═', '╬', '╠', '╣'),
+ Some('║'),
+ Some('║'),
+ Some('║'),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This is a style uses only '.' and ':' chars.
+ /// It has a vertical and horizontal split lines.
+ ///
+ /// ```text
+ /// .................................................
+ /// : id : destribution : link :
+ /// :....:..............:...........................:
+ /// : 0 : Fedora : https://getfedora.org/ :
+ /// :....:..............:...........................:
+ /// : 2 : OpenSUSE : https://www.opensuse.org/ :
+ /// :....:..............:...........................:
+ /// : 3 : Endeavouros : https://endeavouros.com/ :
+ /// :....:..............:...........................:
+ /// ```
+ pub const fn dots() -> Style<On, On, On, On, On, On> {
+ Style::new(
+ create_borders(
+ Line::full('.', '.', '.', '.'),
+ Line::full('.', ':', ':', ':'),
+ Line::full('.', ':', ':', ':'),
+ Some(':'),
+ Some(':'),
+ Some(':'),
+ ),
+ [],
+ [],
+ )
+ }
+
+ /// This style is one of table views in `ReStructuredText`.
+ ///
+ /// ```text
+ /// ==== ============== ===========================
+ /// id destribution link
+ /// ==== ============== ===========================
+ /// 0 Fedora https://getfedora.org/
+ /// 2 OpenSUSE https://www.opensuse.org/
+ /// 3 Endeavouros https://endeavouros.com/
+ /// ==== ============== ===========================
+ /// ```
+ pub const fn re_structured_text() -> Style<On, On, (), (), (), On, HLineArray<1>> {
+ Style::new(
+ create_borders(
+ Line::new(Some('='), Some(' '), None, None),
+ Line::new(Some('='), Some(' '), None, None),
+ Line::empty(),
+ None,
+ None,
+ Some(' '),
+ ),
+ [HorizontalLine::new(
+ 1,
+ Line::new(Some('='), Some(' '), None, None),
+ )],
+ [],
+ )
+ }
+
+ /// This is a theme analog of [`Style::rounded`], but in using ascii charset and
+ /// with no horizontal lines.
+ ///
+ /// ```text
+ /// .-----------------------------------------------.
+ /// | id | destribution | link |
+ /// | 0 | Fedora | https://getfedora.org/ |
+ /// | 2 | OpenSUSE | https://www.opensuse.org/ |
+ /// | 3 | Endeavouros | https://endeavouros.com/ |
+ /// '-----------------------------------------------'
+ /// ```
+ pub const fn ascii_rounded() -> Style<On, On, On, On, (), On> {
+ Style::new(
+ create_borders(
+ Line::full('-', '-', '.', '.'),
+ Line::full('-', '-', '\'', '\''),
+ Line::empty(),
+ Some('|'),
+ Some('|'),
+ Some('|'),
+ ),
+ [],
+ [],
+ )
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines> Style<T, B, L, R, H, V, HLines, VLines> {
+ /// Frame function returns a frame as a border.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{Table, settings::{Style, Highlight, object::Rows}};
+ ///
+ /// let data = [["10:52:19", "Hello"], ["10:52:20", "World"]];
+ /// let table = Table::new(data)
+ /// .with(Highlight::new(Rows::first(), Style::modern().get_frame()))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "┌──────────────────┐\n",
+ /// "│ 0 | 1 │\n",
+ /// "└──────────────────┘\n",
+ /// "| 10:52:19 | Hello |\n",
+ /// "+----------+-------+\n",
+ /// "| 10:52:20 | World |\n",
+ /// "+----------+-------+",
+ /// )
+ /// );
+ /// ```
+ #[cfg(feature = "std")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+ pub const fn get_frame(&self) -> super::Border {
+ let mut border = super::Border::filled(' ');
+
+ if let Some(c) = self.borders.top {
+ border = border.top(c);
+ }
+
+ if let Some(c) = self.borders.bottom {
+ border = border.bottom(c);
+ }
+
+ if let Some(c) = self.borders.left {
+ border = border.left(c);
+ }
+
+ if let Some(c) = self.borders.right {
+ border = border.right(c);
+ }
+
+ if let Some(c) = self.borders.top_left {
+ border = border.corner_top_left(c);
+ }
+
+ if let Some(c) = self.borders.bottom_left {
+ border = border.corner_bottom_left(c);
+ }
+
+ if let Some(c) = self.borders.top_right {
+ border = border.corner_top_right(c);
+ }
+
+ if let Some(c) = self.borders.bottom_right {
+ border = border.corner_bottom_right(c);
+ }
+
+ border
+ }
+
+ /// Get a [`Style`]'s default horizontal line.
+ ///
+ /// It doesn't return an overloaded line via [`Style::horizontals`].
+ ///
+ /// # Example
+ ///
+ #[cfg_attr(feature = "std", doc = "```")]
+ #[cfg_attr(not(feature = "std"), doc = "```ignore")]
+ /// use tabled::{settings::style::{Style, HorizontalLine, Line}, Table};
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", "World", i)))
+ /// .with(Style::ascii().remove_horizontal().horizontals([HorizontalLine::new(1, Style::modern().get_horizontal())]))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "+-------+-------+-----+\n",
+ /// "| &str | &str | i32 |\n",
+ /// "├───────┼───────┼─────┤\n",
+ /// "| Hello | World | 0 |\n",
+ /// "| Hello | World | 1 |\n",
+ /// "| Hello | World | 2 |\n",
+ /// "+-------+-------+-----+",
+ /// )
+ /// )
+ /// ```
+ pub const fn get_horizontal(&self) -> Line {
+ Line::new(
+ self.borders.horizontal,
+ self.borders.intersection,
+ self.borders.left_intersection,
+ self.borders.right_intersection,
+ )
+ }
+
+ /// Get a [`Style`]'s default horizontal line.
+ ///
+ /// It doesn't return an overloaded line via [`Style::verticals`].
+ ///
+ /// # Example
+ ///
+ #[cfg_attr(feature = "std", doc = "```")]
+ #[cfg_attr(not(feature = "std"), doc = "```ignore")]
+ /// use tabled::{settings::style::{Style, VerticalLine, Line}, Table};
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", "World", i)))
+ /// .with(Style::ascii().remove_horizontal().verticals([VerticalLine::new(1, Style::modern().get_vertical())]))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "+-------┬-------+-----+\n",
+ /// "| &str │ &str | i32 |\n",
+ /// "| Hello │ World | 0 |\n",
+ /// "| Hello │ World | 1 |\n",
+ /// "| Hello │ World | 2 |\n",
+ /// "+-------┴-------+-----+",
+ /// )
+ /// )
+ /// ```
+ pub const fn get_vertical(&self) -> Line {
+ Line::new(
+ self.borders.vertical,
+ self.borders.intersection,
+ self.borders.top_intersection,
+ self.borders.bottom_intersection,
+ )
+ }
+
+ /// Sets a top border.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn top(mut self, c: char) -> Style<On, B, L, R, H, V, HLines, VLines>
+ where
+ for<'a> &'a mut VLines: IntoIterator<Item = &'a mut VerticalLine>,
+ {
+ self.borders.top = Some(c);
+
+ if self.borders.has_left() {
+ self.borders.top_left = Some(c);
+ }
+
+ if self.borders.has_right() {
+ self.borders.top_right = Some(c);
+ }
+
+ if self.borders.has_vertical() {
+ self.borders.top_intersection = Some(c);
+ }
+
+ for vl in &mut self.verticals {
+ vl.line.connector1 = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a bottom border.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn bottom(mut self, c: char) -> Style<T, On, L, R, H, V, HLines, VLines>
+ where
+ for<'a> &'a mut VLines: IntoIterator<Item = &'a mut VerticalLine>,
+ {
+ self.borders.bottom = Some(c);
+
+ if self.borders.has_left() {
+ self.borders.bottom_left = Some(c);
+ }
+
+ if self.borders.has_right() {
+ self.borders.bottom_right = Some(c);
+ }
+
+ if self.borders.has_vertical() {
+ self.borders.bottom_intersection = Some(c);
+ }
+
+ for vl in &mut self.verticals {
+ vl.line.connector2 = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a left border.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn left(mut self, c: char) -> Style<T, B, On, R, H, V, HLines, VLines>
+ where
+ for<'a> &'a mut HLines: IntoIterator<Item = &'a mut HorizontalLine>,
+ {
+ self.borders.left = Some(c);
+
+ if self.borders.has_top() {
+ self.borders.top_left = Some(c);
+ }
+
+ if self.borders.has_bottom() {
+ self.borders.bottom_left = Some(c);
+ }
+
+ if self.borders.has_horizontal() {
+ self.borders.left_intersection = Some(c);
+ }
+
+ for hl in &mut self.horizontals {
+ hl.line.connector1 = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a right border.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn right(mut self, c: char) -> Style<T, B, L, On, H, V, HLines, VLines>
+ where
+ for<'a> &'a mut HLines: IntoIterator<Item = &'a mut HorizontalLine>,
+ {
+ self.borders.right = Some(c);
+
+ if self.borders.has_top() {
+ self.borders.top_right = Some(c);
+ }
+
+ if self.borders.has_bottom() {
+ self.borders.bottom_right = Some(c);
+ }
+
+ if self.borders.has_horizontal() {
+ self.borders.right_intersection = Some(c);
+ }
+
+ for hl in &mut self.horizontals {
+ hl.line.connector2 = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a horizontal split line.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn horizontal(mut self, c: char) -> Style<T, B, L, R, On, V, HLines, VLines>
+ where
+ for<'a> &'a mut VLines: IntoIterator<Item = &'a mut VerticalLine>,
+ {
+ self.borders.horizontal = Some(c);
+
+ if self.borders.has_vertical() {
+ self.borders.intersection = Some(c);
+ }
+
+ if self.borders.has_left() {
+ self.borders.left_intersection = Some(c);
+ }
+
+ if self.borders.has_right() {
+ self.borders.right_intersection = Some(c);
+ }
+
+ for vl in &mut self.verticals {
+ vl.line.intersection = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Sets a vertical split line.
+ ///
+ /// Any corners and intersections which were set will be overridden.
+ pub fn vertical(mut self, c: char) -> Style<T, B, L, R, H, On, HLines, VLines>
+ where
+ for<'a> &'a mut HLines: IntoIterator<Item = &'a mut HorizontalLine>,
+ {
+ self.borders.vertical = Some(c);
+
+ if self.borders.has_horizontal() {
+ self.borders.intersection = Some(c);
+ }
+
+ if self.borders.has_top() {
+ self.borders.top_intersection = Some(c);
+ }
+
+ if self.borders.has_bottom() {
+ self.borders.bottom_intersection = Some(c);
+ }
+
+ for hl in &mut self.horizontals {
+ hl.line.intersection = Some(c);
+ }
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+
+ /// Set border horizontal lines.
+ ///
+ /// # Example
+ ///
+ #[cfg_attr(feature = "derive", doc = "```")]
+ #[cfg_attr(not(feature = "derive"), doc = "```ignore")]
+ /// use tabled::{settings::style::{Style, HorizontalLine, Line}, Table};
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", i)))
+ /// .with(Style::rounded().horizontals((1..4).map(|i| HorizontalLine::new(i, Line::filled('#')))))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "╭───────┬─────╮\n",
+ /// "│ &str │ i32 │\n",
+ /// "###############\n",
+ /// "│ Hello │ 0 │\n",
+ /// "###############\n",
+ /// "│ Hello │ 1 │\n",
+ /// "###############\n",
+ /// "│ Hello │ 2 │\n",
+ /// "╰───────┴─────╯",
+ /// )
+ /// )
+ /// ```
+ pub fn horizontals<NewLines>(self, lines: NewLines) -> Style<T, B, L, R, H, V, NewLines, VLines>
+ where
+ NewLines: IntoIterator<Item = HorizontalLine> + Clone,
+ {
+ Style::new(self.borders, lines, self.verticals)
+ }
+
+ /// Set border vertical lines.
+ ///
+ /// # Example
+ ///
+ #[cfg_attr(feature = "derive", doc = "```")]
+ #[cfg_attr(not(feature = "derive"), doc = "```ignore")]
+ /// use tabled::{Table, settings::style::{Style, VerticalLine, Line}};
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", i)))
+ /// .with(Style::rounded().verticals((0..3).map(|i| VerticalLine::new(i, Line::filled('#')))))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "#───────#─────#\n",
+ /// "# &str # i32 #\n",
+ /// "├───────┼─────┤\n",
+ /// "# Hello # 0 #\n",
+ /// "# Hello # 1 #\n",
+ /// "# Hello # 2 #\n",
+ /// "#───────#─────#",
+ /// )
+ /// )
+ /// ```
+ pub fn verticals<NewLines>(self, lines: NewLines) -> Style<T, B, L, R, H, V, HLines, NewLines>
+ where
+ NewLines: IntoIterator<Item = VerticalLine> + Clone,
+ {
+ Style::new(self.borders, self.horizontals, lines)
+ }
+
+ /// Removes all horizontal lines set by [`Style::horizontals`]
+ pub fn remove_horizontals(self) -> Style<T, B, L, R, H, V, HLineArray<0>, VLines> {
+ Style::new(self.borders, [], self.verticals)
+ }
+
+ /// Removes all verticals lines set by [`Style::verticals`]
+ pub fn remove_verticals(self) -> Style<T, B, L, R, H, V, HLines, VLineArray<0>> {
+ Style::new(self.borders, self.horizontals, [])
+ }
+}
+
+impl<B, R, H, V, HLines, VLines> Style<On, B, On, R, H, V, HLines, VLines> {
+ /// Sets a top left corner.
+ pub fn corner_top_left(mut self, c: char) -> Self {
+ self.borders.top_left = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<B, L, H, V, HLines, VLines> Style<On, B, L, On, H, V, HLines, VLines> {
+ /// Sets a top right corner.
+ pub fn corner_top_right(mut self, c: char) -> Self {
+ self.borders.top_right = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, L, H, V, HLines, VLines> Style<T, On, L, On, H, V, HLines, VLines> {
+ /// Sets a bottom right corner.
+ pub fn corner_bottom_right(mut self, c: char) -> Self {
+ self.borders.bottom_right = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, R, H, V, HLines, VLines> Style<T, On, On, R, H, V, HLines, VLines> {
+ /// Sets a bottom left corner.
+ pub fn corner_bottom_left(mut self, c: char) -> Self {
+ self.borders.bottom_left = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, B, R, V, HLines, VLines> Style<T, B, On, R, On, V, HLines, VLines> {
+ /// Sets a left intersection char.
+ pub fn intersection_left(mut self, c: char) -> Self {
+ self.borders.left_intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, B, L, V, HLines, VLines> Style<T, B, L, On, On, V, HLines, VLines> {
+ /// Sets a right intersection char.
+ pub fn intersection_right(mut self, c: char) -> Self {
+ self.borders.right_intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<B, L, R, H, HLines, VLines> Style<On, B, L, R, H, On, HLines, VLines> {
+ /// Sets a top intersection char.
+ pub fn intersection_top(mut self, c: char) -> Self {
+ self.borders.top_intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, L, R, H, HLines, VLines> Style<T, On, L, R, H, On, HLines, VLines> {
+ /// Sets a bottom intersection char.
+ pub fn intersection_bottom(mut self, c: char) -> Self {
+ self.borders.bottom_intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<T, B, L, R, HLines, VLines> Style<T, B, L, R, On, On, HLines, VLines> {
+ /// Sets an inner intersection char.
+ /// A char between horizontal and vertical split lines.
+ pub fn intersection(mut self, c: char) -> Self {
+ self.borders.intersection = Some(c);
+
+ Style::new(self.borders, self.horizontals, self.verticals)
+ }
+}
+
+impl<B, L, R, H, V, HLines, VLines> Style<On, B, L, R, H, V, HLines, VLines> {
+ /// Removes top border.
+ pub fn remove_top(
+ mut self,
+ ) -> Style<(), B, L, R, H, V, HLines, VerticalLineIter<VLines::IntoIter>>
+ where
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+ {
+ self.borders.top = None;
+ self.borders.top_intersection = None;
+ self.borders.top_left = None;
+ self.borders.top_right = None;
+
+ let iter = VerticalLineIter::new(self.verticals.into_iter(), false, true, false);
+ Style::new(self.borders, self.horizontals, iter)
+ }
+}
+
+impl<T, L, R, H, V, HLines, VLines> Style<T, On, L, R, H, V, HLines, VLines> {
+ /// Removes bottom border.
+ pub fn remove_bottom(
+ mut self,
+ ) -> Style<T, (), L, R, H, V, HLines, VerticalLineIter<VLines::IntoIter>>
+ where
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+ {
+ self.borders.bottom = None;
+ self.borders.bottom_intersection = None;
+ self.borders.bottom_left = None;
+ self.borders.bottom_right = None;
+
+ let iter = VerticalLineIter::new(self.verticals.into_iter(), false, false, true);
+ Style::new(self.borders, self.horizontals, iter)
+ }
+}
+
+impl<T, B, R, H, V, HLines, VLines> Style<T, B, On, R, H, V, HLines, VLines> {
+ /// Removes left border.
+ pub fn remove_left(
+ mut self,
+ ) -> Style<T, B, (), R, H, V, HorizontalLineIter<HLines::IntoIter>, VLines>
+ where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ {
+ self.borders.left = None;
+ self.borders.left_intersection = None;
+ self.borders.top_left = None;
+ self.borders.bottom_left = None;
+
+ let iter = HorizontalLineIter::new(self.horizontals.into_iter(), false, true, false);
+ Style::new(self.borders, iter, self.verticals)
+ }
+}
+
+impl<T, B, L, H, V, HLines, VLines> Style<T, B, L, On, H, V, HLines, VLines> {
+ /// Removes right border.
+ pub fn remove_right(
+ mut self,
+ ) -> Style<T, B, L, (), H, V, HorizontalLineIter<HLines::IntoIter>, VLines>
+ where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ {
+ self.borders.right = None;
+ self.borders.right_intersection = None;
+ self.borders.top_right = None;
+ self.borders.bottom_right = None;
+
+ let iter = HorizontalLineIter::new(self.horizontals.into_iter(), false, false, true);
+ Style::new(self.borders, iter, self.verticals)
+ }
+}
+
+impl<T, B, L, R, V, HLines, VLines> Style<T, B, L, R, On, V, HLines, VLines> {
+ /// Removes horizontal split lines.
+ ///
+ /// Not including custom split lines.
+ pub fn remove_horizontal(
+ mut self,
+ ) -> Style<T, B, L, R, (), V, HLines, VerticalLineIter<VLines::IntoIter>>
+ where
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+ {
+ self.borders.horizontal = None;
+ self.borders.left_intersection = None;
+ self.borders.right_intersection = None;
+ self.borders.intersection = None;
+
+ let iter = VerticalLineIter::new(self.verticals.into_iter(), true, false, false);
+ Style::new(self.borders, self.horizontals, iter)
+ }
+}
+
+impl<T, B, L, R, H, HLines, VLines> Style<T, B, L, R, H, On, HLines, VLines> {
+ /// Removes vertical split lines.
+ pub fn remove_vertical(
+ mut self,
+ ) -> Style<T, B, L, R, H, (), HorizontalLineIter<HLines::IntoIter>, VLines>
+ where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ {
+ self.borders.vertical = None;
+ self.borders.top_intersection = None;
+ self.borders.bottom_intersection = None;
+ self.borders.intersection = None;
+
+ let iter = HorizontalLineIter::new(self.horizontals.into_iter(), true, false, false);
+ Style::new(self.borders, iter, self.verticals)
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines> Style<T, B, L, R, H, V, HLines, VLines> {
+ const fn new(borders: Borders<char>, horizontals: HLines, verticals: VLines) -> Self {
+ Self {
+ borders,
+ horizontals,
+ verticals,
+ _top: PhantomData,
+ _bottom: PhantomData,
+ _left: PhantomData,
+ _right: PhantomData,
+ _horizontal: PhantomData,
+ _vertical: PhantomData,
+ }
+ }
+
+ /// Return borders of a table.
+ pub const fn get_borders(&self) -> &Borders<char> {
+ &self.borders
+ }
+
+ /// Return custom horizontals which were set.
+ pub const fn get_horizontals(&self) -> &HLines {
+ &self.horizontals
+ }
+
+ /// Return custom verticals which were set.
+ pub const fn get_verticals(&self) -> &VLines {
+ &self.verticals
+ }
+}
+
+#[cfg(feature = "std")]
+impl<T, B, L, R, H, V, HLines, VLines, I, D> TableOption<I, D, ColoredConfig>
+ for Style<T, B, L, R, H, V, HLines, VLines>
+where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+{
+ fn change(self, records: &mut I, cfg: &mut ColoredConfig, dimension: &mut D) {
+ cfg.clear_theme();
+
+ cfg.set_borders(self.borders);
+
+ for hl in self.horizontals {
+ hl.change(records, cfg, dimension);
+ }
+
+ for vl in self.verticals {
+ vl.change(records, cfg, dimension);
+ }
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines, I, D> TableOption<I, D, CompactConfig>
+ for Style<T, B, L, R, H, V, HLines, VLines>
+where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+{
+ fn change(self, records: &mut I, cfg: &mut CompactConfig, dimension: &mut D) {
+ *cfg = cfg.set_borders(self.borders);
+
+ let first_line = self.horizontals.into_iter().next();
+ if let Some(line) = first_line {
+ line.change(records, cfg, dimension);
+ }
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines, I, D> TableOption<I, D, CompactMultilineConfig>
+ for Style<T, B, L, R, H, V, HLines, VLines>
+where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+{
+ fn change(self, records: &mut I, cfg: &mut CompactMultilineConfig, dimension: &mut D) {
+ self.change(records, cfg.as_mut(), dimension)
+ }
+}
+
+/// An iterator which limits [`Line`] influence on iterations over lines for in [`Style`].
+#[derive(Debug, Clone)]
+pub struct HorizontalLineIter<I> {
+ iter: I,
+ intersection: bool,
+ left: bool,
+ right: bool,
+}
+
+impl<I> HorizontalLineIter<I> {
+ fn new(iter: I, intersection: bool, left: bool, right: bool) -> Self {
+ Self {
+ iter,
+ intersection,
+ left,
+ right,
+ }
+ }
+}
+
+impl<I> Iterator for HorizontalLineIter<I>
+where
+ I: Iterator<Item = HorizontalLine>,
+{
+ type Item = HorizontalLine;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let mut hl = self.iter.next()?;
+
+ if self.intersection {
+ hl.line.intersection = None;
+ }
+
+ if self.left {
+ hl.line.connector1 = None;
+ }
+
+ if self.right {
+ hl.line.connector2 = None;
+ }
+
+ Some(hl)
+ }
+}
+
+/// An iterator which limits [`Line`] influence on iterations over lines for in [`Style`].
+#[derive(Debug, Clone)]
+pub struct VerticalLineIter<I> {
+ iter: I,
+ intersection: bool,
+ top: bool,
+ bottom: bool,
+}
+
+impl<I> VerticalLineIter<I> {
+ fn new(iter: I, intersection: bool, top: bool, bottom: bool) -> Self {
+ Self {
+ iter,
+ intersection,
+ top,
+ bottom,
+ }
+ }
+}
+
+impl<I> Iterator for VerticalLineIter<I>
+where
+ I: Iterator<Item = VerticalLine>,
+{
+ type Item = VerticalLine;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let mut hl = self.iter.next()?;
+
+ if self.intersection {
+ hl.line.intersection = None;
+ }
+
+ if self.top {
+ hl.line.connector1 = None;
+ }
+
+ if self.bottom {
+ hl.line.connector2 = None;
+ }
+
+ Some(hl)
+ }
+}
+
+const fn create_borders(
+ top: Line,
+ bottom: Line,
+ horizontal: Line,
+ left: Option<char>,
+ right: Option<char>,
+ vertical: Option<char>,
+) -> Borders<char> {
+ Borders {
+ top: top.main,
+ bottom: bottom.main,
+ top_left: top.connector1,
+ top_right: top.connector2,
+ bottom_left: bottom.connector1,
+ bottom_right: bottom.connector2,
+ top_intersection: top.intersection,
+ bottom_intersection: bottom.intersection,
+ left_intersection: horizontal.connector1,
+ right_intersection: horizontal.connector2,
+ horizontal: horizontal.main,
+ intersection: horizontal.intersection,
+ left,
+ right,
+ vertical,
+ }
+}
diff --git a/vendor/tabled/src/settings/style/horizontal_line.rs b/vendor/tabled/src/settings/style/horizontal_line.rs
new file mode 100644
index 000000000..bbcdc3fb4
--- /dev/null
+++ b/vendor/tabled/src/settings/style/horizontal_line.rs
@@ -0,0 +1,69 @@
+use crate::{
+ grid::config::{CompactConfig, CompactMultilineConfig},
+ settings::TableOption,
+};
+
+#[cfg(feature = "std")]
+use crate::grid::config::{ColoredConfig, HorizontalLine as GridLine};
+
+use super::Line;
+
+/// A horizontal split line which can be used to set a border.
+#[cfg_attr(not(feature = "std"), allow(dead_code))]
+#[derive(Debug, Clone)]
+pub struct HorizontalLine {
+ pub(super) index: usize,
+ pub(super) line: Line,
+}
+
+impl HorizontalLine {
+ /// Creates a new horizontal split line.
+ pub const fn new(index: usize, line: Line) -> Self {
+ Self { index, line }
+ }
+
+ /// Sets a horizontal character.
+ pub const fn main(mut self, c: Option<char>) -> Self {
+ self.line.main = c;
+ self
+ }
+
+ /// Sets a vertical intersection character.
+ pub const fn intersection(mut self, c: Option<char>) -> Self {
+ self.line.intersection = c;
+ self
+ }
+
+ /// Sets a left character.
+ pub const fn left(mut self, c: Option<char>) -> Self {
+ self.line.connector1 = c;
+ self
+ }
+
+ /// Sets a right character.
+ pub const fn right(mut self, c: Option<char>) -> Self {
+ self.line.connector2 = c;
+ self
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, D> TableOption<R, D, ColoredConfig> for HorizontalLine {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ cfg.insert_horizontal_line(self.index, GridLine::from(self.line))
+ }
+}
+
+impl<R, D> TableOption<R, D, CompactConfig> for HorizontalLine {
+ fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
+ if self.index == 1 {
+ *cfg = cfg.set_first_horizontal_line(papergrid::config::Line::from(self.line));
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, CompactMultilineConfig> for HorizontalLine {
+ fn change(self, records: &mut R, cfg: &mut CompactMultilineConfig, dimension: &mut D) {
+ self.change(records, cfg.as_mut(), dimension)
+ }
+}
diff --git a/vendor/tabled/src/settings/style/line.rs b/vendor/tabled/src/settings/style/line.rs
new file mode 100644
index 000000000..10b3895c4
--- /dev/null
+++ b/vendor/tabled/src/settings/style/line.rs
@@ -0,0 +1,91 @@
+#[cfg(feature = "std")]
+use crate::grid::config::{HorizontalLine, VerticalLine};
+
+/// The structure represent a vertical or horizontal line.
+#[derive(Debug, Default, Clone, Copy)]
+pub struct Line {
+ pub(crate) main: Option<char>,
+ pub(crate) intersection: Option<char>,
+ pub(crate) connector1: Option<char>,
+ pub(crate) connector2: Option<char>,
+}
+
+impl Line {
+ /// Creates a new [`Line`] object.
+ pub const fn new(
+ main: Option<char>,
+ intersection: Option<char>,
+ connector1: Option<char>,
+ connector2: Option<char>,
+ ) -> Self {
+ Self {
+ main,
+ intersection,
+ connector1,
+ connector2,
+ }
+ }
+
+ /// Creates a new [`Line`] object with all chars set.
+ pub const fn full(main: char, intersection: char, connector1: char, connector2: char) -> Self {
+ Self::new(
+ Some(main),
+ Some(intersection),
+ Some(connector1),
+ Some(connector2),
+ )
+ }
+
+ /// Creates a new [`Line`] object with all chars set to the provided one.
+ pub const fn filled(c: char) -> Self {
+ Self::full(c, c, c, c)
+ }
+
+ /// Creates a new [`Line`] object with all chars not set.
+ pub const fn empty() -> Self {
+ Self::new(None, None, None, None)
+ }
+
+ /// Checks if the line has nothing set.
+ pub const fn is_empty(&self) -> bool {
+ self.main.is_none()
+ && self.intersection.is_none()
+ && self.connector1.is_none()
+ && self.connector2.is_none()
+ }
+}
+
+#[cfg(feature = "std")]
+impl From<Line> for HorizontalLine {
+ fn from(l: Line) -> Self {
+ Self {
+ main: l.main,
+ intersection: l.intersection,
+ left: l.connector1,
+ right: l.connector2,
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl From<Line> for VerticalLine {
+ fn from(l: Line) -> Self {
+ Self {
+ main: l.main,
+ intersection: l.intersection,
+ top: l.connector1,
+ bottom: l.connector2,
+ }
+ }
+}
+
+impl From<Line> for papergrid::config::Line<char> {
+ fn from(l: Line) -> Self {
+ Self {
+ main: l.main.unwrap_or(' '),
+ intersection: l.intersection,
+ connect1: l.connector1,
+ connect2: l.connector2,
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/style/mod.rs b/vendor/tabled/src/settings/style/mod.rs
new file mode 100644
index 000000000..b8d7f688d
--- /dev/null
+++ b/vendor/tabled/src/settings/style/mod.rs
@@ -0,0 +1,127 @@
+//! This module contains a list of primitives which can be applied to change [`Table`] style.
+//!
+//! ## [`Style`]
+//!
+//! It is responsible for a table border style.
+//! An individual cell border can be set by [`Border`].
+//!
+//! ### Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::Style};
+//!
+//! let data = vec!["Hello", "2022"];
+//! let mut table = Table::new(&data);
+//! table.with(Style::psql());
+//!
+//! assert_eq!(
+//! table.to_string(),
+//! concat!(
+//! " &str \n",
+//! "-------\n",
+//! " Hello \n",
+//! " 2022 ",
+//! )
+//! )
+//! ```
+//!
+//! ## [`BorderText`]
+//!
+//! It's used to override a border with a custom text.
+//!
+//! ### Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::style::{BorderText, Style}};
+//!
+//! let data = vec!["Hello", "2022"];
+//! let table = Table::new(&data)
+//! .with(Style::psql())
+//! .with(BorderText::new("Santa").horizontal(1))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! " &str \n",
+//! "Santa--\n",
+//! " Hello \n",
+//! " 2022 ",
+//! )
+//! )
+//! ```
+//!
+//! ## [`Border`]
+//!
+//! [`Border`] can be used to modify cell's borders.
+//!
+//! It's possible to set a collored border when `color` feature is on.
+//!
+//! ### Example
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//! use tabled::{Table, settings::{Modify, Style}};
+//!
+//! let data = vec!["Hello", "2022"];
+//! let table = Table::new(&data)
+//! .with(Style::psql())
+//! .with(Modify::new((0, 0)).with(Style::modern().get_frame()))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "┌───────┐\n",
+//! "│ &str │\n",
+//! "└───────┘\n",
+//! " Hello \n",
+//! " 2022 ",
+//! )
+//! )
+//! ```
+//!
+//! ## [`RawStyle`]
+//!
+//! A different representation of [`Style`].
+//! With no checks in place.
+//!
+//! It also contains a list of types to support colors.
+//!
+//! [`Table`]: crate::Table
+//! [`BorderText`]: crate::settings::style::BorderText
+//! [`RawStyle`]: crate::settings::style::RawStyle
+
+#[cfg(feature = "std")]
+mod border;
+#[cfg(feature = "std")]
+mod border_char;
+#[cfg(feature = "std")]
+mod border_color;
+#[cfg(feature = "std")]
+mod border_text;
+#[cfg(feature = "std")]
+mod offset;
+#[cfg(feature = "std")]
+mod raw_style;
+#[cfg(feature = "std")]
+mod span_border_correction;
+
+mod builder;
+mod horizontal_line;
+mod line;
+mod vertical_line;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use self::{
+ border::Border, border_char::BorderChar, border_color::BorderColor, border_text::BorderText,
+ offset::Offset, raw_style::RawStyle, span_border_correction::BorderSpanCorrection,
+};
+
+pub use builder::{HorizontalLineIter, On, Style, VerticalLineIter};
+pub use horizontal_line::HorizontalLine;
+pub use line::Line;
+pub use vertical_line::VerticalLine;
diff --git a/vendor/tabled/src/settings/style/offset.rs b/vendor/tabled/src/settings/style/offset.rs
new file mode 100644
index 000000000..3f8a4f85e
--- /dev/null
+++ b/vendor/tabled/src/settings/style/offset.rs
@@ -0,0 +1,19 @@
+use crate::grid::config;
+
+/// The structure represents an offset in a text.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum Offset {
+ /// An offset from the start.
+ Begin(usize),
+ /// An offset from the end.
+ End(usize),
+}
+
+impl From<Offset> for config::Offset {
+ fn from(o: Offset) -> Self {
+ match o {
+ Offset::Begin(i) => config::Offset::Begin(i),
+ Offset::End(i) => config::Offset::End(i),
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/style/raw_style.rs b/vendor/tabled/src/settings/style/raw_style.rs
new file mode 100644
index 000000000..eedb48e79
--- /dev/null
+++ b/vendor/tabled/src/settings/style/raw_style.rs
@@ -0,0 +1,438 @@
+//! This module contains [`RawStyle`] structure, which is analogues to [`Style`] but not generic,
+//! so sometimes it can be used more conviently.
+
+use std::collections::HashMap;
+
+use crate::{
+ grid::{color::AnsiColor, config, config::Borders, config::ColoredConfig, records::Records},
+ settings::{Color, TableOption},
+};
+
+use super::{Border, HorizontalLine, Line, Style, VerticalLine};
+
+/// A raw style data, which can be produced safely from [`Style`].
+///
+/// It can be useful in order to not have a generics and be able to use it as a variable more conveniently.
+#[derive(Default, Debug, Clone)]
+pub struct RawStyle {
+ borders: Borders<char>,
+ colors: Borders<AnsiColor<'static>>,
+ horizontals: HashMap<usize, Line>,
+ verticals: HashMap<usize, Line>,
+}
+
+impl RawStyle {
+ /// Set a top border character.
+ pub fn set_top(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.top = s;
+ self
+ }
+
+ /// Set a top border color.
+ pub fn set_color_top(&mut self, color: Color) -> &mut Self {
+ self.colors.top = Some(color.into());
+ self
+ }
+
+ /// Set a bottom border character.
+ pub fn set_bottom(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.bottom = s;
+ self
+ }
+
+ /// Set a bottom border color.
+ pub fn set_color_bottom(&mut self, color: Color) -> &mut Self {
+ self.colors.bottom = Some(color.into());
+ self
+ }
+
+ /// Set a left border character.
+ pub fn set_left(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.left = s;
+ self
+ }
+
+ /// Set a left border color.
+ pub fn set_color_left(&mut self, color: Color) -> &mut Self {
+ self.colors.left = Some(color.into());
+ self
+ }
+
+ /// Set a right border character.
+ pub fn set_right(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.right = s;
+ self
+ }
+
+ /// Set a right border color.
+ pub fn set_color_right(&mut self, color: Color) -> &mut Self {
+ self.colors.right = Some(color.into());
+ self
+ }
+
+ /// Set a top intersection character.
+ pub fn set_intersection_top(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.top_intersection = s;
+ self
+ }
+
+ /// Set a top intersection color.
+ pub fn set_color_intersection_top(&mut self, color: Color) -> &mut Self {
+ self.colors.top_intersection = Some(color.into());
+ self
+ }
+
+ /// Set a bottom intersection character.
+ pub fn set_intersection_bottom(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.bottom_intersection = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_intersection_bottom(&mut self, color: Color) -> &mut Self {
+ self.colors.bottom_intersection = Some(color.into());
+ self
+ }
+
+ /// Set a left split character.
+ pub fn set_intersection_left(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.left_intersection = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_intersection_left(&mut self, color: Color) -> &mut Self {
+ self.colors.left_intersection = Some(color.into());
+ self
+ }
+
+ /// Set a right split character.
+ pub fn set_intersection_right(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.right_intersection = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_intersection_right(&mut self, color: Color) -> &mut Self {
+ self.colors.right_intersection = Some(color.into());
+ self
+ }
+
+ /// Set an internal character.
+ pub fn set_intersection(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.intersection = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_intersection(&mut self, color: Color) -> &mut Self {
+ self.colors.intersection = Some(color.into());
+ self
+ }
+
+ /// Set a vertical character.
+ pub fn set_vertical(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.vertical = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_vertical(&mut self, color: Color) -> &mut Self {
+ self.colors.vertical = Some(color.into());
+ self
+ }
+
+ /// Set a horizontal character.
+ pub fn set_horizontal(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.horizontal = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_horizontal(&mut self, color: Color) -> &mut Self {
+ self.colors.horizontal = Some(color.into());
+ self
+ }
+
+ /// Set a character for a top left corner.
+ pub fn set_corner_top_left(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.top_left = s;
+ self
+ }
+ /// Set a bottom intersection color.
+ pub fn set_color_corner_top_left(&mut self, color: Color) -> &mut Self {
+ self.colors.top_left = Some(color.into());
+ self
+ }
+
+ /// Set a character for a top right corner.
+ pub fn set_corner_top_right(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.top_right = s;
+ self
+ }
+
+ /// Set a bottom intersection color.
+ pub fn set_color_corner_top_right(&mut self, color: Color) -> &mut Self {
+ self.colors.top_right = Some(color.into());
+ self
+ }
+
+ /// Set a character for a bottom left corner.
+ pub fn set_corner_bottom_left(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.bottom_left = s;
+ self
+ }
+ /// Set a bottom intersection color.
+ pub fn set_color_corner_bottom_left(&mut self, color: Color) -> &mut Self {
+ self.colors.bottom_left = Some(color.into());
+ self
+ }
+
+ /// Set a character for a bottom right corner.
+ pub fn set_corner_bottom_right(&mut self, s: Option<char>) -> &mut Self {
+ self.borders.bottom_right = s;
+ self
+ }
+ /// Set a bottom intersection color.
+ pub fn set_color_corner_bottom_right(&mut self, color: Color) -> &mut Self {
+ self.colors.bottom_right = Some(color.into());
+ self
+ }
+
+ /// Set horizontal border lines.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use std::collections::HashMap;
+ /// use tabled::{Table, settings::style::{Style, Line, RawStyle}};
+ ///
+ /// let mut style = RawStyle::from(Style::re_structured_text());
+ ///
+ /// let mut lines = HashMap::new();
+ /// lines.insert(1, Style::extended().get_horizontal());
+ /// style.set_horizontals(lines);
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", i)))
+ /// .with(style)
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// " ======= ===== \n",
+ /// " &str i32 \n",
+ /// "╠═══════╬═════╣\n",
+ /// " Hello 0 \n",
+ /// " Hello 1 \n",
+ /// " Hello 2 \n",
+ /// " ======= ===== ",
+ /// ),
+ /// )
+ /// ```
+ pub fn set_horizontals(&mut self, lines: HashMap<usize, Line>) -> &mut Self {
+ self.horizontals = lines;
+ self
+ }
+
+ /// Insert a horizontal line to a specific row location.
+ pub fn insert_horizontal(&mut self, row: usize, line: Line) -> &mut Self {
+ let _ = self.horizontals.insert(row, line);
+ self
+ }
+
+ /// Insert a horizontal line to a specific row location.
+ pub fn get_horizontal(&self, row: usize) -> Option<Line> {
+ self.horizontals.get(&row).cloned()
+ }
+
+ /// Set vertical border lines.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use std::collections::HashMap;
+ /// use tabled::{Table, settings::style::{Style, Line, RawStyle}};
+ ///
+ /// let mut style = RawStyle::from(Style::re_structured_text());
+ ///
+ /// let mut lines = HashMap::new();
+ /// lines.insert(1, Style::extended().get_horizontal());
+ /// style.set_verticals(lines);
+ ///
+ /// let table = Table::new((0..3).map(|i| ("Hello", i)))
+ /// .with(style)
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// concat!(
+ /// "=======╠=====\n",
+ /// " &str ═ i32 \n",
+ /// "======= =====\n",
+ /// " Hello ═ 0 \n",
+ /// " Hello ═ 1 \n",
+ /// " Hello ═ 2 \n",
+ /// "=======╣=====",
+ /// ),
+ /// )
+ /// ```
+ pub fn set_verticals(&mut self, lines: HashMap<usize, Line>) -> &mut Self {
+ self.verticals = lines;
+ self
+ }
+
+ /// Insert a vertical line into specific column location.
+ pub fn insert_vertical(&mut self, column: usize, line: Line) -> &mut Self {
+ let _ = self.verticals.insert(column, line);
+ self
+ }
+
+ /// Get a left char.
+ pub fn get_left(&self) -> Option<char> {
+ self.borders.left
+ }
+
+ /// Get a left intersection char.
+ pub fn get_left_intersection(&self) -> Option<char> {
+ self.borders.left_intersection
+ }
+
+ /// Get a right char.
+ pub fn get_right(&self) -> Option<char> {
+ self.borders.right
+ }
+
+ /// Get a right intersection char.
+ pub fn get_right_intersection(&self) -> Option<char> {
+ self.borders.right_intersection
+ }
+
+ /// Get a top char.
+ pub fn get_top(&self) -> Option<char> {
+ self.borders.top
+ }
+
+ /// Get a top left char.
+ pub fn get_top_left(&self) -> Option<char> {
+ self.borders.top_left
+ }
+
+ /// Get a top right char.
+ pub fn get_top_right(&self) -> Option<char> {
+ self.borders.top_right
+ }
+
+ /// Get a top intersection char.
+ pub fn get_top_intersection(&self) -> Option<char> {
+ self.borders.top_intersection
+ }
+
+ /// Get a bottom intersection char.
+ pub fn get_bottom(&self) -> Option<char> {
+ self.borders.bottom
+ }
+
+ /// Get a bottom intersection char.
+ pub fn get_bottom_left(&self) -> Option<char> {
+ self.borders.bottom_left
+ }
+
+ /// Get a bottom intersection char.
+ pub fn get_bottom_right(&self) -> Option<char> {
+ self.borders.bottom_right
+ }
+
+ /// Get a bottom intersection char.
+ pub fn get_bottom_intersection(&self) -> Option<char> {
+ self.borders.bottom_intersection
+ }
+
+ /// Returns an outer border of the style.
+ pub fn get_frame(&self) -> Border {
+ Border::from(crate::grid::config::Border {
+ top: self.borders.top,
+ bottom: self.borders.bottom,
+ left: self.borders.left,
+ right: self.borders.right,
+ left_top_corner: self.borders.top_left,
+ right_top_corner: self.borders.top_right,
+ left_bottom_corner: self.borders.bottom_left,
+ right_bottom_corner: self.borders.bottom_right,
+ })
+ }
+
+ /// Returns an general borders configuration of the style.
+ pub fn get_borders(&self) -> Borders<char> {
+ self.borders
+ }
+}
+
+impl From<Borders<char>> for RawStyle {
+ fn from(borders: Borders<char>) -> Self {
+ Self {
+ borders,
+ horizontals: HashMap::new(),
+ verticals: HashMap::new(),
+ colors: Borders::default(),
+ }
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for RawStyle
+where
+ R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dimension: &mut D) {
+ (&self).change(records, cfg, dimension)
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for &RawStyle {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ cfg.clear_theme();
+
+ cfg.set_borders(self.borders);
+
+ for (&row, line) in &self.horizontals {
+ cfg.insert_horizontal_line(row, config::HorizontalLine::from(*line));
+ }
+
+ for (&col, line) in &self.verticals {
+ cfg.insert_vertical_line(col, config::VerticalLine::from(*line));
+ }
+
+ if !self.colors.is_empty() {
+ cfg.set_borders_color(self.colors.clone());
+ }
+ }
+}
+
+impl<T, B, L, R, H, V, HLines, VLines> From<Style<T, B, L, R, H, V, HLines, VLines>> for RawStyle
+where
+ HLines: IntoIterator<Item = HorizontalLine> + Clone,
+ VLines: IntoIterator<Item = VerticalLine> + Clone,
+{
+ fn from(style: Style<T, B, L, R, H, V, HLines, VLines>) -> Self {
+ let horizontals = style
+ .get_horizontals()
+ .clone()
+ .into_iter()
+ .map(|hr| (hr.index, hr.line))
+ .collect();
+
+ let verticals = style
+ .get_verticals()
+ .clone()
+ .into_iter()
+ .map(|hr| (hr.index, hr.line))
+ .collect();
+
+ Self {
+ borders: *style.get_borders(),
+ horizontals,
+ verticals,
+ colors: Borders::default(),
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/style/span_border_correction.rs b/vendor/tabled/src/settings/style/span_border_correction.rs
new file mode 100644
index 000000000..665a12788
--- /dev/null
+++ b/vendor/tabled/src/settings/style/span_border_correction.rs
@@ -0,0 +1,216 @@
+//! This module contains [`BorderSpanCorrection`] structure, which can be useful when [`Span`] is used, and
+//! you want to fix the intersections symbols which are left intact by default.
+//!
+//! [`Span`]: crate::settings::span::Span
+
+use crate::{
+ grid::{
+ config::{ColoredConfig, Position, SpannedConfig},
+ records::{ExactRecords, Records},
+ },
+ settings::TableOption,
+};
+
+/// A correctness function of style for [`Table`] which has [`Span`]s.
+///
+/// Try to fix the style when table contains spans.
+///
+/// By default [`Style`] doesn't implies any logic to better render split lines when
+/// [`Span`] is used.
+///
+/// So this function can be used to set the split lines in regard of spans used.
+///
+/// # Example
+///
+/// ```
+/// use tabled::{
+/// Table,
+/// settings::{
+/// Modify, style::{Style, BorderSpanCorrection},
+/// Format, Span, object::Cell
+/// }
+/// };
+///
+/// let data = vec![
+/// ("09", "June", "2022"),
+/// ("10", "July", "2022"),
+/// ];
+///
+/// let mut table = Table::new(data);
+/// table.with(Modify::new((0, 0)).with("date").with(Span::column(3)));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// concat!(
+/// "+----+------+------+\n",
+/// "| date |\n",
+/// "+----+------+------+\n",
+/// "| 09 | June | 2022 |\n",
+/// "+----+------+------+\n",
+/// "| 10 | July | 2022 |\n",
+/// "+----+------+------+",
+/// )
+/// );
+///
+/// table.with(BorderSpanCorrection);
+///
+/// assert_eq!(
+/// table.to_string(),
+/// concat!(
+/// "+------------------+\n",
+/// "| date |\n",
+/// "+----+------+------+\n",
+/// "| 09 | June | 2022 |\n",
+/// "+----+------+------+\n",
+/// "| 10 | July | 2022 |\n",
+/// "+----+------+------+",
+/// )
+/// );
+/// ```
+/// See [`BorderSpanCorrection`].
+///
+/// [`Table`]: crate::Table
+/// [`Span`]: crate::settings::span::Span
+/// [`Style`]: crate::settings::Style
+/// [`Style::correct_spans`]: crate::settings::style::BorderSpanCorrection
+#[derive(Debug)]
+pub struct BorderSpanCorrection;
+
+impl<R, D> TableOption<R, D, ColoredConfig> for BorderSpanCorrection
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let shape = (records.count_rows(), records.count_columns());
+ correct_span_styles(cfg, shape);
+ }
+}
+
+fn correct_span_styles(cfg: &mut SpannedConfig, shape: (usize, usize)) {
+ for ((row, c), span) in cfg.get_column_spans() {
+ for col in c..c + span {
+ if col == 0 {
+ continue;
+ }
+
+ let is_first = col == c;
+ let has_up = row > 0 && has_left(cfg, (row - 1, col), shape);
+ let has_down = row + 1 < shape.0 && has_left(cfg, (row + 1, col), shape);
+
+ let borders = cfg.get_borders();
+
+ let mut border = cfg.get_border((row, col), shape);
+
+ let has_top_border = border.left_top_corner.is_some() && border.top.is_some();
+ if has_top_border {
+ if has_up && is_first {
+ border.left_top_corner = borders.intersection;
+ } else if has_up {
+ border.left_top_corner = borders.bottom_intersection;
+ } else if is_first {
+ border.left_top_corner = borders.top_intersection;
+ } else {
+ border.left_top_corner = border.top;
+ }
+ }
+
+ let has_bottom_border = border.left_bottom_corner.is_some() && border.bottom.is_some();
+ if has_bottom_border {
+ if has_down && is_first {
+ border.left_bottom_corner = borders.intersection;
+ } else if has_down {
+ border.left_bottom_corner = borders.top_intersection;
+ } else if is_first {
+ border.left_bottom_corner = borders.bottom_intersection;
+ } else {
+ border.left_bottom_corner = border.bottom;
+ }
+ }
+
+ cfg.set_border((row, col), border);
+ }
+ }
+
+ for ((r, col), span) in cfg.get_row_spans() {
+ for row in r + 1..r + span {
+ let mut border = cfg.get_border((row, col), shape);
+ let borders = cfg.get_borders();
+
+ let has_left_border = border.left_top_corner.is_some();
+ if has_left_border {
+ let has_left = col > 0 && has_top(cfg, (row, col - 1), shape);
+ if has_left {
+ border.left_top_corner = borders.right_intersection;
+ } else {
+ border.left_top_corner = borders.vertical;
+ }
+ }
+
+ let has_right_border = border.right_top_corner.is_some();
+ if has_right_border {
+ let has_right = col + 1 < shape.1 && has_top(cfg, (row, col + 1), shape);
+ if has_right {
+ border.right_top_corner = borders.left_intersection;
+ } else {
+ border.right_top_corner = borders.vertical;
+ }
+ }
+
+ cfg.set_border((row, col), border);
+ }
+ }
+
+ let cells = iter_totaly_spanned_cells(cfg, shape).collect::<Vec<_>>();
+ for (row, col) in cells {
+ if row == 0 {
+ continue;
+ }
+
+ let mut border = cfg.get_border((row, col), shape);
+ let borders = cfg.get_borders();
+
+ let has_right = col + 1 < shape.1 && has_top(cfg, (row, col + 1), shape);
+ let has_up = has_left(cfg, (row - 1, col), shape);
+ if has_up && !has_right {
+ border.right_top_corner = borders.right_intersection;
+ }
+
+ let has_down = row + 1 < shape.0 && has_left(cfg, (row + 1, col), shape);
+ if has_down {
+ border.left_bottom_corner = borders.top_intersection;
+ }
+
+ cfg.set_border((row, col), border);
+ }
+}
+
+fn has_left(cfg: &SpannedConfig, pos: Position, shape: (usize, usize)) -> bool {
+ if cfg.is_cell_covered_by_both_spans(pos) || cfg.is_cell_covered_by_column_span(pos) {
+ return false;
+ }
+
+ let border = cfg.get_border(pos, shape);
+ border.left.is_some() || border.left_top_corner.is_some() || border.left_bottom_corner.is_some()
+}
+
+fn has_top(cfg: &SpannedConfig, pos: Position, shape: (usize, usize)) -> bool {
+ if cfg.is_cell_covered_by_both_spans(pos) || cfg.is_cell_covered_by_row_span(pos) {
+ return false;
+ }
+
+ let border = cfg.get_border(pos, shape);
+ border.top.is_some() || border.left_top_corner.is_some() || border.right_top_corner.is_some()
+}
+
+fn iter_totaly_spanned_cells(
+ cfg: &SpannedConfig,
+ shape: (usize, usize),
+) -> impl Iterator<Item = Position> + '_ {
+ // todo: can be optimized
+ let (count_rows, count_cols) = shape;
+ (0..count_rows).flat_map(move |row| {
+ (0..count_cols)
+ .map(move |col| (row, col))
+ .filter(move |&p| cfg.is_cell_covered_by_both_spans(p))
+ })
+}
diff --git a/vendor/tabled/src/settings/style/vertical_line.rs b/vendor/tabled/src/settings/style/vertical_line.rs
new file mode 100644
index 000000000..adbef2c82
--- /dev/null
+++ b/vendor/tabled/src/settings/style/vertical_line.rs
@@ -0,0 +1,50 @@
+#[cfg(feature = "std")]
+use crate::grid::config::{ColoredConfig, VerticalLine as VLine};
+
+use super::Line;
+
+/// A horizontal split line which can be used to set a border.
+#[cfg_attr(not(feature = "std"), allow(dead_code))]
+#[derive(Debug, Clone)]
+pub struct VerticalLine {
+ pub(crate) index: usize,
+ pub(crate) line: Line,
+}
+
+impl VerticalLine {
+ /// Creates a new horizontal split line.
+ pub const fn new(index: usize, line: Line) -> Self {
+ Self { index, line }
+ }
+
+ /// Sets a horizontal character.
+ pub const fn main(mut self, c: Option<char>) -> Self {
+ self.line.main = c;
+ self
+ }
+
+ /// Sets a vertical intersection character.
+ pub const fn intersection(mut self, c: Option<char>) -> Self {
+ self.line.intersection = c;
+ self
+ }
+
+ /// Sets a top character.
+ pub const fn top(mut self, c: Option<char>) -> Self {
+ self.line.connector1 = c;
+ self
+ }
+
+ /// Sets a bottom character.
+ pub const fn bottom(mut self, c: Option<char>) -> Self {
+ self.line.connector2 = c;
+ self
+ }
+}
+
+#[cfg(feature = "std")]
+impl<R, D> crate::settings::TableOption<R, D, ColoredConfig> for VerticalLine {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ cfg.insert_vertical_line(self.index, VLine::from(self.line));
+ }
+}
diff --git a/vendor/tabled/src/settings/table_option.rs b/vendor/tabled/src/settings/table_option.rs
new file mode 100644
index 000000000..e20aa4480
--- /dev/null
+++ b/vendor/tabled/src/settings/table_option.rs
@@ -0,0 +1,30 @@
+/// A trait which is responsible for configuration of a [`Table`].
+///
+/// [`Table`]: crate::Table
+pub trait TableOption<R, D, C> {
+ /// The function allows modification of records and a grid configuration.
+ fn change(self, records: &mut R, cfg: &mut C, dimension: &mut D);
+}
+
+impl<T, R, D, C> TableOption<R, D, C> for &[T]
+where
+ for<'a> &'a T: TableOption<R, D, C>,
+{
+ fn change(self, records: &mut R, cfg: &mut C, dimension: &mut D) {
+ for opt in self {
+ opt.change(records, cfg, dimension)
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<T, R, D, C> TableOption<R, D, C> for Vec<T>
+where
+ T: TableOption<R, D, C>,
+{
+ fn change(self, records: &mut R, cfg: &mut C, dimension: &mut D) {
+ for opt in self {
+ opt.change(records, cfg, dimension)
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/themes/colorization.rs b/vendor/tabled/src/settings/themes/colorization.rs
new file mode 100644
index 000000000..41c721330
--- /dev/null
+++ b/vendor/tabled/src/settings/themes/colorization.rs
@@ -0,0 +1,388 @@
+use papergrid::{
+ color::AnsiColor,
+ config::{Entity, Sides},
+};
+
+use crate::{
+ grid::{
+ config::ColoredConfig,
+ records::{ExactRecords, Records},
+ },
+ settings::{object::Object, Color, TableOption},
+};
+
+/// [`Colorization`] sets a color for the whole table data (so it's not include the borders).
+///
+/// You can colorize borders in a different round using [`BorderColor`] or [`RawStyle`]
+///
+/// # Examples
+///
+/// ```
+/// use std::iter::FromIterator;
+///
+/// use tabled::builder::Builder;
+/// use tabled::settings::{style::BorderColor, themes::Colorization, Color, Style};
+///
+/// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]];
+///
+/// let color1 = Color::FG_BLACK | Color::BG_WHITE;
+/// let color2 = Color::BG_BLACK | Color::FG_WHITE;
+/// let color3 = Color::FG_RED | Color::BG_RED;
+///
+/// let mut table = Builder::from_iter(data).build();
+/// table
+/// .with(Colorization::chess(color1, color2))
+/// .with(Style::modern())
+/// .with(BorderColor::filled(color3));
+///
+/// println!("{table}");
+/// ```
+///
+/// [`RawStyle`]: crate::settings::style::RawStyle
+/// [`BorderColor`]: crate::settings::style::BorderColor
+#[derive(Debug, Clone)]
+pub struct Colorization {
+ pattern: ColorizationPattern,
+ colors: Vec<Color>,
+}
+
+#[derive(Debug, Clone)]
+enum ColorizationPattern {
+ Column,
+ Row,
+ ByRow,
+ ByColumn,
+ Chess,
+}
+
+impl Colorization {
+ /// Creates a [`Colorization`] with a chess pattern.
+ ///
+ /// ```
+ /// use std::iter::FromIterator;
+ ///
+ /// use tabled::builder::Builder;
+ /// use tabled::settings::{themes::Colorization, Color, Style};
+ ///
+ /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]];
+ ///
+ /// let color1 = Color::FG_BLACK | Color::BG_WHITE;
+ /// let color2 = Color::BG_BLACK | Color::FG_WHITE;
+ ///
+ /// let mut table = Builder::from_iter(data).build();
+ /// table
+ /// .with(Colorization::chess(color1, color2))
+ /// .with(Style::empty());
+ ///
+ /// println!("{table}");
+ /// ```
+ pub fn chess(white: Color, black: Color) -> Self {
+ Self::new(vec![white, black], ColorizationPattern::Chess)
+ }
+
+ /// Creates a [`Colorization`] with a target [`Object`].
+ ///
+ /// ```
+ /// use std::iter::FromIterator;
+ ///
+ /// use tabled::builder::Builder;
+ /// use tabled::settings::object::Rows;
+ /// use tabled::settings::{themes::Colorization, Color, Style};
+ ///
+ /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]];
+ ///
+ /// let color1 = Color::FG_BLACK | Color::BG_WHITE;
+ /// let color2 = Color::BG_BLACK | Color::FG_WHITE;
+ ///
+ /// let mut table = Builder::from_iter(data).build();
+ /// table
+ /// .with(Colorization::exact([color1, color2], Rows::first()))
+ /// .with(Style::empty());
+ ///
+ /// println!("{table}");
+ /// ```
+ pub fn exact<I, O>(colors: I, target: O) -> ExactColorization<O>
+ where
+ I: IntoIterator,
+ I::Item: Into<Color>,
+ {
+ let colors = colors.into_iter().map(Into::into).collect();
+ ExactColorization::new(colors, target)
+ }
+
+ /// Creates a [`Colorization`] with a pattern which changes row by row.
+ ///
+ /// ```
+ /// use std::iter::FromIterator;
+ ///
+ /// use tabled::builder::Builder;
+ /// use tabled::settings::object::Rows;
+ /// use tabled::settings::{themes::Colorization, Color, Style};
+ ///
+ /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]];
+ ///
+ /// let color1 = Color::FG_BLACK | Color::BG_WHITE;
+ /// let color2 = Color::BG_BLACK | Color::FG_WHITE;
+ ///
+ /// let mut table = Builder::from_iter(data).build();
+ /// table
+ /// .with(Colorization::rows([color1, color2]))
+ /// .with(Style::empty());
+ ///
+ /// println!("{table}");
+ /// ```
+ pub fn rows<I>(colors: I) -> Self
+ where
+ I: IntoIterator,
+ I::Item: Into<Color>,
+ {
+ Self::new(colors, ColorizationPattern::Row)
+ }
+
+ /// Creates a [`Colorization`] with a pattern which changes column by column.
+ ///
+ /// ```
+ /// use std::iter::FromIterator;
+ ///
+ /// use tabled::builder::Builder;
+ /// use tabled::settings::object::Rows;
+ /// use tabled::settings::{themes::Colorization, Color, Style};
+ ///
+ /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]];
+ ///
+ /// let color1 = Color::FG_BLACK | Color::BG_WHITE;
+ /// let color2 = Color::BG_BLACK | Color::FG_WHITE;
+ ///
+ /// let mut table = Builder::from_iter(data).build();
+ /// table
+ /// .with(Colorization::columns([color1, color2]))
+ /// .with(Style::empty());
+ ///
+ /// println!("{table}");
+ /// ```
+ pub fn columns<I>(colors: I) -> Self
+ where
+ I: IntoIterator,
+ I::Item: Into<Color>,
+ {
+ Self::new(colors, ColorizationPattern::Column)
+ }
+
+ /// Creates a [`Colorization`] with a pattern which peaks cells one by one iterating over rows.
+ ///
+ /// ```
+ /// use std::iter::FromIterator;
+ ///
+ /// use tabled::builder::Builder;
+ /// use tabled::settings::object::Rows;
+ /// use tabled::settings::{themes::Colorization, Color, Style};
+ ///
+ /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]];
+ ///
+ /// let color1 = Color::FG_BLACK | Color::BG_WHITE;
+ /// let color2 = Color::BG_BLACK | Color::FG_WHITE;
+ ///
+ /// let mut table = Builder::from_iter(data).build();
+ /// table
+ /// .with(Colorization::by_row([color1, color2]))
+ /// .with(Style::empty());
+ ///
+ /// println!("{table}");
+ /// ```
+ pub fn by_row<I>(colors: I) -> Self
+ where
+ I: IntoIterator,
+ I::Item: Into<Color>,
+ {
+ Self::new(colors, ColorizationPattern::ByRow)
+ }
+
+ /// Creates a [`Colorization`] with a pattern which peaks cells one by one iterating over columns.
+ ///
+ /// ```
+ /// use std::iter::FromIterator;
+ ///
+ /// use tabled::builder::Builder;
+ /// use tabled::settings::object::Rows;
+ /// use tabled::settings::{themes::Colorization, Color, Style};
+ ///
+ /// let data = [["Hello", "World"], ["Hi", "World"], ["Halo", "World"]];
+ ///
+ /// let color1 = Color::FG_BLACK | Color::BG_WHITE;
+ /// let color2 = Color::BG_BLACK | Color::FG_WHITE;
+ ///
+ /// let mut table = Builder::from_iter(data).build();
+ /// table
+ /// .with(Colorization::by_column([color1, color2]))
+ /// .with(Style::empty());
+ ///
+ /// println!("{table}");
+ /// ```
+ pub fn by_column<I>(colors: I) -> Self
+ where
+ I: IntoIterator,
+ I::Item: Into<Color>,
+ {
+ Self::new(colors, ColorizationPattern::ByColumn)
+ }
+
+ fn new<I>(colors: I, pattern: ColorizationPattern) -> Self
+ where
+ I: IntoIterator,
+ I::Item: Into<Color>,
+ {
+ let colors = colors.into_iter().map(Into::into).collect();
+ Self { colors, pattern }
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for Colorization
+where
+ R: Records + ExactRecords,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ if self.colors.is_empty() {
+ return;
+ }
+
+ let count_columns = records.count_columns();
+ let count_rows = records.count_rows();
+
+ match self.pattern {
+ ColorizationPattern::Column => colorize_columns(&self.colors, count_columns, cfg),
+ ColorizationPattern::Row => colorize_rows(&self.colors, count_rows, cfg),
+ ColorizationPattern::ByRow => {
+ colorize_by_row(&self.colors, count_rows, count_columns, cfg)
+ }
+ ColorizationPattern::ByColumn => {
+ colorize_by_column(&self.colors, count_rows, count_columns, cfg)
+ }
+ ColorizationPattern::Chess => {
+ colorize_diogonals(&self.colors, count_rows, count_columns, cfg)
+ }
+ }
+ }
+}
+
+fn colorize_columns(colors: &[Color], count_columns: usize, cfg: &mut ColoredConfig) {
+ for (col, color) in (0..count_columns).zip(colors.iter().cycle()) {
+ colorize_entity(color, Entity::Column(col), cfg);
+ }
+}
+
+fn colorize_rows(colors: &[Color], count_rows: usize, cfg: &mut ColoredConfig) {
+ for (row, color) in (0..count_rows).zip(colors.iter().cycle()) {
+ colorize_entity(color, Entity::Row(row), cfg);
+ }
+}
+
+fn colorize_by_row(
+ colors: &[Color],
+ count_rows: usize,
+ count_columns: usize,
+ cfg: &mut ColoredConfig,
+) {
+ let mut color_peek = colors.iter().cycle();
+ for row in 0..count_rows {
+ for col in 0..count_columns {
+ let color = color_peek.next().unwrap();
+ colorize_entity(color, Entity::Cell(row, col), cfg);
+ }
+ }
+}
+
+fn colorize_by_column(
+ colors: &[Color],
+ count_rows: usize,
+ count_columns: usize,
+ cfg: &mut ColoredConfig,
+) {
+ let mut color_peek = colors.iter().cycle();
+ for col in 0..count_columns {
+ for row in 0..count_rows {
+ let color = color_peek.next().unwrap();
+ colorize_entity(color, Entity::Cell(row, col), cfg);
+ }
+ }
+}
+
+fn colorize_diogonals(
+ colors: &[Color],
+ count_rows: usize,
+ count_columns: usize,
+ cfg: &mut ColoredConfig,
+) {
+ let mut color_peek = colors.iter().cycle();
+ for mut row in 0..count_rows {
+ let color = color_peek.next().unwrap();
+ for col in 0..count_columns {
+ colorize_entity(color, Entity::Cell(row, col), cfg);
+
+ row += 1;
+ if row == count_rows {
+ break;
+ }
+ }
+ }
+
+ let _ = color_peek.next().unwrap();
+
+ for mut col in 1..count_columns {
+ let color = color_peek.next().unwrap();
+ for row in 0..count_rows {
+ colorize_entity(color, Entity::Cell(row, col), cfg);
+
+ col += 1;
+ if col == count_columns {
+ break;
+ }
+ }
+ }
+}
+
+fn colorize_entity(color: &Color, pos: Entity, cfg: &mut ColoredConfig) {
+ let ansi_color = AnsiColor::from(color.clone());
+ let _ = cfg.set_color(pos, ansi_color.clone());
+ cfg.set_justification_color(pos, Some(ansi_color.clone()));
+ cfg.set_padding_color(
+ pos,
+ Sides::new(
+ Some(ansi_color.clone()),
+ Some(ansi_color.clone()),
+ Some(ansi_color.clone()),
+ Some(ansi_color),
+ ),
+ );
+}
+
+/// A colorization of a target [`Object`].
+///
+/// Can be created by [`Colorization::exact`].
+#[derive(Debug, Clone)]
+pub struct ExactColorization<O> {
+ colors: Vec<Color>,
+ target: O,
+}
+
+impl<O> ExactColorization<O> {
+ fn new(colors: Vec<Color>, target: O) -> Self {
+ Self { colors, target }
+ }
+}
+
+impl<R, D, O> TableOption<R, D, ColoredConfig> for ExactColorization<O>
+where
+ O: Object<R>,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ if self.colors.is_empty() {
+ return;
+ }
+
+ let mut color_peek = self.colors.iter().cycle();
+ for pos in self.target.cells(records) {
+ let color = color_peek.next().unwrap();
+ colorize_entity(color, pos, cfg);
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/themes/column_names.rs b/vendor/tabled/src/settings/themes/column_names.rs
new file mode 100644
index 000000000..02848166d
--- /dev/null
+++ b/vendor/tabled/src/settings/themes/column_names.rs
@@ -0,0 +1,355 @@
+use crate::{
+ grid::{
+ config::{AlignmentHorizontal, ColoredConfig},
+ dimension::{CompleteDimensionVecRecords, Dimension, Estimate},
+ records::{
+ vec_records::{CellInfo, VecRecords},
+ ExactRecords, PeekableRecords, Records, Resizable,
+ },
+ util::string::string_width,
+ },
+ settings::{
+ style::{BorderText, Offset},
+ Color, TableOption,
+ },
+};
+
+/// [`ColumnNames`] sets strings on horizontal lines for the columns.
+///
+/// Notice that using a [`Default`] would reuse a names from the first row.
+///
+/// # Examples
+///
+/// ```
+/// use std::iter::FromIterator;
+/// use tabled::{Table, settings::themes::ColumnNames};
+///
+/// let data = vec![
+/// vec!["Hello", "World"],
+/// vec!["Hello", "World"],
+/// ];
+///
+/// let mut table = Table::from_iter(data);
+/// table.with(ColumnNames::new(["head1", "head2"]).set_offset(3).set_line(2));
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+--------+--------+\n\
+/// | Hello | World |\n\
+/// +--------+--------+\n\
+/// | Hello | World |\n\
+/// +---head1+---head2+"
+/// );
+/// ```
+///
+/// [`Default`] usage.
+///
+/// ```
+/// use std::iter::FromIterator;
+/// use tabled::{Table, settings::themes::ColumnNames};
+///
+/// let data = vec![
+/// vec!["Hello", "World"],
+/// vec!["Hello", "World"],
+/// ];
+///
+/// let mut table = Table::from_iter(data);
+/// table.with(ColumnNames::default());
+///
+/// assert_eq!(
+/// table.to_string(),
+/// "+Hello--+World--+\n\
+/// | Hello | World |\n\
+/// +-------+-------+"
+/// );
+/// ```
+#[derive(Debug, Clone)]
+pub struct ColumnNames {
+ names: Option<Vec<String>>,
+ colors: Vec<Option<Color>>,
+ offset: usize,
+ line: usize,
+ alignment: AlignmentHorizontal,
+}
+
+impl Default for ColumnNames {
+ fn default() -> Self {
+ Self {
+ names: Default::default(),
+ colors: Default::default(),
+ offset: Default::default(),
+ line: Default::default(),
+ alignment: AlignmentHorizontal::Left,
+ }
+ }
+}
+
+impl ColumnNames {
+ /// Creates a [`ColumnNames`] with a given names.
+ ///
+ /// Using a [`Default`] would reuse a names from the first row.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use std::iter::FromIterator;
+ /// use tabled::{Table, settings::themes::ColumnNames};
+ ///
+ /// let mut table = Table::from_iter(vec![vec!["Hello", "World"]]);
+ /// table.with(ColumnNames::new(["head1", "head2"]));
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+head1--+head2--+\n\
+ /// | Hello | World |\n\
+ /// +-------+-------+"
+ /// );
+ /// ```
+ pub fn new<I>(names: I) -> Self
+ where
+ I: IntoIterator,
+ I::Item: Into<String>,
+ {
+ let names = names.into_iter().map(Into::into).collect::<Vec<_>>();
+ Self {
+ names: Some(names),
+ colors: Vec::new(),
+ offset: 0,
+ line: 0,
+ alignment: AlignmentHorizontal::Left,
+ }
+ }
+
+ /// Set color for the column names.
+ ///
+ /// By default there's no colors.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use std::iter::FromIterator;
+ /// use tabled::Table;
+ /// use tabled::settings::{Color, themes::ColumnNames};
+ ///
+ /// let mut table = Table::from_iter(vec![vec!["Hello", "World"]]);
+ /// table.with(ColumnNames::new(["head1", "head2"]).set_colors([Color::FG_RED]));
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+\u{1b}[31mh\u{1b}[39m\u{1b}[31me\u{1b}[39m\u{1b}[31ma\u{1b}[39m\u{1b}[31md\u{1b}[39m\u{1b}[31m1\u{1b}[39m--+head2--+\n\
+ /// | Hello | World |\n\
+ /// +-------+-------+"
+ /// );
+ /// ```
+ pub fn set_colors<I>(self, colors: I) -> Self
+ where
+ I: IntoIterator,
+ I::Item: Into<Option<Color>>,
+ {
+ let colors = colors.into_iter().map(Into::into).collect::<Vec<_>>();
+ Self {
+ names: self.names,
+ offset: self.offset,
+ line: self.line,
+ alignment: self.alignment,
+ colors,
+ }
+ }
+
+ /// Set a left offset after which the names will be used.
+ ///
+ /// By default there's no offset.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use std::iter::FromIterator;
+ /// use tabled::{Table, settings::themes::ColumnNames};
+ ///
+ /// let mut table = Table::from_iter(vec![vec!["Hello", "World"]]);
+ /// table.with(ColumnNames::new(["head1", "head2"]).set_offset(1));
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+-head1-+-head2-+\n\
+ /// | Hello | World |\n\
+ /// +-------+-------+"
+ /// );
+ /// ```
+ pub fn set_offset(self, i: usize) -> Self {
+ Self {
+ names: self.names,
+ colors: self.colors,
+ line: self.line,
+ alignment: self.alignment,
+ offset: i,
+ }
+ }
+
+ /// Set a horizontal line the names will be applied to.
+ ///
+ /// The default value is 0 (the top horizontal line).
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use std::iter::FromIterator;
+ /// use tabled::{Table, settings::themes::ColumnNames};
+ ///
+ /// let mut table = Table::from_iter(vec![vec!["Hello", "World"]]);
+ /// table.with(ColumnNames::new(["head1", "head2"]).set_line(1));
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+-------+-------+\n\
+ /// | Hello | World |\n\
+ /// +head1--+head2--+"
+ /// );
+ /// ```
+ pub fn set_line(self, i: usize) -> Self {
+ Self {
+ names: self.names,
+ colors: self.colors,
+ offset: self.offset,
+ alignment: self.alignment,
+ line: i,
+ }
+ }
+
+ /// Set an alignment for the names.
+ ///
+ /// By default it's left aligned.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use std::iter::FromIterator;
+ /// use tabled::{
+ /// Table,
+ /// settings::themes::ColumnNames,
+ /// grid::config::AlignmentHorizontal,
+ /// };
+ ///
+ /// let mut table = Table::from_iter(vec![vec!["Hello", "World"]]);
+ /// table.with(ColumnNames::new(["head1", "head2"]).set_alignment(AlignmentHorizontal::Right));
+ ///
+ /// assert_eq!(
+ /// table.to_string(),
+ /// "+--head1+--head2+\n\
+ /// | Hello | World |\n\
+ /// +-------+-------+"
+ /// );
+ /// ```
+ pub fn set_alignment(self, alignment: AlignmentHorizontal) -> Self {
+ Self {
+ names: self.names,
+ colors: self.colors,
+ offset: self.offset,
+ line: self.line,
+ alignment,
+ }
+ }
+}
+
+impl TableOption<VecRecords<CellInfo<String>>, CompleteDimensionVecRecords<'static>, ColoredConfig>
+ for ColumnNames
+{
+ fn change(
+ self,
+ records: &mut VecRecords<CellInfo<String>>,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ let names = match self.names {
+ Some(names) => names,
+ None => {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return;
+ }
+
+ let names = (0..records.count_columns())
+ .map(|column| records.get_text((0, column)))
+ .map(ToString::to_string)
+ .collect::<Vec<_>>();
+
+ records.remove_row(0);
+
+ names
+ }
+ };
+
+ let names = names.iter().map(|name| name.lines().next().unwrap_or(""));
+
+ dims.estimate(&*records, cfg);
+
+ let mut widths = (0..records.count_columns())
+ .map(|column| dims.get_width(column))
+ .collect::<Vec<_>>();
+
+ let names = names.take(widths.len());
+
+ let offset = self.offset;
+ widths
+ .iter_mut()
+ .zip(names.clone())
+ .for_each(|(width, text)| {
+ let name_width = string_width(text) + offset;
+ *width = std::cmp::max(name_width, *width);
+ });
+
+ let _ = dims.set_widths(widths.clone());
+
+ let mut total_width = 0;
+ for (i, (width, name)) in widths.iter().zip(names).enumerate() {
+ let color = get_color(&self.colors, i);
+ let offset = total_width + 1;
+ let btext = get_border_text(
+ name,
+ offset,
+ *width,
+ self.alignment,
+ self.offset,
+ self.line,
+ color,
+ );
+ btext.change(records, cfg, dims);
+
+ total_width += width + 1;
+ }
+ }
+}
+
+fn get_border_text(
+ text: &str,
+ offset: usize,
+ available: usize,
+ alignment: AlignmentHorizontal,
+ alignment_offset: usize,
+ line: usize,
+ color: Option<&Color>,
+) -> BorderText<usize> {
+ let name = text.to_string();
+ let left_indent = get_indent(text, alignment, alignment_offset, available);
+ let left_offset = Offset::Begin(offset + left_indent);
+ let mut btext = BorderText::new(name).horizontal(line).offset(left_offset);
+ if let Some(color) = color {
+ btext = btext.color(color.clone());
+ }
+
+ btext
+}
+
+fn get_color(colors: &[Option<Color>], i: usize) -> Option<&Color> {
+ colors.get(i).and_then(|color| match color {
+ Some(color) => Some(color),
+ None => None,
+ })
+}
+
+fn get_indent(text: &str, align: AlignmentHorizontal, offset: usize, available: usize) -> usize {
+ match align {
+ AlignmentHorizontal::Left => offset,
+ AlignmentHorizontal::Right => available - string_width(text) - offset,
+ AlignmentHorizontal::Center => (available - string_width(text)) / 2,
+ }
+}
diff --git a/vendor/tabled/src/settings/themes/mod.rs b/vendor/tabled/src/settings/themes/mod.rs
new file mode 100644
index 000000000..75cf458cc
--- /dev/null
+++ b/vendor/tabled/src/settings/themes/mod.rs
@@ -0,0 +1,9 @@
+//! The module contains a varieity of configurations of table, which often
+//! changes not a single setting.
+//! As such they are making relatively big changes to the configuration.
+
+mod colorization;
+mod column_names;
+
+pub use colorization::{Colorization, ExactColorization};
+pub use column_names::ColumnNames;
diff --git a/vendor/tabled/src/settings/width/justify.rs b/vendor/tabled/src/settings/width/justify.rs
new file mode 100644
index 000000000..03b2afe0d
--- /dev/null
+++ b/vendor/tabled/src/settings/width/justify.rs
@@ -0,0 +1,96 @@
+//! This module contains [`Justify`] structure, used to set an exact width to each column.
+
+use crate::{
+ grid::config::ColoredConfig,
+ grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ settings::{
+ measurement::{Max, Measurement, Min},
+ CellOption, TableOption, Width,
+ },
+};
+
+/// Justify sets all columns widths to the set value.
+///
+/// Be aware that it doesn't consider padding.
+/// So if you want to set a exact width you might need to use [`Padding`] to set it to 0.
+///
+/// ## Examples
+///
+/// ```
+/// use tabled::{Table, settings::{Width, Style, object::Segment, Padding, Modify}};
+///
+/// let data = ["Hello", "World", "!"];
+///
+/// let table = Table::new(&data)
+/// .with(Style::markdown())
+/// .with(Modify::new(Segment::all()).with(Padding::zero()))
+/// .with(Width::justify(3));
+/// ```
+///
+/// [`Max`] usage to justify by a max column width.
+///
+/// ```
+/// use tabled::{Table, settings::{width::Justify, Style}};
+///
+/// let data = ["Hello", "World", "!"];
+///
+/// let table = Table::new(&data)
+/// .with(Style::markdown())
+/// .with(Justify::max());
+/// ```
+///
+/// [`Padding`]: crate::settings::Padding
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Justify<W> {
+ width: W,
+}
+
+impl<W> Justify<W>
+where
+ W: Measurement<Width>,
+{
+ /// Creates a new [`Justify`] instance.
+ ///
+ /// Be aware that [`Padding`] is not considered when comparing the width.
+ ///
+ /// [`Padding`]: crate::settings::Padding
+ pub fn new(width: W) -> Self {
+ Self { width }
+ }
+}
+
+impl Justify<Max> {
+ /// Creates a new Justify instance with a Max width used as a value.
+ pub fn max() -> Self {
+ Self { width: Max }
+ }
+}
+
+impl Justify<Min> {
+ /// Creates a new Justify instance with a Min width used as a value.
+ pub fn min() -> Self {
+ Self { width: Min }
+ }
+}
+
+impl<R, D, W> TableOption<R, D, ColoredConfig> for Justify<W>
+where
+ W: Measurement<Width>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let width = self.width.measure(&*records, cfg);
+
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ for row in 0..count_rows {
+ for col in 0..count_columns {
+ let pos = (row, col).into();
+ CellOption::change(Width::increase(width), records, cfg, pos);
+ CellOption::change(Width::truncate(width), records, cfg, pos);
+ }
+ }
+ }
+}
diff --git a/vendor/tabled/src/settings/width/min_width.rs b/vendor/tabled/src/settings/width/min_width.rs
new file mode 100644
index 000000000..afb9ae302
--- /dev/null
+++ b/vendor/tabled/src/settings/width/min_width.rs
@@ -0,0 +1,205 @@
+//! This module contains [`MinWidth`] structure, used to increase width of a [`Table`]s or a cell on a [`Table`].
+//!
+//! [`Table`]: crate::Table
+
+use std::marker::PhantomData;
+
+use crate::{
+ grid::config::ColoredConfig,
+ grid::config::Entity,
+ grid::dimension::CompleteDimensionVecRecords,
+ grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
+ grid::util::string::{get_lines, string_width_multiline},
+ settings::{
+ measurement::Measurement,
+ peaker::{Peaker, PriorityNone},
+ CellOption, TableOption, Width,
+ },
+};
+
+use super::util::get_table_widths_with_total;
+
+/// [`MinWidth`] changes a content in case if it's length is lower then the boundary.
+///
+/// It can be applied to a whole table.
+///
+/// It does nothing in case if the content's length is bigger then the boundary.
+///
+/// Be aware that further changes of the table may cause the width being not set.
+/// For example applying [`Padding`] after applying [`MinWidth`] will make the former have no affect.
+/// (You should use [`Padding`] first).
+///
+/// Be aware that it doesn't consider padding.
+/// So if you want to set a exact width you might need to use [`Padding`] to set it to 0.
+///
+/// ## Examples
+///
+/// Cell change
+///
+/// ```
+/// use tabled::{Table, settings::{object::Segment, Width, Style, Modify}};
+///
+/// let data = ["Hello", "World", "!"];
+///
+/// let table = Table::new(&data)
+/// .with(Style::markdown())
+/// .with(Modify::new(Segment::all()).with(Width::increase(10)));
+/// ```
+/// Table change
+///
+/// ```
+/// use tabled::{Table, settings::Width};
+///
+/// let table = Table::new(&["Hello World!"]).with(Width::increase(5));
+/// ```
+///
+/// [`Padding`]: crate::settings::Padding
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct MinWidth<W = usize, P = PriorityNone> {
+ width: W,
+ fill: char,
+ _priority: PhantomData<P>,
+}
+
+impl<W> MinWidth<W>
+where
+ W: Measurement<Width>,
+{
+ /// Creates a new instance of [`MinWidth`].
+ pub fn new(width: W) -> Self {
+ Self {
+ width,
+ fill: ' ',
+ _priority: PhantomData,
+ }
+ }
+}
+
+impl<W, P> MinWidth<W, P> {
+ /// Set's a fill character which will be used to fill the space
+ /// when increasing the length of the string to the set boundary.
+ ///
+ /// Used only if chaning cells.
+ pub fn fill_with(mut self, c: char) -> Self {
+ self.fill = c;
+ self
+ }
+
+ /// Priority defines the logic by which a increase of width will be applied when is done for the whole table.
+ ///
+ /// - [`PriorityNone`] which inc the columns one after another.
+ /// - [`PriorityMax`] inc the biggest columns first.
+ /// - [`PriorityMin`] inc the lowest columns first.
+ ///
+ /// [`PriorityMax`]: crate::settings::peaker::PriorityMax
+ /// [`PriorityMin`]: crate::settings::peaker::PriorityMin
+ pub fn priority<PP: Peaker>(self) -> MinWidth<W, PP> {
+ MinWidth {
+ fill: self.fill,
+ width: self.width,
+ _priority: PhantomData,
+ }
+ }
+}
+
+impl<W, R> CellOption<R, ColoredConfig> for MinWidth<W>
+where
+ W: Measurement<Width>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let width = self.width.measure(&*records, cfg);
+
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_columns) {
+ let is_valid_pos = pos.0 < count_rows && pos.1 < count_columns;
+ if !is_valid_pos {
+ continue;
+ }
+
+ let cell = records.get_text(pos);
+ let cell_width = string_width_multiline(cell);
+ if cell_width >= width {
+ continue;
+ }
+
+ let content = increase_width(cell, width, self.fill);
+ records.set(pos, content);
+ }
+ }
+}
+
+impl<W, P, R> TableOption<R, CompleteDimensionVecRecords<'static>, ColoredConfig> for MinWidth<W, P>
+where
+ W: Measurement<Width>,
+ P: Peaker,
+ R: Records + ExactRecords + PeekableRecords,
+ for<'a> &'a R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return;
+ }
+
+ let nessary_width = self.width.measure(&*records, cfg);
+
+ let (widths, total_width) = get_table_widths_with_total(&*records, cfg);
+ if total_width >= nessary_width {
+ return;
+ }
+
+ let widths = get_increase_list(widths, nessary_width, total_width, P::create());
+ let _ = dims.set_widths(widths);
+ }
+}
+
+fn get_increase_list<F>(
+ mut widths: Vec<usize>,
+ need: usize,
+ mut current: usize,
+ mut peaker: F,
+) -> Vec<usize>
+where
+ F: Peaker,
+{
+ while need != current {
+ let col = match peaker.peak(&[], &widths) {
+ Some(col) => col,
+ None => break,
+ };
+
+ widths[col] += 1;
+ current += 1;
+ }
+
+ widths
+}
+
+fn increase_width(s: &str, width: usize, fill_with: char) -> String {
+ use crate::grid::util::string::string_width;
+ use std::{borrow::Cow, iter::repeat};
+
+ get_lines(s)
+ .map(|line| {
+ let length = string_width(&line);
+
+ if length < width {
+ let mut line = line.into_owned();
+ let remain = width - length;
+ line.extend(repeat(fill_with).take(remain));
+ Cow::Owned(line)
+ } else {
+ line
+ }
+ })
+ .collect::<Vec<_>>()
+ .join("\n")
+}
diff --git a/vendor/tabled/src/settings/width/mod.rs b/vendor/tabled/src/settings/width/mod.rs
new file mode 100644
index 000000000..c1202f70f
--- /dev/null
+++ b/vendor/tabled/src/settings/width/mod.rs
@@ -0,0 +1,163 @@
+//! This module contains object which can be used to limit a cell to a given width:
+//!
+//! - [`Truncate`] cuts a cell content to limit width.
+//! - [`Wrap`] split the content via new lines in order to fit max width.
+//! - [`Justify`] sets columns width to the same value.
+//!
+//! To set a a table width, a combination of [`Width::truncate`] or [`Width::wrap`] and [`Width::increase`] can be used.
+//!
+//! ## Example
+//!
+//! ```
+//! use tabled::{Table, settings::Width};
+//!
+//! let table = Table::new(&["Hello World!"])
+//! .with(Width::wrap(7))
+//! .with(Width::increase(7))
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! "+-----+\n",
+//! "| &st |\n",
+//! "| r |\n",
+//! "+-----+\n",
+//! "| Hel |\n",
+//! "| lo |\n",
+//! "| Wor |\n",
+//! "| ld! |\n",
+//! "+-----+",
+//! )
+//! );
+//! ```
+
+mod justify;
+mod min_width;
+mod truncate;
+mod util;
+mod width_list;
+mod wrap;
+
+use crate::settings::measurement::Measurement;
+
+pub use self::{
+ justify::Justify,
+ min_width::MinWidth,
+ truncate::{SuffixLimit, Truncate},
+ width_list::WidthList,
+ wrap::Wrap,
+};
+
+/// Width allows you to set a min and max width of an object on a [`Table`]
+/// using different strategies.
+///
+/// It also allows you to set a min and max width for a whole table.
+///
+/// You can apply a min and max strategy at the same time with the same value,
+/// the value will be a total table width.
+///
+/// It is an abstract factory.
+///
+/// Beware that borders are not removed when you set a size value to very small.
+/// For example if you set size to 0 the table still be rendered but with all content removed.
+///
+/// Also be aware that it doesn't changes [`Padding`] settings nor it considers them.
+///
+/// The function is color aware if a `color` feature is on.
+///
+/// ## Examples
+///
+/// ### Cell change
+///
+/// ```
+/// use tabled::{Table, settings::{object::Segment, Width, Style, Modify}};
+///
+/// let data = ["Hello", "World", "!"];
+///
+/// let table = Table::new(&data)
+/// .with(Style::markdown())
+/// .with(Modify::new(Segment::all()).with(Width::truncate(3).suffix("...")));
+/// ```
+///
+/// ### Table change
+///
+/// ```
+/// use tabled::{Table, settings::Width};
+///
+/// let table = Table::new(&["Hello World!"]).with(Width::wrap(5));
+/// ```
+///
+/// ### Total width
+///
+/// ```
+/// use tabled::{Table, settings::Width};
+///
+/// let table = Table::new(&["Hello World!"])
+/// .with(Width::wrap(5))
+/// .with(Width::increase(5));
+/// ```
+///
+/// [`Padding`]: crate::settings::Padding
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct Width;
+
+impl Width {
+ /// Returns a [`Wrap`] structure.
+ pub fn wrap<W: Measurement<Width>>(width: W) -> Wrap<W> {
+ Wrap::new(width)
+ }
+
+ /// Returns a [`Truncate`] structure.
+ pub fn truncate<W: Measurement<Width>>(width: W) -> Truncate<'static, W> {
+ Truncate::new(width)
+ }
+
+ /// Returns a [`MinWidth`] structure.
+ pub fn increase<W: Measurement<Width>>(width: W) -> MinWidth<W> {
+ MinWidth::new(width)
+ }
+
+ /// Returns a [`Justify`] structure.
+ pub fn justify<W: Measurement<Width>>(width: W) -> Justify<W> {
+ Justify::new(width)
+ }
+
+ /// Create [`WidthList`] to set a table width to a constant list of column widths.
+ ///
+ /// Notice if you provide a list with `.len()` smaller than `Table::count_columns` then it will have no affect.
+ ///
+ /// Also notice that you must provide values bigger than or equal to a real content width, otherwise it may panic.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use tabled::{Table, settings::Width};
+ ///
+ /// let data = vec![
+ /// ("Some\ndata", "here", "and here"),
+ /// ("Some\ndata on a next", "line", "right here"),
+ /// ];
+ ///
+ /// let table = Table::new(data)
+ /// .with(Width::list([20, 10, 12]))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+--------------------+----------+------------+\n\
+ /// | &str | &str | &str |\n\
+ /// +--------------------+----------+------------+\n\
+ /// | Some | here | and here |\n\
+ /// | data | | |\n\
+ /// +--------------------+----------+------------+\n\
+ /// | Some | line | right here |\n\
+ /// | data on a next | | |\n\
+ /// +--------------------+----------+------------+"
+ /// )
+ /// ```
+ pub fn list<I: IntoIterator<Item = usize>>(rows: I) -> WidthList {
+ WidthList::new(rows.into_iter().collect())
+ }
+}
diff --git a/vendor/tabled/src/settings/width/truncate.rs b/vendor/tabled/src/settings/width/truncate.rs
new file mode 100644
index 000000000..c7336f037
--- /dev/null
+++ b/vendor/tabled/src/settings/width/truncate.rs
@@ -0,0 +1,505 @@
+//! This module contains [`Truncate`] structure, used to decrease width of a [`Table`]s or a cell on a [`Table`] by truncating the width.
+//!
+//! [`Table`]: crate::Table
+
+use std::{borrow::Cow, iter, marker::PhantomData};
+
+use crate::{
+ grid::{
+ config::{ColoredConfig, SpannedConfig},
+ dimension::CompleteDimensionVecRecords,
+ records::{EmptyRecords, ExactRecords, PeekableRecords, Records, RecordsMut},
+ util::string::{string_width, string_width_multiline},
+ },
+ settings::{
+ measurement::Measurement,
+ peaker::{Peaker, PriorityNone},
+ CellOption, TableOption, Width,
+ },
+};
+
+use super::util::{cut_str, get_table_widths, get_table_widths_with_total};
+
+/// Truncate cut the string to a given width if its length exceeds it.
+/// Otherwise keeps the content of a cell untouched.
+///
+/// The function is color aware if a `color` feature is on.
+///
+/// Be aware that it doesn't consider padding.
+/// So if you want to set a exact width you might need to use [`Padding`] to set it to 0.
+///
+/// ## Example
+///
+/// ```
+/// use tabled::{Table, settings::{object::Segment, Width, Modify}};
+///
+/// let table = Table::new(&["Hello World!"])
+/// .with(Modify::new(Segment::all()).with(Width::truncate(3)));
+/// ```
+///
+/// [`Padding`]: crate::settings::Padding
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Truncate<'a, W = usize, P = PriorityNone> {
+ width: W,
+ suffix: Option<TruncateSuffix<'a>>,
+ multiline: bool,
+ _priority: PhantomData<P>,
+}
+#[cfg(feature = "color")]
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+struct TruncateSuffix<'a> {
+ text: Cow<'a, str>,
+ limit: SuffixLimit,
+ try_color: bool,
+}
+
+#[cfg(not(feature = "color"))]
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+struct TruncateSuffix<'a> {
+ text: Cow<'a, str>,
+ limit: SuffixLimit,
+}
+
+impl Default for TruncateSuffix<'_> {
+ fn default() -> Self {
+ Self {
+ text: Cow::default(),
+ limit: SuffixLimit::Cut,
+ #[cfg(feature = "color")]
+ try_color: false,
+ }
+ }
+}
+
+/// A suffix limit settings.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum SuffixLimit {
+ /// Cut the suffix.
+ Cut,
+ /// Don't show the suffix.
+ Ignore,
+ /// Use a string with n chars instead.
+ Replace(char),
+}
+
+impl<W> Truncate<'static, W>
+where
+ W: Measurement<Width>,
+{
+ /// Creates a [`Truncate`] object
+ pub fn new(width: W) -> Truncate<'static, W> {
+ Self {
+ width,
+ multiline: false,
+ suffix: None,
+ _priority: PhantomData,
+ }
+ }
+}
+
+impl<'a, W, P> Truncate<'a, W, P> {
+ /// Sets a suffix which will be appended to a resultant string.
+ ///
+ /// The suffix is used in 3 circumstances:
+ /// 1. If original string is *bigger* than the suffix.
+ /// We cut more of the original string and append the suffix.
+ /// 2. If suffix is bigger than the original string.
+ /// We cut the suffix to fit in the width by default.
+ /// But you can peak the behaviour by using [`Truncate::suffix_limit`]
+ pub fn suffix<S: Into<Cow<'a, str>>>(self, suffix: S) -> Truncate<'a, W, P> {
+ let mut suff = self.suffix.unwrap_or_default();
+ suff.text = suffix.into();
+
+ Truncate {
+ width: self.width,
+ multiline: self.multiline,
+ suffix: Some(suff),
+ _priority: PhantomData,
+ }
+ }
+
+ /// Sets a suffix limit, which is used when the suffix is too big to be used.
+ pub fn suffix_limit(self, limit: SuffixLimit) -> Truncate<'a, W, P> {
+ let mut suff = self.suffix.unwrap_or_default();
+ suff.limit = limit;
+
+ Truncate {
+ width: self.width,
+ multiline: self.multiline,
+ suffix: Some(suff),
+ _priority: PhantomData,
+ }
+ }
+
+ /// Use trancate logic per line, not as a string as a whole.
+ pub fn multiline(self) -> Truncate<'a, W, P> {
+ Truncate {
+ width: self.width,
+ multiline: true,
+ suffix: self.suffix,
+ _priority: self._priority,
+ }
+ }
+
+ #[cfg(feature = "color")]
+ /// Sets a optional logic to try to colorize a suffix.
+ pub fn suffix_try_color(self, color: bool) -> Truncate<'a, W, P> {
+ let mut suff = self.suffix.unwrap_or_default();
+ suff.try_color = color;
+
+ Truncate {
+ width: self.width,
+ multiline: self.multiline,
+ suffix: Some(suff),
+ _priority: PhantomData,
+ }
+ }
+}
+
+impl<'a, W, P> Truncate<'a, W, P> {
+ /// Priority defines the logic by which a truncate will be applied when is done for the whole table.
+ ///
+ /// - [`PriorityNone`] which cuts the columns one after another.
+ /// - [`PriorityMax`] cuts the biggest columns first.
+ /// - [`PriorityMin`] cuts the lowest columns first.
+ ///
+ /// [`PriorityMax`]: crate::settings::peaker::PriorityMax
+ /// [`PriorityMin`]: crate::settings::peaker::PriorityMin
+ pub fn priority<PP: Peaker>(self) -> Truncate<'a, W, PP> {
+ Truncate {
+ width: self.width,
+ multiline: self.multiline,
+ suffix: self.suffix,
+ _priority: PhantomData,
+ }
+ }
+}
+
+impl Truncate<'_, (), ()> {
+ /// Truncate a given string
+ pub fn truncate_text(text: &str, width: usize) -> Cow<'_, str> {
+ truncate_text(text, width, "", false)
+ }
+}
+
+impl<W, P, R> CellOption<R, ColoredConfig> for Truncate<'_, W, P>
+where
+ W: Measurement<Width>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: papergrid::config::Entity) {
+ let available = self.width.measure(&*records, cfg);
+
+ let mut width = available;
+ let mut suffix = Cow::Borrowed("");
+
+ if let Some(x) = self.suffix.as_ref() {
+ let (cutted_suffix, rest_width) = make_suffix(x, width);
+ suffix = cutted_suffix;
+ width = rest_width;
+ };
+
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ let colorize = need_suffix_color_preservation(&self.suffix);
+
+ for pos in entity.iter(count_rows, count_columns) {
+ let is_valid_pos = pos.0 < count_rows && pos.1 < count_columns;
+ if !is_valid_pos {
+ continue;
+ }
+
+ let text = records.get_text(pos);
+
+ let cell_width = string_width_multiline(text);
+ if available >= cell_width {
+ continue;
+ }
+
+ let text =
+ truncate_multiline(text, &suffix, width, available, colorize, self.multiline);
+
+ records.set(pos, text.into_owned());
+ }
+ }
+}
+
+fn truncate_multiline<'a>(
+ text: &'a str,
+ suffix: &'a str,
+ width: usize,
+ twidth: usize,
+ suffix_color: bool,
+ multiline: bool,
+) -> Cow<'a, str> {
+ if multiline {
+ let mut buf = String::new();
+ for (i, line) in crate::grid::util::string::get_lines(text).enumerate() {
+ if i != 0 {
+ buf.push('\n');
+ }
+
+ let line = make_text_truncated(&line, suffix, width, twidth, suffix_color);
+ buf.push_str(&line);
+ }
+
+ Cow::Owned(buf)
+ } else {
+ make_text_truncated(text, suffix, width, twidth, suffix_color)
+ }
+}
+
+fn make_text_truncated<'a>(
+ text: &'a str,
+ suffix: &'a str,
+ width: usize,
+ twidth: usize,
+ suffix_color: bool,
+) -> Cow<'a, str> {
+ if width == 0 {
+ if twidth == 0 {
+ Cow::Borrowed("")
+ } else {
+ Cow::Borrowed(suffix)
+ }
+ } else {
+ truncate_text(text, width, suffix, suffix_color)
+ }
+}
+
+fn need_suffix_color_preservation(_suffix: &Option<TruncateSuffix<'_>>) -> bool {
+ #[cfg(not(feature = "color"))]
+ {
+ false
+ }
+ #[cfg(feature = "color")]
+ {
+ _suffix.as_ref().map_or(false, |s| s.try_color)
+ }
+}
+
+fn make_suffix<'a>(suffix: &'a TruncateSuffix<'_>, width: usize) -> (Cow<'a, str>, usize) {
+ let suffix_length = string_width(&suffix.text);
+ if width > suffix_length {
+ return (Cow::Borrowed(suffix.text.as_ref()), width - suffix_length);
+ }
+
+ match suffix.limit {
+ SuffixLimit::Ignore => (Cow::Borrowed(""), width),
+ SuffixLimit::Cut => {
+ let suffix = cut_str(&suffix.text, width);
+ (suffix, 0)
+ }
+ SuffixLimit::Replace(c) => {
+ let suffix = Cow::Owned(iter::repeat(c).take(width).collect());
+ (suffix, 0)
+ }
+ }
+}
+
+impl<W, P, R> TableOption<R, CompleteDimensionVecRecords<'static>, ColoredConfig>
+ for Truncate<'_, W, P>
+where
+ W: Measurement<Width>,
+ P: Peaker,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return;
+ }
+
+ let width = self.width.measure(&*records, cfg);
+ let (widths, total) = get_table_widths_with_total(&*records, cfg);
+ if total <= width {
+ return;
+ }
+
+ let suffix = self.suffix.as_ref().map(|s| TruncateSuffix {
+ text: Cow::Borrowed(&s.text),
+ limit: s.limit,
+ #[cfg(feature = "color")]
+ try_color: s.try_color,
+ });
+
+ let priority = P::create();
+ let multiline = self.multiline;
+ let widths = truncate_total_width(
+ records, cfg, widths, total, width, priority, suffix, multiline,
+ );
+
+ let _ = dims.set_widths(widths);
+ }
+}
+
+#[allow(clippy::too_many_arguments)]
+fn truncate_total_width<P, R>(
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ mut widths: Vec<usize>,
+ total: usize,
+ width: usize,
+ priority: P,
+ suffix: Option<TruncateSuffix<'_>>,
+ multiline: bool,
+) -> Vec<usize>
+where
+ for<'a> &'a R: Records,
+ P: Peaker,
+ R: Records + PeekableRecords + ExactRecords + RecordsMut<String>,
+{
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ let min_widths = get_table_widths(EmptyRecords::new(count_rows, count_columns), cfg);
+
+ decrease_widths(&mut widths, &min_widths, total, width, priority);
+
+ let points = get_decrease_cell_list(cfg, &widths, &min_widths, (count_rows, count_columns));
+
+ for ((row, col), width) in points {
+ let mut truncate = Truncate::new(width);
+ truncate.suffix = suffix.clone();
+ truncate.multiline = multiline;
+ CellOption::change(truncate, records, cfg, (row, col).into());
+ }
+
+ widths
+}
+
+fn truncate_text<'a>(
+ text: &'a str,
+ width: usize,
+ suffix: &str,
+ _suffix_color: bool,
+) -> Cow<'a, str> {
+ let content = cut_str(text, width);
+ if suffix.is_empty() {
+ return content;
+ }
+
+ #[cfg(feature = "color")]
+ {
+ if _suffix_color {
+ if let Some(block) = ansi_str::get_blocks(text).last() {
+ if block.has_ansi() {
+ let style = block.style();
+ Cow::Owned(format!(
+ "{}{}{}{}",
+ content,
+ style.start(),
+ suffix,
+ style.end()
+ ))
+ } else {
+ let mut content = content.into_owned();
+ content.push_str(suffix);
+ Cow::Owned(content)
+ }
+ } else {
+ let mut content = content.into_owned();
+ content.push_str(suffix);
+ Cow::Owned(content)
+ }
+ } else {
+ let mut content = content.into_owned();
+ content.push_str(suffix);
+ Cow::Owned(content)
+ }
+ }
+
+ #[cfg(not(feature = "color"))]
+ {
+ let mut content = content.into_owned();
+ content.push_str(suffix);
+ Cow::Owned(content)
+ }
+}
+
+fn get_decrease_cell_list(
+ cfg: &SpannedConfig,
+ widths: &[usize],
+ min_widths: &[usize],
+ shape: (usize, usize),
+) -> Vec<((usize, usize), usize)> {
+ let mut points = Vec::new();
+ (0..shape.1).for_each(|col| {
+ (0..shape.0)
+ .filter(|&row| cfg.is_cell_visible((row, col)))
+ .for_each(|row| {
+ let (width, width_min) = match cfg.get_column_span((row, col)) {
+ Some(span) => {
+ let width = (col..col + span).map(|i| widths[i]).sum::<usize>();
+ let min_width = (col..col + span).map(|i| min_widths[i]).sum::<usize>();
+ let count_borders = count_borders(cfg, col, col + span, shape.1);
+ (width + count_borders, min_width + count_borders)
+ }
+ None => (widths[col], min_widths[col]),
+ };
+
+ if width >= width_min {
+ let padding = cfg.get_padding((row, col).into());
+ let width = width.saturating_sub(padding.left.size + padding.right.size);
+
+ points.push(((row, col), width));
+ }
+ });
+ });
+
+ points
+}
+
+fn decrease_widths<F>(
+ widths: &mut [usize],
+ min_widths: &[usize],
+ total_width: usize,
+ mut width: usize,
+ mut peeaker: F,
+) where
+ F: Peaker,
+{
+ let mut empty_list = 0;
+ for col in 0..widths.len() {
+ if widths[col] == 0 || widths[col] <= min_widths[col] {
+ empty_list += 1;
+ }
+ }
+
+ while width != total_width {
+ if empty_list == widths.len() {
+ break;
+ }
+
+ let col = match peeaker.peak(min_widths, widths) {
+ Some(col) => col,
+ None => break,
+ };
+
+ if widths[col] == 0 || widths[col] <= min_widths[col] {
+ continue;
+ }
+
+ widths[col] -= 1;
+
+ if widths[col] == 0 || widths[col] <= min_widths[col] {
+ empty_list += 1;
+ }
+
+ width += 1;
+ }
+}
+
+fn count_borders(cfg: &SpannedConfig, start: usize, end: usize, count_columns: usize) -> usize {
+ (start..end)
+ .skip(1)
+ .filter(|&i| cfg.has_vertical(i, count_columns))
+ .count()
+}
diff --git a/vendor/tabled/src/settings/width/util.rs b/vendor/tabled/src/settings/width/util.rs
new file mode 100644
index 000000000..92cc48c41
--- /dev/null
+++ b/vendor/tabled/src/settings/width/util.rs
@@ -0,0 +1,265 @@
+use std::borrow::Cow;
+
+use crate::{
+ grid::config::SpannedConfig, grid::dimension::SpannedGridDimension, grid::records::Records,
+};
+
+pub(crate) fn get_table_widths<R: Records>(records: R, cfg: &SpannedConfig) -> Vec<usize> {
+ SpannedGridDimension::width(records, cfg)
+}
+
+pub(crate) fn get_table_widths_with_total<R: Records>(
+ records: R,
+ cfg: &SpannedConfig,
+) -> (Vec<usize>, usize) {
+ let widths = SpannedGridDimension::width(records, cfg);
+ let total_width = get_table_total_width(&widths, cfg);
+ (widths, total_width)
+}
+
+fn get_table_total_width(list: &[usize], cfg: &SpannedConfig) -> usize {
+ let margin = cfg.get_margin();
+ list.iter().sum::<usize>()
+ + cfg.count_vertical(list.len())
+ + margin.left.size
+ + margin.right.size
+}
+
+/// The function cuts the string to a specific width.
+///
+/// BE AWARE: width is expected to be in bytes.
+pub(crate) fn cut_str(s: &str, width: usize) -> Cow<'_, str> {
+ #[cfg(feature = "color")]
+ {
+ const REPLACEMENT: char = '\u{FFFD}';
+
+ let stripped = ansi_str::AnsiStr::ansi_strip(s);
+ let (length, count_unknowns, _) = split_at_pos(&stripped, width);
+
+ let mut buf = ansi_str::AnsiStr::ansi_cut(s, ..length);
+ if count_unknowns > 0 {
+ let mut b = buf.into_owned();
+ b.extend(std::iter::repeat(REPLACEMENT).take(count_unknowns));
+ buf = Cow::Owned(b);
+ }
+
+ buf
+ }
+
+ #[cfg(not(feature = "color"))]
+ {
+ cut_str_basic(s, width)
+ }
+}
+
+/// The function cuts the string to a specific width.
+///
+/// BE AWARE: width is expected to be in bytes.
+#[cfg(not(feature = "color"))]
+pub(crate) fn cut_str_basic(s: &str, width: usize) -> Cow<'_, str> {
+ const REPLACEMENT: char = '\u{FFFD}';
+
+ let (length, count_unknowns, _) = split_at_pos(s, width);
+ let buf = &s[..length];
+ if count_unknowns == 0 {
+ return Cow::Borrowed(buf);
+ }
+
+ let mut buf = buf.to_owned();
+ buf.extend(std::iter::repeat(REPLACEMENT).take(count_unknowns));
+
+ Cow::Owned(buf)
+}
+
+/// The function splits a string in the position and
+/// returns a exact number of bytes before the position and in case of a split in an unicode grapheme
+/// a width of a character which was tried to be splited in.
+///
+/// BE AWARE: pos is expected to be in bytes.
+pub(crate) fn split_at_pos(s: &str, pos: usize) -> (usize, usize, usize) {
+ let mut length = 0;
+ let mut i = 0;
+ for c in s.chars() {
+ if i == pos {
+ break;
+ };
+
+ let c_width = unicode_width::UnicodeWidthChar::width(c).unwrap_or_default();
+
+ // We cut the chars which takes more then 1 symbol to display,
+ // in order to archive the necessary width.
+ if i + c_width > pos {
+ let count = pos - i;
+ return (length, count, c.len_utf8());
+ }
+
+ i += c_width;
+ length += c.len_utf8();
+ }
+
+ (length, 0, 0)
+}
+
+/// Strip OSC codes from `s`. If `s` is a single OSC8 hyperlink, with no other text, then return
+/// (s_with_all_hyperlinks_removed, Some(url)). If `s` does not meet this description, then return
+/// (s_with_all_hyperlinks_removed, None). Any ANSI color sequences in `s` will be retained. See
+/// <https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>
+///
+/// The function is based on Dan Davison <https://github.com/dandavison> delta <https://github.com/dandavison/delta> ansi library.
+#[cfg(feature = "color")]
+pub(crate) fn strip_osc(text: &str) -> (String, Option<String>) {
+ #[derive(Debug)]
+ enum ExtractOsc8HyperlinkState {
+ ExpectOsc8Url,
+ ExpectFirstText,
+ ExpectMoreTextOrTerminator,
+ SeenOneHyperlink,
+ WillNotReturnUrl,
+ }
+
+ use ExtractOsc8HyperlinkState::*;
+
+ let mut url = None;
+ let mut state = ExpectOsc8Url;
+ let mut buf = String::with_capacity(text.len());
+
+ for el in ansitok::parse_ansi(text) {
+ match el.kind() {
+ ansitok::ElementKind::Osc => match state {
+ ExpectOsc8Url => {
+ url = Some(&text[el.start()..el.end()]);
+ state = ExpectFirstText;
+ }
+ ExpectMoreTextOrTerminator => state = SeenOneHyperlink,
+ _ => state = WillNotReturnUrl,
+ },
+ ansitok::ElementKind::Sgr => buf.push_str(&text[el.start()..el.end()]),
+ ansitok::ElementKind::Csi => buf.push_str(&text[el.start()..el.end()]),
+ ansitok::ElementKind::Esc => {}
+ ansitok::ElementKind::Text => {
+ buf.push_str(&text[el.start()..el.end()]);
+ match state {
+ ExpectFirstText => state = ExpectMoreTextOrTerminator,
+ ExpectMoreTextOrTerminator => {}
+ _ => state = WillNotReturnUrl,
+ }
+ }
+ }
+ }
+
+ match state {
+ WillNotReturnUrl => (buf, None),
+ _ => {
+ let url = url.and_then(|s| {
+ s.strip_prefix("\x1b]8;;")
+ .and_then(|s| s.strip_suffix('\x1b'))
+ });
+ if let Some(url) = url {
+ (buf, Some(url.to_string()))
+ } else {
+ (buf, None)
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use crate::grid::util::string::string_width;
+
+ #[cfg(feature = "color")]
+ use owo_colors::{colors::Yellow, OwoColorize};
+
+ #[test]
+ fn strip_test() {
+ assert_eq!(cut_str("123456", 0), "");
+ assert_eq!(cut_str("123456", 3), "123");
+ assert_eq!(cut_str("123456", 10), "123456");
+
+ assert_eq!(cut_str("a week ago", 4), "a we");
+
+ assert_eq!(cut_str("😳😳😳😳😳", 0), "");
+ assert_eq!(cut_str("😳😳😳😳😳", 3), "😳�");
+ assert_eq!(cut_str("😳😳😳😳😳", 4), "😳😳");
+ assert_eq!(cut_str("😳😳😳😳😳", 20), "😳😳😳😳😳");
+
+ assert_eq!(cut_str("🏳️🏳️", 0), "");
+ assert_eq!(cut_str("🏳️🏳️", 1), "🏳");
+ assert_eq!(cut_str("🏳️🏳️", 2), "🏳\u{fe0f}🏳");
+ assert_eq!(string_width("🏳️🏳️"), string_width("🏳\u{fe0f}🏳"));
+
+ assert_eq!(cut_str("🎓", 1), "�");
+ assert_eq!(cut_str("🎓", 2), "🎓");
+
+ assert_eq!(cut_str("🥿", 1), "�");
+ assert_eq!(cut_str("🥿", 2), "🥿");
+
+ assert_eq!(cut_str("🩰", 1), "�");
+ assert_eq!(cut_str("🩰", 2), "🩰");
+
+ assert_eq!(cut_str("👍🏿", 1), "�");
+ assert_eq!(cut_str("👍🏿", 2), "👍");
+ assert_eq!(cut_str("👍🏿", 3), "👍�");
+ assert_eq!(cut_str("👍🏿", 4), "👍🏿");
+
+ assert_eq!(cut_str("🇻🇬", 1), "🇻");
+ assert_eq!(cut_str("🇻🇬", 2), "🇻🇬");
+ assert_eq!(cut_str("🇻🇬", 3), "🇻🇬");
+ assert_eq!(cut_str("🇻🇬", 4), "🇻🇬");
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn strip_color_test() {
+ let numbers = "123456".red().on_bright_black().to_string();
+
+ assert_eq!(cut_str(&numbers, 0), "\u{1b}[31;100m\u{1b}[39m\u{1b}[49m");
+ assert_eq!(
+ cut_str(&numbers, 3),
+ "\u{1b}[31;100m123\u{1b}[39m\u{1b}[49m"
+ );
+ assert_eq!(cut_str(&numbers, 10), "\u{1b}[31;100m123456\u{1b}[0m");
+
+ let emojies = "😳😳😳😳😳".red().on_bright_black().to_string();
+
+ assert_eq!(cut_str(&emojies, 0), "\u{1b}[31;100m\u{1b}[39m\u{1b}[49m");
+ assert_eq!(
+ cut_str(&emojies, 3),
+ "\u{1b}[31;100m😳\u{1b}[39m\u{1b}[49m�"
+ );
+ assert_eq!(
+ cut_str(&emojies, 4),
+ "\u{1b}[31;100m😳😳\u{1b}[39m\u{1b}[49m"
+ );
+ assert_eq!(cut_str(&emojies, 20), "\u{1b}[31;100m😳😳😳😳😳\u{1b}[0m");
+
+ let emojies = "🏳️🏳️".red().on_bright_black().to_string();
+
+ assert_eq!(cut_str(&emojies, 0), "\u{1b}[31;100m\u{1b}[39m\u{1b}[49m");
+ assert_eq!(cut_str(&emojies, 1), "\u{1b}[31;100m🏳\u{1b}[39m\u{1b}[49m");
+ assert_eq!(
+ cut_str(&emojies, 2),
+ "\u{1b}[31;100m🏳\u{fe0f}🏳\u{1b}[39m\u{1b}[49m"
+ );
+ assert_eq!(
+ string_width(&emojies),
+ string_width("\u{1b}[31;100m🏳\u{fe0f}🏳\u{1b}[39m\u{1b}[49m")
+ );
+ }
+
+ #[test]
+ #[cfg(feature = "color")]
+ fn test_color_strip() {
+ let s = "Collored string"
+ .fg::<Yellow>()
+ .on_truecolor(12, 200, 100)
+ .blink()
+ .to_string();
+ assert_eq!(
+ cut_str(&s, 1),
+ "\u{1b}[5m\u{1b}[48;2;12;200;100m\u{1b}[33mC\u{1b}[25m\u{1b}[39m\u{1b}[49m"
+ )
+ }
+}
diff --git a/vendor/tabled/src/settings/width/width_list.rs b/vendor/tabled/src/settings/width/width_list.rs
new file mode 100644
index 000000000..7547b97f3
--- /dev/null
+++ b/vendor/tabled/src/settings/width/width_list.rs
@@ -0,0 +1,50 @@
+use std::iter::FromIterator;
+
+use crate::{
+ grid::dimension::CompleteDimensionVecRecords, grid::records::Records, settings::TableOption,
+};
+
+/// A structure used to set [`Table`] width via a list of columns widths.
+///
+/// [`Table`]: crate::Table
+#[derive(Debug)]
+pub struct WidthList {
+ list: Vec<usize>,
+}
+
+impl WidthList {
+ /// Creates a new object.
+ pub fn new(list: Vec<usize>) -> Self {
+ Self { list }
+ }
+}
+
+impl From<Vec<usize>> for WidthList {
+ fn from(list: Vec<usize>) -> Self {
+ Self::new(list)
+ }
+}
+
+impl FromIterator<usize> for WidthList {
+ fn from_iter<T: IntoIterator<Item = usize>>(iter: T) -> Self {
+ Self::new(iter.into_iter().collect())
+ }
+}
+
+impl<R, C> TableOption<R, CompleteDimensionVecRecords<'static>, C> for WidthList
+where
+ R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ _: &mut C,
+ dimension: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ if self.list.len() < records.count_columns() {
+ return;
+ }
+
+ let _ = dimension.set_widths(self.list);
+ }
+}
diff --git a/vendor/tabled/src/settings/width/wrap.rs b/vendor/tabled/src/settings/width/wrap.rs
new file mode 100644
index 000000000..96a370408
--- /dev/null
+++ b/vendor/tabled/src/settings/width/wrap.rs
@@ -0,0 +1,1468 @@
+//! This module contains [`Wrap`] structure, used to decrease width of a [`Table`]s or a cell on a [`Table`] by wrapping it's content
+//! to a new line.
+//!
+//! [`Table`]: crate::Table
+
+use std::marker::PhantomData;
+
+use crate::{
+ grid::config::ColoredConfig,
+ grid::dimension::CompleteDimensionVecRecords,
+ grid::records::{EmptyRecords, ExactRecords, PeekableRecords, Records, RecordsMut},
+ grid::{config::Entity, config::SpannedConfig, util::string::string_width_multiline},
+ settings::{
+ measurement::Measurement,
+ peaker::{Peaker, PriorityNone},
+ width::Width,
+ CellOption, TableOption,
+ },
+};
+
+use super::util::{get_table_widths, get_table_widths_with_total, split_at_pos};
+
+/// Wrap wraps a string to a new line in case it exceeds the provided max boundary.
+/// Otherwise keeps the content of a cell untouched.
+///
+/// The function is color aware if a `color` feature is on.
+///
+/// Be aware that it doesn't consider padding.
+/// So if you want to set a exact width you might need to use [`Padding`] to set it to 0.
+///
+/// ## Example
+///
+/// ```
+/// use tabled::{Table, settings::{object::Segment, width::Width, Modify}};
+///
+/// let table = Table::new(&["Hello World!"])
+/// .with(Modify::new(Segment::all()).with(Width::wrap(3)));
+/// ```
+///
+/// [`Padding`]: crate::settings::Padding
+#[derive(Debug, Clone)]
+pub struct Wrap<W = usize, P = PriorityNone> {
+ width: W,
+ keep_words: bool,
+ _priority: PhantomData<P>,
+}
+
+impl<W> Wrap<W> {
+ /// Creates a [`Wrap`] object
+ pub fn new(width: W) -> Self
+ where
+ W: Measurement<Width>,
+ {
+ Wrap {
+ width,
+ keep_words: false,
+ _priority: PhantomData,
+ }
+ }
+}
+
+impl<W, P> Wrap<W, P> {
+ /// Priority defines the logic by which a truncate will be applied when is done for the whole table.
+ ///
+ /// - [`PriorityNone`] which cuts the columns one after another.
+ /// - [`PriorityMax`] cuts the biggest columns first.
+ /// - [`PriorityMin`] cuts the lowest columns first.
+ ///
+ /// Be aware that it doesn't consider padding.
+ /// So if you want to set a exact width you might need to use [`Padding`] to set it to 0.
+ ///
+ /// [`Padding`]: crate::settings::Padding
+ /// [`PriorityMax`]: crate::settings::peaker::PriorityMax
+ /// [`PriorityMin`]: crate::settings::peaker::PriorityMin
+ pub fn priority<PP>(self) -> Wrap<W, PP> {
+ Wrap {
+ width: self.width,
+ keep_words: self.keep_words,
+ _priority: PhantomData,
+ }
+ }
+
+ /// Set the keep words option.
+ ///
+ /// If a wrapping point will be in a word, [`Wrap`] will
+ /// preserve a word (if possible) and wrap the string before it.
+ pub fn keep_words(mut self) -> Self {
+ self.keep_words = true;
+ self
+ }
+}
+
+impl Wrap<(), ()> {
+ /// Wrap a given string
+ pub fn wrap_text(text: &str, width: usize, keeping_words: bool) -> String {
+ wrap_text(text, width, keeping_words)
+ }
+}
+
+impl<W, P, R> TableOption<R, CompleteDimensionVecRecords<'static>, ColoredConfig> for Wrap<W, P>
+where
+ W: Measurement<Width>,
+ P: Peaker,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(
+ self,
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ dims: &mut CompleteDimensionVecRecords<'static>,
+ ) {
+ if records.count_rows() == 0 || records.count_columns() == 0 {
+ return;
+ }
+
+ let width = self.width.measure(&*records, cfg);
+ let (widths, total) = get_table_widths_with_total(&*records, cfg);
+ if width >= total {
+ return;
+ }
+
+ let priority = P::create();
+ let keep_words = self.keep_words;
+ let widths = wrap_total_width(records, cfg, widths, total, width, keep_words, priority);
+
+ let _ = dims.set_widths(widths);
+ }
+}
+
+impl<W, R> CellOption<R, ColoredConfig> for Wrap<W>
+where
+ W: Measurement<Width>,
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ for<'a> &'a R: Records,
+{
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
+ let width = self.width.measure(&*records, cfg);
+
+ let count_rows = records.count_rows();
+ let count_columns = records.count_columns();
+
+ for pos in entity.iter(count_rows, count_columns) {
+ let is_valid_pos = pos.0 < records.count_rows() && pos.1 < records.count_columns();
+ if !is_valid_pos {
+ continue;
+ }
+
+ let text = records.get_text(pos);
+ let cell_width = string_width_multiline(text);
+ if cell_width <= width {
+ continue;
+ }
+
+ let wrapped = wrap_text(text, width, self.keep_words);
+ records.set(pos, wrapped);
+ }
+ }
+}
+
+fn wrap_total_width<R, P>(
+ records: &mut R,
+ cfg: &mut ColoredConfig,
+ mut widths: Vec<usize>,
+ total_width: usize,
+ width: usize,
+ keep_words: bool,
+ priority: P,
+) -> Vec<usize>
+where
+ R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
+ P: Peaker,
+ for<'a> &'a R: Records,
+{
+ let shape = (records.count_rows(), records.count_columns());
+ let min_widths = get_table_widths(EmptyRecords::from(shape), cfg);
+
+ decrease_widths(&mut widths, &min_widths, total_width, width, priority);
+
+ let points = get_decrease_cell_list(cfg, &widths, &min_widths, shape);
+
+ for ((row, col), width) in points {
+ let mut wrap = Wrap::new(width);
+ wrap.keep_words = keep_words;
+ <Wrap as CellOption<_, _>>::change(wrap, records, cfg, (row, col).into());
+ }
+
+ widths
+}
+
+#[cfg(not(feature = "color"))]
+pub(crate) fn wrap_text(text: &str, width: usize, keep_words: bool) -> String {
+ if width == 0 {
+ return String::new();
+ }
+
+ if keep_words {
+ split_keeping_words(text, width, "\n")
+ } else {
+ chunks(text, width).join("\n")
+ }
+}
+
+#[cfg(feature = "color")]
+pub(crate) fn wrap_text(text: &str, width: usize, keep_words: bool) -> String {
+ use super::util::strip_osc;
+
+ if width == 0 {
+ return String::new();
+ }
+
+ let (text, url): (String, Option<String>) = strip_osc(text);
+ let (prefix, suffix) = build_link_prefix_suffix(url);
+
+ if keep_words {
+ split_keeping_words(&text, width, &prefix, &suffix)
+ } else {
+ chunks(&text, width, &prefix, &suffix).join("\n")
+ }
+}
+
+#[cfg(feature = "color")]
+fn build_link_prefix_suffix(url: Option<String>) -> (String, String) {
+ match url {
+ Some(url) => {
+ // https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
+ let osc8 = "\x1b]8;;";
+ let st = "\x1b\\";
+
+ (format!("{osc8}{url}{st}"), format!("{osc8}{st}"))
+ }
+ None => ("".to_string(), "".to_string()),
+ }
+}
+
+#[cfg(not(feature = "color"))]
+fn chunks(s: &str, width: usize) -> Vec<String> {
+ if width == 0 {
+ return Vec::new();
+ }
+
+ const REPLACEMENT: char = '\u{FFFD}';
+
+ let mut buf = String::with_capacity(width);
+ let mut list = Vec::new();
+ let mut i = 0;
+ for c in s.chars() {
+ let c_width = unicode_width::UnicodeWidthChar::width(c).unwrap_or_default();
+ if i + c_width > width {
+ let count_unknowns = width - i;
+ buf.extend(std::iter::repeat(REPLACEMENT).take(count_unknowns));
+ i += count_unknowns;
+ } else {
+ buf.push(c);
+ i += c_width;
+ }
+
+ if i == width {
+ list.push(buf);
+ buf = String::with_capacity(width);
+ i = 0;
+ }
+ }
+
+ if !buf.is_empty() {
+ list.push(buf);
+ }
+
+ list
+}
+
+#[cfg(feature = "color")]
+fn chunks(s: &str, width: usize, prefix: &str, suffix: &str) -> Vec<String> {
+ use std::fmt::Write;
+
+ if width == 0 {
+ return Vec::new();
+ }
+
+ let mut list = Vec::new();
+ let mut line = String::with_capacity(width);
+ let mut line_width = 0;
+
+ for b in ansi_str::get_blocks(s) {
+ let text_style = b.style();
+ let mut text_slice = b.text();
+ if text_slice.is_empty() {
+ continue;
+ }
+
+ let available_space = width - line_width;
+ if available_space == 0 {
+ list.push(line);
+ line = String::with_capacity(width);
+ line_width = 0;
+ }
+
+ line.push_str(prefix);
+ let _ = write!(&mut line, "{}", text_style.start());
+
+ while !text_slice.is_empty() {
+ let available_space = width - line_width;
+
+ let part_width = unicode_width::UnicodeWidthStr::width(text_slice);
+ if part_width <= available_space {
+ line.push_str(text_slice);
+ line_width += part_width;
+
+ if available_space == 0 {
+ let _ = write!(&mut line, "{}", text_style.end());
+ line.push_str(suffix);
+ list.push(line);
+ line = String::with_capacity(width);
+ line.push_str(prefix);
+ line_width = 0;
+ let _ = write!(&mut line, "{}", text_style.start());
+ }
+
+ break;
+ }
+
+ let (lhs, rhs, (unknowns, split_char)) = split_string_at(text_slice, available_space);
+
+ text_slice = &rhs[split_char..];
+
+ line.push_str(lhs);
+ line_width += unicode_width::UnicodeWidthStr::width(lhs);
+
+ const REPLACEMENT: char = '\u{FFFD}';
+ line.extend(std::iter::repeat(REPLACEMENT).take(unknowns));
+ line_width += unknowns;
+
+ if line_width == width {
+ let _ = write!(&mut line, "{}", text_style.end());
+ line.push_str(suffix);
+ list.push(line);
+ line = String::with_capacity(width);
+ line.push_str(prefix);
+ line_width = 0;
+ let _ = write!(&mut line, "{}", text_style.start());
+ }
+ }
+
+ if line_width > 0 {
+ let _ = write!(&mut line, "{}", text_style.end());
+ }
+ }
+
+ if line_width > 0 {
+ line.push_str(suffix);
+ list.push(line);
+ }
+
+ list
+}
+
+#[cfg(not(feature = "color"))]
+fn split_keeping_words(s: &str, width: usize, sep: &str) -> String {
+ const REPLACEMENT: char = '\u{FFFD}';
+
+ let mut lines = Vec::new();
+ let mut line = String::with_capacity(width);
+ let mut line_width = 0;
+
+ let mut is_first_word = true;
+
+ for word in s.split(' ') {
+ if !is_first_word {
+ let line_has_space = line_width < width;
+ if line_has_space {
+ line.push(' ');
+ line_width += 1;
+ is_first_word = false;
+ }
+ }
+
+ if is_first_word {
+ is_first_word = false;
+ }
+
+ let word_width = unicode_width::UnicodeWidthStr::width(word);
+
+ let line_has_space = line_width + word_width <= width;
+ if line_has_space {
+ line.push_str(word);
+ line_width += word_width;
+ continue;
+ }
+
+ if word_width <= width {
+ // the word can be fit to 'width' so we put it on new line
+
+ line.extend(std::iter::repeat(' ').take(width - line_width));
+ lines.push(line);
+
+ line = String::with_capacity(width);
+ line_width = 0;
+
+ line.push_str(word);
+ line_width += word_width;
+ is_first_word = false;
+ } else {
+ // the word is too long any way so we split it
+
+ let mut word_part = word;
+ while !word_part.is_empty() {
+ let available_space = width - line_width;
+ let (lhs, rhs, (unknowns, split_char)) =
+ split_string_at(word_part, available_space);
+
+ word_part = &rhs[split_char..];
+ line_width += unicode_width::UnicodeWidthStr::width(lhs) + unknowns;
+ is_first_word = false;
+
+ line.push_str(lhs);
+ line.extend(std::iter::repeat(REPLACEMENT).take(unknowns));
+
+ if line_width == width {
+ lines.push(line);
+ line = String::with_capacity(width);
+ line_width = 0;
+ is_first_word = true;
+ }
+ }
+ }
+ }
+
+ if line_width > 0 {
+ line.extend(std::iter::repeat(' ').take(width - line_width));
+ lines.push(line);
+ }
+
+ lines.join(sep)
+}
+
+#[cfg(feature = "color")]
+fn split_keeping_words(text: &str, width: usize, prefix: &str, suffix: &str) -> String {
+ if text.is_empty() || width == 0 {
+ return String::new();
+ }
+
+ let stripped_text = ansi_str::AnsiStr::ansi_strip(text);
+ let mut word_width = 0;
+ let mut word_chars = 0;
+ let mut blocks = parsing::Blocks::new(ansi_str::get_blocks(text));
+ let mut buf = parsing::MultilineBuffer::new(width);
+ buf.set_prefix(prefix);
+ buf.set_suffix(suffix);
+
+ for c in stripped_text.chars() {
+ match c {
+ ' ' => {
+ parsing::handle_word(&mut buf, &mut blocks, word_chars, word_width, 1);
+ word_chars = 0;
+ word_width = 0;
+ }
+ '\n' => {
+ parsing::handle_word(&mut buf, &mut blocks, word_chars, word_width, 1);
+ word_chars = 0;
+ word_width = 0;
+ }
+ _ => {
+ word_width += unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
+ word_chars += 1;
+ }
+ }
+ }
+
+ if word_chars > 0 {
+ parsing::handle_word(&mut buf, &mut blocks, word_chars, word_width, 0);
+ buf.finish_line(&blocks);
+ }
+
+ buf.into_string()
+}
+
+#[cfg(feature = "color")]
+mod parsing {
+ use ansi_str::{AnsiBlock, AnsiBlockIter, Style};
+ use std::fmt::Write;
+
+ pub(super) struct Blocks<'a> {
+ iter: AnsiBlockIter<'a>,
+ current: Option<RelativeBlock<'a>>,
+ }
+
+ impl<'a> Blocks<'a> {
+ pub(super) fn new(iter: AnsiBlockIter<'a>) -> Self {
+ Self {
+ iter,
+ current: None,
+ }
+ }
+
+ pub(super) fn next_block(&mut self) -> Option<RelativeBlock<'a>> {
+ self.current
+ .take()
+ .or_else(|| self.iter.next().map(RelativeBlock::new))
+ }
+ }
+
+ pub(super) struct RelativeBlock<'a> {
+ block: AnsiBlock<'a>,
+ pos: usize,
+ }
+
+ impl<'a> RelativeBlock<'a> {
+ pub(super) fn new(block: AnsiBlock<'a>) -> Self {
+ Self { block, pos: 0 }
+ }
+
+ pub(super) fn get_text(&self) -> &str {
+ &self.block.text()[self.pos..]
+ }
+
+ pub(super) fn get_origin(&self) -> &str {
+ self.block.text()
+ }
+
+ pub(super) fn get_style(&self) -> &Style {
+ self.block.style()
+ }
+ }
+
+ pub(super) struct MultilineBuffer<'a> {
+ buf: String,
+ width_last: usize,
+ width: usize,
+ prefix: &'a str,
+ suffix: &'a str,
+ }
+
+ impl<'a> MultilineBuffer<'a> {
+ pub(super) fn new(width: usize) -> Self {
+ Self {
+ buf: String::new(),
+ width_last: 0,
+ prefix: "",
+ suffix: "",
+ width,
+ }
+ }
+
+ pub(super) fn into_string(self) -> String {
+ self.buf
+ }
+
+ pub(super) fn set_suffix(&mut self, suffix: &'a str) {
+ self.suffix = suffix;
+ }
+
+ pub(super) fn set_prefix(&mut self, prefix: &'a str) {
+ self.prefix = prefix;
+ }
+
+ pub(super) fn max_width(&self) -> usize {
+ self.width
+ }
+
+ pub(super) fn available_width(&self) -> usize {
+ self.width - self.width_last
+ }
+
+ pub(super) fn fill(&mut self, c: char) -> usize {
+ debug_assert_eq!(unicode_width::UnicodeWidthChar::width(c), Some(1));
+
+ let rest_width = self.available_width();
+ for _ in 0..rest_width {
+ self.buf.push(c);
+ }
+
+ rest_width
+ }
+
+ pub(super) fn set_next_line(&mut self, blocks: &Blocks<'_>) {
+ if let Some(block) = &blocks.current {
+ let _ = self
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().end()));
+ }
+
+ self.buf.push_str(self.suffix);
+
+ let _ = self.fill(' ');
+ self.buf.push('\n');
+ self.width_last = 0;
+
+ self.buf.push_str(self.prefix);
+
+ if let Some(block) = &blocks.current {
+ let _ = self
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().start()));
+ }
+ }
+
+ pub(super) fn finish_line(&mut self, blocks: &Blocks<'_>) {
+ if let Some(block) = &blocks.current {
+ let _ = self
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().end()));
+ }
+
+ self.buf.push_str(self.suffix);
+
+ let _ = self.fill(' ');
+ self.width_last = 0;
+ }
+
+ pub(super) fn read_chars(&mut self, block: &RelativeBlock<'_>, n: usize) -> (usize, usize) {
+ let mut count_chars = 0;
+ let mut count_bytes = 0;
+ for c in block.get_text().chars() {
+ if count_chars == n {
+ break;
+ }
+
+ count_chars += 1;
+ count_bytes += c.len_utf8();
+
+ let cwidth = unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
+
+ let available_space = self.width - self.width_last;
+ if available_space == 0 {
+ let _ = self
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().end()));
+ self.buf.push_str(self.suffix);
+ self.buf.push('\n');
+ self.buf.push_str(self.prefix);
+ let _ = self
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().start()));
+ self.width_last = 0;
+ }
+
+ let is_enough_space = self.width_last + cwidth <= self.width;
+ if !is_enough_space {
+ // thereatically a cwidth can be 2 but buf_width is 1
+ // but it handled here too;
+
+ const REPLACEMENT: char = '\u{FFFD}';
+ let _ = self.fill(REPLACEMENT);
+ self.width_last = self.width;
+ } else {
+ self.buf.push(c);
+ self.width_last += cwidth;
+ }
+ }
+
+ (count_chars, count_bytes)
+ }
+
+ pub(super) fn read_chars_unchecked(
+ &mut self,
+ block: &RelativeBlock<'_>,
+ n: usize,
+ ) -> (usize, usize) {
+ let mut count_chars = 0;
+ let mut count_bytes = 0;
+ for c in block.get_text().chars() {
+ if count_chars == n {
+ break;
+ }
+
+ count_chars += 1;
+ count_bytes += c.len_utf8();
+
+ let cwidth = unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
+ self.width_last += cwidth;
+
+ self.buf.push(c);
+ }
+
+ debug_assert!(self.width_last <= self.width);
+
+ (count_chars, count_bytes)
+ }
+ }
+
+ pub(super) fn read_chars(buf: &mut MultilineBuffer<'_>, blocks: &mut Blocks<'_>, n: usize) {
+ let mut n = n;
+ while n > 0 {
+ let is_new_block = blocks.current.is_none();
+ let mut block = blocks.next_block().expect("Must never happen");
+ if is_new_block {
+ buf.buf.push_str(buf.prefix);
+ let _ = buf
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().start()));
+ }
+
+ let (read_count, read_bytes) = buf.read_chars(&block, n);
+
+ if block.pos + read_bytes == block.get_origin().len() {
+ let _ = buf
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().end()));
+ } else {
+ block.pos += read_bytes;
+ blocks.current = Some(block);
+ }
+
+ n -= read_count;
+ }
+ }
+
+ pub(super) fn read_chars_unchecked(
+ buf: &mut MultilineBuffer<'_>,
+ blocks: &mut Blocks<'_>,
+ n: usize,
+ ) {
+ let mut n = n;
+ while n > 0 {
+ let is_new_block = blocks.current.is_none();
+ let mut block = blocks.next_block().expect("Must never happen");
+
+ if is_new_block {
+ buf.buf.push_str(buf.prefix);
+ let _ = buf
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().start()));
+ }
+
+ let (read_count, read_bytes) = buf.read_chars_unchecked(&block, n);
+
+ if block.pos + read_bytes == block.get_origin().len() {
+ let _ = buf
+ .buf
+ .write_fmt(format_args!("{}", block.get_style().end()));
+ } else {
+ block.pos += read_bytes;
+ blocks.current = Some(block);
+ }
+
+ n -= read_count;
+ }
+ }
+
+ pub(super) fn handle_word(
+ buf: &mut MultilineBuffer<'_>,
+ blocks: &mut Blocks<'_>,
+ word_chars: usize,
+ word_width: usize,
+ additional_read: usize,
+ ) {
+ if word_chars > 0 {
+ let has_line_space = word_width <= buf.available_width();
+ let is_word_too_big = word_width > buf.max_width();
+
+ if is_word_too_big {
+ read_chars(buf, blocks, word_chars + additional_read);
+ } else if has_line_space {
+ read_chars_unchecked(buf, blocks, word_chars);
+ if additional_read > 0 {
+ read_chars(buf, blocks, additional_read);
+ }
+ } else {
+ buf.set_next_line(&*blocks);
+ read_chars_unchecked(buf, blocks, word_chars);
+ if additional_read > 0 {
+ read_chars(buf, blocks, additional_read);
+ }
+ }
+
+ return;
+ }
+
+ let has_current_line_space = additional_read <= buf.available_width();
+ if has_current_line_space {
+ read_chars_unchecked(buf, blocks, additional_read);
+ } else {
+ buf.set_next_line(&*blocks);
+ read_chars_unchecked(buf, blocks, additional_read);
+ }
+ }
+}
+
+fn split_string_at(text: &str, at: usize) -> (&str, &str, (usize, usize)) {
+ let (length, count_unknowns, split_char_size) = split_at_pos(text, at);
+ let (lhs, rhs) = text.split_at(length);
+
+ (lhs, rhs, (count_unknowns, split_char_size))
+}
+
+fn decrease_widths<F>(
+ widths: &mut [usize],
+ min_widths: &[usize],
+ total_width: usize,
+ mut width: usize,
+ mut peeaker: F,
+) where
+ F: Peaker,
+{
+ let mut empty_list = 0;
+ for col in 0..widths.len() {
+ if widths[col] == 0 || widths[col] <= min_widths[col] {
+ empty_list += 1;
+ }
+ }
+
+ while width != total_width {
+ if empty_list == widths.len() {
+ break;
+ }
+
+ let col = match peeaker.peak(min_widths, widths) {
+ Some(col) => col,
+ None => break,
+ };
+
+ if widths[col] == 0 || widths[col] <= min_widths[col] {
+ continue;
+ }
+
+ widths[col] -= 1;
+
+ if widths[col] == 0 || widths[col] <= min_widths[col] {
+ empty_list += 1;
+ }
+
+ width += 1;
+ }
+}
+
+fn get_decrease_cell_list(
+ cfg: &SpannedConfig,
+ widths: &[usize],
+ min_widths: &[usize],
+ shape: (usize, usize),
+) -> Vec<((usize, usize), usize)> {
+ let mut points = Vec::new();
+ (0..shape.1).for_each(|col| {
+ (0..shape.0)
+ .filter(|&row| cfg.is_cell_visible((row, col)))
+ .for_each(|row| {
+ let (width, width_min) = match cfg.get_column_span((row, col)) {
+ Some(span) => {
+ let width = (col..col + span).map(|i| widths[i]).sum::<usize>();
+ let min_width = (col..col + span).map(|i| min_widths[i]).sum::<usize>();
+ let count_borders = count_borders(cfg, col, col + span, shape.1);
+ (width + count_borders, min_width + count_borders)
+ }
+ None => (widths[col], min_widths[col]),
+ };
+
+ if width >= width_min {
+ let padding = cfg.get_padding((row, col).into());
+ let width = width.saturating_sub(padding.left.size + padding.right.size);
+
+ points.push(((row, col), width));
+ }
+ });
+ });
+
+ points
+}
+
+fn count_borders(cfg: &SpannedConfig, start: usize, end: usize, count_columns: usize) -> usize {
+ (start..end)
+ .skip(1)
+ .filter(|&i| cfg.has_vertical(i, count_columns))
+ .count()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn split_test() {
+ #[cfg(not(feature = "color"))]
+ let split = |text, width| chunks(text, width).join("\n");
+
+ #[cfg(feature = "color")]
+ let split = |text, width| chunks(text, width, "", "").join("\n");
+
+ assert_eq!(split("123456", 0), "");
+
+ assert_eq!(split("123456", 1), "1\n2\n3\n4\n5\n6");
+ assert_eq!(split("123456", 2), "12\n34\n56");
+ assert_eq!(split("12345", 2), "12\n34\n5");
+ assert_eq!(split("123456", 6), "123456");
+ assert_eq!(split("123456", 10), "123456");
+
+ assert_eq!(split("😳😳😳😳😳", 1), "�\n�\n�\n�\n�");
+ assert_eq!(split("😳😳😳😳😳", 2), "😳\n😳\n😳\n😳\n😳");
+ assert_eq!(split("😳😳😳😳😳", 3), "😳�\n😳�\n😳");
+ assert_eq!(split("😳😳😳😳😳", 6), "😳😳😳\n😳😳");
+ assert_eq!(split("😳😳😳😳😳", 20), "😳😳😳😳😳");
+
+ assert_eq!(split("😳123😳", 1), "�\n1\n2\n3\n�");
+ assert_eq!(split("😳12😳3", 1), "�\n1\n2\n�\n3");
+ }
+
+ #[test]
+ fn chunks_test() {
+ #[allow(clippy::redundant_closure)]
+ #[cfg(not(feature = "color"))]
+ let chunks = |text, width| chunks(text, width);
+
+ #[cfg(feature = "color")]
+ let chunks = |text, width| chunks(text, width, "", "");
+
+ assert_eq!(chunks("123456", 0), [""; 0]);
+
+ assert_eq!(chunks("123456", 1), ["1", "2", "3", "4", "5", "6"]);
+ assert_eq!(chunks("123456", 2), ["12", "34", "56"]);
+ assert_eq!(chunks("12345", 2), ["12", "34", "5"]);
+
+ assert_eq!(chunks("😳😳😳😳😳", 1), ["�", "�", "�", "�", "�"]);
+ assert_eq!(chunks("😳😳😳😳😳", 2), ["😳", "😳", "😳", "😳", "😳"]);
+ assert_eq!(chunks("😳😳😳😳😳", 3), ["😳�", "😳�", "😳"]);
+ }
+
+ #[cfg(not(feature = "color"))]
+ #[test]
+ fn split_by_line_keeping_words_test() {
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "\n");
+
+ assert_eq!(split_keeping_words("123456", 1), "1\n2\n3\n4\n5\n6");
+ assert_eq!(split_keeping_words("123456", 2), "12\n34\n56");
+ assert_eq!(split_keeping_words("12345", 2), "12\n34\n5 ");
+
+ assert_eq!(split_keeping_words("😳😳😳😳😳", 1), "�\n�\n�\n�\n�");
+
+ assert_eq!(split_keeping_words("111 234 1", 4), "111 \n234 \n1 ");
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn split_by_line_keeping_words_test() {
+ #[cfg(feature = "color")]
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "", "");
+
+ assert_eq!(split_keeping_words("123456", 1), "1\n2\n3\n4\n5\n6");
+ assert_eq!(split_keeping_words("123456", 2), "12\n34\n56");
+ assert_eq!(split_keeping_words("12345", 2), "12\n34\n5 ");
+
+ assert_eq!(split_keeping_words("😳😳😳😳😳", 1), "�\n�\n�\n�\n�");
+
+ assert_eq!(split_keeping_words("111 234 1", 4), "111 \n234 \n1 ");
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn split_by_line_keeping_words_color_test() {
+ #[cfg(feature = "color")]
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "", "");
+
+ #[cfg(not(feature = "color"))]
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "\n");
+
+ let text = "\u{1b}[36mJapanese “vacancy” button\u{1b}[0m";
+
+ assert_eq!(split_keeping_words(text, 2), "\u{1b}[36mJa\u{1b}[39m\n\u{1b}[36mpa\u{1b}[39m\n\u{1b}[36mne\u{1b}[39m\n\u{1b}[36mse\u{1b}[39m\n\u{1b}[36m “\u{1b}[39m\n\u{1b}[36mva\u{1b}[39m\n\u{1b}[36mca\u{1b}[39m\n\u{1b}[36mnc\u{1b}[39m\n\u{1b}[36my”\u{1b}[39m\n\u{1b}[36m b\u{1b}[39m\n\u{1b}[36mut\u{1b}[39m\n\u{1b}[36mto\u{1b}[39m\n\u{1b}[36mn\u{1b}[39m ");
+ assert_eq!(split_keeping_words(text, 1), "\u{1b}[36mJ\u{1b}[39m\n\u{1b}[36ma\u{1b}[39m\n\u{1b}[36mp\u{1b}[39m\n\u{1b}[36ma\u{1b}[39m\n\u{1b}[36mn\u{1b}[39m\n\u{1b}[36me\u{1b}[39m\n\u{1b}[36ms\u{1b}[39m\n\u{1b}[36me\u{1b}[39m\n\u{1b}[36m \u{1b}[39m\n\u{1b}[36m“\u{1b}[39m\n\u{1b}[36mv\u{1b}[39m\n\u{1b}[36ma\u{1b}[39m\n\u{1b}[36mc\u{1b}[39m\n\u{1b}[36ma\u{1b}[39m\n\u{1b}[36mn\u{1b}[39m\n\u{1b}[36mc\u{1b}[39m\n\u{1b}[36my\u{1b}[39m\n\u{1b}[36m”\u{1b}[39m\n\u{1b}[36m \u{1b}[39m\n\u{1b}[36mb\u{1b}[39m\n\u{1b}[36mu\u{1b}[39m\n\u{1b}[36mt\u{1b}[39m\n\u{1b}[36mt\u{1b}[39m\n\u{1b}[36mo\u{1b}[39m\n\u{1b}[36mn\u{1b}[39m");
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn split_by_line_keeping_words_color_2_test() {
+ use ansi_str::AnsiStr;
+
+ #[cfg(feature = "color")]
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "", "");
+
+ #[cfg(not(feature = "color"))]
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "\n");
+
+ let text = "\u{1b}[37mTigre Ecuador OMYA Andina 3824909999 Calcium carbonate Colombia\u{1b}[0m";
+
+ assert_eq!(
+ split_keeping_words(text, 2)
+ .ansi_split("\n")
+ .collect::<Vec<_>>(),
+ [
+ "\u{1b}[37mTi\u{1b}[39m",
+ "\u{1b}[37mgr\u{1b}[39m",
+ "\u{1b}[37me \u{1b}[39m",
+ "\u{1b}[37mEc\u{1b}[39m",
+ "\u{1b}[37mua\u{1b}[39m",
+ "\u{1b}[37mdo\u{1b}[39m",
+ "\u{1b}[37mr \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mOM\u{1b}[39m",
+ "\u{1b}[37mYA\u{1b}[39m",
+ "\u{1b}[37m A\u{1b}[39m",
+ "\u{1b}[37mnd\u{1b}[39m",
+ "\u{1b}[37min\u{1b}[39m",
+ "\u{1b}[37ma \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m38\u{1b}[39m",
+ "\u{1b}[37m24\u{1b}[39m",
+ "\u{1b}[37m90\u{1b}[39m",
+ "\u{1b}[37m99\u{1b}[39m",
+ "\u{1b}[37m99\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mCa\u{1b}[39m",
+ "\u{1b}[37mlc\u{1b}[39m",
+ "\u{1b}[37miu\u{1b}[39m",
+ "\u{1b}[37mm \u{1b}[39m",
+ "\u{1b}[37mca\u{1b}[39m",
+ "\u{1b}[37mrb\u{1b}[39m",
+ "\u{1b}[37mon\u{1b}[39m",
+ "\u{1b}[37mat\u{1b}[39m",
+ "\u{1b}[37me \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mCo\u{1b}[39m",
+ "\u{1b}[37mlo\u{1b}[39m",
+ "\u{1b}[37mmb\u{1b}[39m",
+ "\u{1b}[37mia\u{1b}[39m"
+ ]
+ );
+
+ assert_eq!(
+ split_keeping_words(text, 1)
+ .ansi_split("\n")
+ .collect::<Vec<_>>(),
+ [
+ "\u{1b}[37mT\u{1b}[39m",
+ "\u{1b}[37mi\u{1b}[39m",
+ "\u{1b}[37mg\u{1b}[39m",
+ "\u{1b}[37mr\u{1b}[39m",
+ "\u{1b}[37me\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mE\u{1b}[39m",
+ "\u{1b}[37mc\u{1b}[39m",
+ "\u{1b}[37mu\u{1b}[39m",
+ "\u{1b}[37ma\u{1b}[39m",
+ "\u{1b}[37md\u{1b}[39m",
+ "\u{1b}[37mo\u{1b}[39m",
+ "\u{1b}[37mr\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mO\u{1b}[39m",
+ "\u{1b}[37mM\u{1b}[39m",
+ "\u{1b}[37mY\u{1b}[39m",
+ "\u{1b}[37mA\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mA\u{1b}[39m",
+ "\u{1b}[37mn\u{1b}[39m",
+ "\u{1b}[37md\u{1b}[39m",
+ "\u{1b}[37mi\u{1b}[39m",
+ "\u{1b}[37mn\u{1b}[39m",
+ "\u{1b}[37ma\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m3\u{1b}[39m",
+ "\u{1b}[37m8\u{1b}[39m",
+ "\u{1b}[37m2\u{1b}[39m",
+ "\u{1b}[37m4\u{1b}[39m",
+ "\u{1b}[37m9\u{1b}[39m",
+ "\u{1b}[37m0\u{1b}[39m",
+ "\u{1b}[37m9\u{1b}[39m",
+ "\u{1b}[37m9\u{1b}[39m",
+ "\u{1b}[37m9\u{1b}[39m",
+ "\u{1b}[37m9\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mC\u{1b}[39m",
+ "\u{1b}[37ma\u{1b}[39m",
+ "\u{1b}[37ml\u{1b}[39m",
+ "\u{1b}[37mc\u{1b}[39m",
+ "\u{1b}[37mi\u{1b}[39m",
+ "\u{1b}[37mu\u{1b}[39m",
+ "\u{1b}[37mm\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mc\u{1b}[39m",
+ "\u{1b}[37ma\u{1b}[39m",
+ "\u{1b}[37mr\u{1b}[39m",
+ "\u{1b}[37mb\u{1b}[39m",
+ "\u{1b}[37mo\u{1b}[39m",
+ "\u{1b}[37mn\u{1b}[39m",
+ "\u{1b}[37ma\u{1b}[39m",
+ "\u{1b}[37mt\u{1b}[39m",
+ "\u{1b}[37me\u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37m \u{1b}[39m",
+ "\u{1b}[37mC\u{1b}[39m",
+ "\u{1b}[37mo\u{1b}[39m",
+ "\u{1b}[37ml\u{1b}[39m",
+ "\u{1b}[37mo\u{1b}[39m",
+ "\u{1b}[37mm\u{1b}[39m",
+ "\u{1b}[37mb\u{1b}[39m",
+ "\u{1b}[37mi\u{1b}[39m",
+ "\u{1b}[37ma\u{1b}[39m"
+ ]
+ )
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn split_by_line_keeping_words_color_3_test() {
+ let split = |text, width| split_keeping_words(text, width, "", "");
+ assert_eq!(
+ split(
+ "\u{1b}[37m🚵🏻🚵🏻🚵🏻🚵🏻🚵🏻🚵🏻🚵🏻🚵🏻🚵🏻🚵🏻\u{1b}[0m",
+ 3,
+ ),
+ "\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m\n\u{1b}[37m🚵�\u{1b}[39m",
+ );
+ assert_eq!(
+ split("\u{1b}[37mthis is a long sentence\u{1b}[0m", 7),
+ "\u{1b}[37mthis is\u{1b}[39m\n\u{1b}[37m a long\u{1b}[39m\n\u{1b}[37m senten\u{1b}[39m\n\u{1b}[37mce\u{1b}[39m "
+ );
+ assert_eq!(
+ split("\u{1b}[37mHello World\u{1b}[0m", 7),
+ "\u{1b}[37mHello \u{1b}[39m \n\u{1b}[37mWorld\u{1b}[39m "
+ );
+ assert_eq!(
+ split("\u{1b}[37mHello Wo\u{1b}[37mrld\u{1b}[0m", 7),
+ "\u{1b}[37mHello \u{1b}[39m \n\u{1b}[37mWo\u{1b}[39m\u{1b}[37mrld\u{1b}[39m "
+ );
+ assert_eq!(
+ split("\u{1b}[37mHello Wo\u{1b}[37mrld\u{1b}[0m", 8),
+ "\u{1b}[37mHello \u{1b}[39m \n\u{1b}[37mWo\u{1b}[39m\u{1b}[37mrld\u{1b}[39m "
+ );
+ }
+
+ #[cfg(not(feature = "color"))]
+ #[test]
+ fn split_keeping_words_4_test() {
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "\n");
+
+ assert_eq!(split_keeping_words("12345678", 3,), "123\n456\n78 ");
+ assert_eq!(split_keeping_words("12345678", 2,), "12\n34\n56\n78");
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn split_keeping_words_4_test() {
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "", "");
+
+ #[cfg(not(feature = "color"))]
+ let split_keeping_words = |text, width| split_keeping_words(text, width, "\n");
+
+ assert_eq!(split_keeping_words("12345678", 3,), "123\n456\n78 ");
+ assert_eq!(split_keeping_words("12345678", 2,), "12\n34\n56\n78");
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn chunks_test_with_prefix_and_suffix() {
+ assert_eq!(chunks("123456", 0, "^", "$"), ["^$"; 0]);
+
+ assert_eq!(
+ chunks("123456", 1, "^", "$"),
+ ["^1$", "^2$", "^3$", "^4$", "^5$", "^6$"]
+ );
+ assert_eq!(chunks("123456", 2, "^", "$"), ["^12$", "^34$", "^56$"]);
+ assert_eq!(chunks("12345", 2, "^", "$"), ["^12$", "^34$", "^5$"]);
+
+ assert_eq!(
+ chunks("😳😳😳😳😳", 1, "^", "$"),
+ ["^�$", "^�$", "^�$", "^�$", "^�$"]
+ );
+ assert_eq!(
+ chunks("😳😳😳😳😳", 2, "^", "$"),
+ ["^😳$", "^😳$", "^😳$", "^😳$", "^😳$"]
+ );
+ assert_eq!(
+ chunks("😳😳😳😳😳", 3, "^", "$"),
+ ["^😳�$", "^😳�$", "^😳$"]
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn split_by_line_keeping_words_test_with_prefix_and_suffix() {
+ assert_eq!(
+ split_keeping_words("123456", 1, "^", "$"),
+ "^1$\n^2$\n^3$\n^4$\n^5$\n^6$"
+ );
+ assert_eq!(
+ split_keeping_words("123456", 2, "^", "$"),
+ "^12$\n^34$\n^56$"
+ );
+ assert_eq!(
+ split_keeping_words("12345", 2, "^", "$"),
+ "^12$\n^34$\n^5$ "
+ );
+
+ assert_eq!(
+ split_keeping_words("😳😳😳😳😳", 1, "^", "$"),
+ "^�$\n^�$\n^�$\n^�$\n^�$"
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn split_by_line_keeping_words_color_2_test_with_prefix_and_suffix() {
+ use ansi_str::AnsiStr;
+
+ let text = "\u{1b}[37mTigre Ecuador OMYA Andina 3824909999 Calcium carbonate Colombia\u{1b}[0m";
+
+ assert_eq!(
+ split_keeping_words(text, 2, "^", "$")
+ .ansi_split("\n")
+ .collect::<Vec<_>>(),
+ [
+ "^\u{1b}[37mTi\u{1b}[39m$",
+ "^\u{1b}[37mgr\u{1b}[39m$",
+ "^\u{1b}[37me \u{1b}[39m$",
+ "^\u{1b}[37mEc\u{1b}[39m$",
+ "^\u{1b}[37mua\u{1b}[39m$",
+ "^\u{1b}[37mdo\u{1b}[39m$",
+ "^\u{1b}[37mr \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mOM\u{1b}[39m$",
+ "^\u{1b}[37mYA\u{1b}[39m$",
+ "^\u{1b}[37m A\u{1b}[39m$",
+ "^\u{1b}[37mnd\u{1b}[39m$",
+ "^\u{1b}[37min\u{1b}[39m$",
+ "^\u{1b}[37ma \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m38\u{1b}[39m$",
+ "^\u{1b}[37m24\u{1b}[39m$",
+ "^\u{1b}[37m90\u{1b}[39m$",
+ "^\u{1b}[37m99\u{1b}[39m$",
+ "^\u{1b}[37m99\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mCa\u{1b}[39m$",
+ "^\u{1b}[37mlc\u{1b}[39m$",
+ "^\u{1b}[37miu\u{1b}[39m$",
+ "^\u{1b}[37mm \u{1b}[39m$",
+ "^\u{1b}[37mca\u{1b}[39m$",
+ "^\u{1b}[37mrb\u{1b}[39m$",
+ "^\u{1b}[37mon\u{1b}[39m$",
+ "^\u{1b}[37mat\u{1b}[39m$",
+ "^\u{1b}[37me \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mCo\u{1b}[39m$",
+ "^\u{1b}[37mlo\u{1b}[39m$",
+ "^\u{1b}[37mmb\u{1b}[39m$",
+ "^\u{1b}[37mia\u{1b}[39m$"
+ ]
+ );
+
+ assert_eq!(
+ split_keeping_words(text, 1, "^", "$")
+ .ansi_split("\n")
+ .collect::<Vec<_>>(),
+ [
+ "^\u{1b}[37mT\u{1b}[39m$",
+ "^\u{1b}[37mi\u{1b}[39m$",
+ "^\u{1b}[37mg\u{1b}[39m$",
+ "^\u{1b}[37mr\u{1b}[39m$",
+ "^\u{1b}[37me\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mE\u{1b}[39m$",
+ "^\u{1b}[37mc\u{1b}[39m$",
+ "^\u{1b}[37mu\u{1b}[39m$",
+ "^\u{1b}[37ma\u{1b}[39m$",
+ "^\u{1b}[37md\u{1b}[39m$",
+ "^\u{1b}[37mo\u{1b}[39m$",
+ "^\u{1b}[37mr\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mO\u{1b}[39m$",
+ "^\u{1b}[37mM\u{1b}[39m$",
+ "^\u{1b}[37mY\u{1b}[39m$",
+ "^\u{1b}[37mA\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mA\u{1b}[39m$",
+ "^\u{1b}[37mn\u{1b}[39m$",
+ "^\u{1b}[37md\u{1b}[39m$",
+ "^\u{1b}[37mi\u{1b}[39m$",
+ "^\u{1b}[37mn\u{1b}[39m$",
+ "^\u{1b}[37ma\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m3\u{1b}[39m$",
+ "^\u{1b}[37m8\u{1b}[39m$",
+ "^\u{1b}[37m2\u{1b}[39m$",
+ "^\u{1b}[37m4\u{1b}[39m$",
+ "^\u{1b}[37m9\u{1b}[39m$",
+ "^\u{1b}[37m0\u{1b}[39m$",
+ "^\u{1b}[37m9\u{1b}[39m$",
+ "^\u{1b}[37m9\u{1b}[39m$",
+ "^\u{1b}[37m9\u{1b}[39m$",
+ "^\u{1b}[37m9\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mC\u{1b}[39m$",
+ "^\u{1b}[37ma\u{1b}[39m$",
+ "^\u{1b}[37ml\u{1b}[39m$",
+ "^\u{1b}[37mc\u{1b}[39m$",
+ "^\u{1b}[37mi\u{1b}[39m$",
+ "^\u{1b}[37mu\u{1b}[39m$",
+ "^\u{1b}[37mm\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mc\u{1b}[39m$",
+ "^\u{1b}[37ma\u{1b}[39m$",
+ "^\u{1b}[37mr\u{1b}[39m$",
+ "^\u{1b}[37mb\u{1b}[39m$",
+ "^\u{1b}[37mo\u{1b}[39m$",
+ "^\u{1b}[37mn\u{1b}[39m$",
+ "^\u{1b}[37ma\u{1b}[39m$",
+ "^\u{1b}[37mt\u{1b}[39m$",
+ "^\u{1b}[37me\u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37m \u{1b}[39m$",
+ "^\u{1b}[37mC\u{1b}[39m$",
+ "^\u{1b}[37mo\u{1b}[39m$",
+ "^\u{1b}[37ml\u{1b}[39m$",
+ "^\u{1b}[37mo\u{1b}[39m$",
+ "^\u{1b}[37mm\u{1b}[39m$",
+ "^\u{1b}[37mb\u{1b}[39m$",
+ "^\u{1b}[37mi\u{1b}[39m$",
+ "^\u{1b}[37ma\u{1b}[39m$"
+ ]
+ )
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn chunks_wrap_2() {
+ let text = "\u{1b}[30mDebian\u{1b}[0m\u{1b}[31mDebian\u{1b}[0m\u{1b}[32mDebian\u{1b}[0m\u{1b}[33mDebian\u{1b}[0m\u{1b}[34mDebian\u{1b}[0m\u{1b}[35mDebian\u{1b}[0m\u{1b}[36mDebian\u{1b}[0m\u{1b}[37mDebian\u{1b}[0m\u{1b}[40mDebian\u{1b}[0m\u{1b}[41mDebian\u{1b}[0m\u{1b}[42mDebian\u{1b}[0m\u{1b}[43mDebian\u{1b}[0m\u{1b}[44mDebian\u{1b}[0m";
+ assert_eq!(
+ chunks(text, 30, "", ""),
+ [
+ "\u{1b}[30mDebian\u{1b}[39m\u{1b}[31mDebian\u{1b}[39m\u{1b}[32mDebian\u{1b}[39m\u{1b}[33mDebian\u{1b}[39m\u{1b}[34mDebian\u{1b}[39m",
+ "\u{1b}[35mDebian\u{1b}[39m\u{1b}[36mDebian\u{1b}[39m\u{1b}[37mDebian\u{1b}[39m\u{1b}[40mDebian\u{1b}[49m\u{1b}[41mDebian\u{1b}[49m",
+ "\u{1b}[42mDebian\u{1b}[49m\u{1b}[43mDebian\u{1b}[49m\u{1b}[44mDebian\u{1b}[49m",
+ ]
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn chunks_wrap_3() {
+ let text = "\u{1b}[37mCreate bytes from the \u{1b}[0m\u{1b}[7;34marg\u{1b}[0m\u{1b}[37muments.\u{1b}[0m";
+
+ assert_eq!(
+ chunks(text, 22, "", ""),
+ [
+ "\u{1b}[37mCreate bytes from the \u{1b}[39m",
+ "\u{1b}[7m\u{1b}[34marg\u{1b}[27m\u{1b}[39m\u{1b}[37muments.\u{1b}[39m"
+ ]
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn chunks_wrap_3_keeping_words() {
+ let text = "\u{1b}[37mCreate bytes from the \u{1b}[0m\u{1b}[7;34marg\u{1b}[0m\u{1b}[37muments.\u{1b}[0m";
+
+ assert_eq!(
+ split_keeping_words(text, 22, "", ""),
+ "\u{1b}[37mCreate bytes from the \u{1b}[39m\n\u{1b}[7m\u{1b}[34marg\u{1b}[27m\u{1b}[39m\u{1b}[37muments.\u{1b}[39m "
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn chunks_wrap_4() {
+ let text = "\u{1b}[37mReturns the floor of a number (l\u{1b}[0m\u{1b}[41;37marg\u{1b}[0m\u{1b}[37mest integer less than or equal to that number).\u{1b}[0m";
+
+ assert_eq!(
+ chunks(text, 10, "", ""),
+ [
+ "\u{1b}[37mReturns th\u{1b}[39m",
+ "\u{1b}[37me floor of\u{1b}[39m",
+ "\u{1b}[37m a number \u{1b}[39m",
+ "\u{1b}[37m(l\u{1b}[39m\u{1b}[37m\u{1b}[41marg\u{1b}[39m\u{1b}[49m\u{1b}[37mest i\u{1b}[39m",
+ "\u{1b}[37mnteger les\u{1b}[39m",
+ "\u{1b}[37ms than or \u{1b}[39m",
+ "\u{1b}[37mequal to t\u{1b}[39m",
+ "\u{1b}[37mhat number\u{1b}[39m",
+ "\u{1b}[37m).\u{1b}[39m",
+ ]
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn chunks_wrap_4_keeping_words() {
+ let text = "\u{1b}[37mReturns the floor of a number (l\u{1b}[0m\u{1b}[41;37marg\u{1b}[0m\u{1b}[37mest integer less than or equal to that number).\u{1b}[0m";
+ assert_eq!(
+ split_keeping_words(text, 10, "", ""),
+ concat!(
+ "\u{1b}[37mReturns \u{1b}[39m \n",
+ "\u{1b}[37mthe floor \u{1b}[39m\n",
+ "\u{1b}[37mof a \u{1b}[39m \n",
+ "\u{1b}[37mnumber \u{1b}[39m \n",
+ "\u{1b}[37m(l\u{1b}[39m\u{1b}[37m\u{1b}[41marg\u{1b}[39m\u{1b}[49m\u{1b}[37mest \u{1b}[39m \n",
+ "\u{1b}[37minteger \u{1b}[39m \n",
+ "\u{1b}[37mless than \u{1b}[39m\n",
+ "\u{1b}[37mor equal \u{1b}[39m \n",
+ "\u{1b}[37mto that \u{1b}[39m \n",
+ "\u{1b}[37mnumber).\u{1b}[39m ",
+ )
+ );
+ }
+}
+
+// \u{1b}[37mReturns \u{1b}[39m\n
+// \u{1b}[37mthe floor \u{1b}[39m\n
+// \u{1b}[37mof a \u{1b}[39m\n
+// \u{1b}[37mnumber \u{1b}[39m\u{1b}[49m\n
+// \u{1b}[37m\u{1b}[41m(l\u{1b}[39m\u{1b}[37m\u{1b}[41marg\u{1b}[39m\u{1b}[49m\u{1b}[37mest \u{1b}[39m\n
+// \u{1b}[37minteger \u{1b}[39m\n
+// \u{1b}[37mless than \u{1b}[39m\n
+// \u{1b}[37mor equal \u{1b}[39m\n
+// \u{1b}[37mto that \u{1b}[39m\n
+// \u{1b}[37mnumber).\u{1b}[39m "
+
+//
+//
+
+// \u{1b}[37mReturns \u{1b}[39m\n
+// \u{1b}[37mthe floor \u{1b}[39m\n
+// \u{1b}[37mof a \u{1b}[39m\n
+// \u{1b}[37mnumber \u{1b}[39m\u{1b}[49m\n
+// \u{1b}[37m\u{1b}[41m(l\u{1b}[39m\u{1b}[37m\u{1b}[41marg\u{1b}[39m\u{1b}[49m\u{1b}[37mest \u{1b}[39m\n
+// \u{1b}[37minteger \u{1b}[39m\n
+// \u{1b}[37mless than \u{1b}[39m\n
+// \u{1b}[37mor equal \u{1b}[39m\n
+// \u{1b}[37mto that \u{1b}[39m\n
+// \u{1b}[37mnumber).\u{1b}[39m "
+
+// "\u{1b}[37mReturns\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37mthe\u{1b}[37m floor\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37mof\u{1b}[37m a\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37mnumber\u{1b}[37m \u{1b}[39m\u{1b}[49m\n
+// \u{1b}[37m\u{1b}[41m(l\u{1b}[39m\u{1b}[37m\u{1b}[41marg\u{1b}[39m\u{1b}[49m\u{1b}[37mest\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37minteger\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37mless\u{1b}[37m than\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37mor\u{1b}[37m equal\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37mto\u{1b}[37m that\u{1b}[37m \u{1b}[39m\n
+// \u{1b}[37mnumber).\u{1b}[39m "
diff --git a/vendor/tabled/src/tabled.rs b/vendor/tabled/src/tabled.rs
new file mode 100644
index 000000000..636173dc0
--- /dev/null
+++ b/vendor/tabled/src/tabled.rs
@@ -0,0 +1,150 @@
+use std::borrow::Cow;
+
+/// Tabled a trait responsible for providing a header fields and a row fields.
+///
+/// It's urgent that `header` len is equal to `fields` len.
+///
+/// ```text
+/// Self::headers().len() == self.fields().len()
+/// ```
+pub trait Tabled {
+ /// A length of fields and headers,
+ /// which must be the same.
+ const LENGTH: usize;
+
+ /// Fields method must return a list of cells.
+ ///
+ /// The cells will be placed in the same row, preserving the order.
+ fn fields(&self) -> Vec<Cow<'_, str>>;
+ /// Headers must return a list of column names.
+ fn headers() -> Vec<Cow<'static, str>>;
+}
+
+impl<T> Tabled for &T
+where
+ T: Tabled,
+{
+ const LENGTH: usize = T::LENGTH;
+
+ fn fields(&self) -> Vec<Cow<'_, str>> {
+ T::fields(self)
+ }
+ fn headers() -> Vec<Cow<'static, str>> {
+ T::headers()
+ }
+}
+
+impl<T> Tabled for Box<T>
+where
+ T: Tabled,
+{
+ const LENGTH: usize = T::LENGTH;
+
+ fn fields(&self) -> Vec<Cow<'_, str>> {
+ T::fields(self)
+ }
+ fn headers() -> Vec<Cow<'static, str>> {
+ T::headers()
+ }
+}
+
+macro_rules! tuple_table {
+ ( $($name:ident)+ ) => {
+ impl<$($name: Tabled),+> Tabled for ($($name,)+){
+ const LENGTH: usize = $($name::LENGTH+)+ 0;
+
+ fn fields(&self) -> Vec<Cow<'_, str>> {
+ #![allow(non_snake_case)]
+ let ($($name,)+) = self;
+ let mut fields = Vec::with_capacity(Self::LENGTH);
+ $(fields.append(&mut $name.fields());)+
+ fields
+ }
+
+ fn headers() -> Vec<Cow<'static, str>> {
+ let mut fields = Vec::with_capacity(Self::LENGTH);
+ $(fields.append(&mut $name::headers());)+
+ fields
+ }
+ }
+ };
+}
+
+tuple_table! { A }
+tuple_table! { A B }
+tuple_table! { A B C }
+tuple_table! { A B C D }
+tuple_table! { A B C D E }
+tuple_table! { A B C D E F }
+
+macro_rules! default_table {
+ ( $t:ty ) => {
+ impl Tabled for $t {
+ const LENGTH: usize = 1;
+
+ fn fields(&self) -> Vec<Cow<'_, str>> {
+ vec![Cow::Owned(self.to_string())]
+ }
+ fn headers() -> Vec<Cow<'static, str>> {
+ vec![Cow::Borrowed(stringify!($t))]
+ }
+ }
+ };
+
+ ( $t:ty = borrowed ) => {
+ impl Tabled for $t {
+ const LENGTH: usize = 1;
+
+ fn fields(&self) -> Vec<Cow<'_, str>> {
+ vec![Cow::Borrowed(self)]
+ }
+ fn headers() -> Vec<Cow<'static, str>> {
+ vec![Cow::Borrowed(stringify!($t))]
+ }
+ }
+ };
+}
+
+default_table!(&str = borrowed);
+default_table!(str = borrowed);
+default_table!(String);
+
+default_table!(char);
+
+default_table!(bool);
+
+default_table!(isize);
+default_table!(usize);
+
+default_table!(u8);
+default_table!(u16);
+default_table!(u32);
+default_table!(u64);
+default_table!(u128);
+
+default_table!(i8);
+default_table!(i16);
+default_table!(i32);
+default_table!(i64);
+default_table!(i128);
+
+default_table!(f32);
+default_table!(f64);
+
+impl<T, const N: usize> Tabled for [T; N]
+where
+ T: std::fmt::Display,
+{
+ const LENGTH: usize = N;
+
+ fn fields(&self) -> Vec<Cow<'_, str>> {
+ self.iter()
+ .map(ToString::to_string)
+ .map(Cow::Owned)
+ .collect()
+ }
+
+ fn headers() -> Vec<Cow<'static, str>> {
+ (0..N).map(|i| Cow::Owned(format!("{i}"))).collect()
+ }
+}
diff --git a/vendor/tabled/src/tables/compact.rs b/vendor/tabled/src/tables/compact.rs
new file mode 100644
index 000000000..14c1e2b5b
--- /dev/null
+++ b/vendor/tabled/src/tables/compact.rs
@@ -0,0 +1,309 @@
+//! This module contains a [`CompactTable`] table.
+//!
+//! In contrast to [`Table`] [`CompactTable`] does no allocations but it consumes an iterator.
+//! It's useful when you don't want to re/allocate a buffer for your data.
+//!
+//! # Example
+//!
+//! It works smoothly with arrays.
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//!use tabled::{settings::Style, tables::CompactTable};
+//!
+//! let data = [
+//! ["FreeBSD", "1993", "William and Lynne Jolitz", "?"],
+//! ["OpenBSD", "1995", "Theo de Raadt", ""],
+//! ["HardenedBSD", "2014", "Oliver Pinter and Shawn Webb", ""],
+//! ];
+//!
+//! let table = CompactTable::from(data)
+//! .with(Style::psql())
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! concat!(
+//! " FreeBSD | 1993 | William and Lynne Jolitz | ? \n",
+//! "-------------+------+------------------------------+---\n",
+//! " OpenBSD | 1995 | Theo de Raadt | \n",
+//! " HardenedBSD | 2014 | Oliver Pinter and Shawn Webb | ",
+//! )
+//! );
+//! ```
+//!
+//! But it's default creation requires to be given an estimated cell width, and the amount of columns.
+//!
+#![cfg_attr(feature = "std", doc = "```")]
+#![cfg_attr(not(feature = "std"), doc = "```ignore")]
+//!use tabled::{settings::Style, tables::CompactTable};
+//!
+//! let data = [
+//! ["FreeBSD", "1993", "William and Lynne Jolitz", "?"],
+//! ["OpenBSD", "1995", "Theo de Raadt", ""],
+//! ["HardenedBSD", "2014", "Oliver Pinter and Shawn Webb", ""],
+//! ];
+//!
+//! // See what will happen if the given width is too narrow
+//!
+//! let table = CompactTable::new(&data)
+//! .columns(4)
+//! .width(5)
+//! .with(Style::ascii())
+//! .to_string();
+//!
+//! assert_eq!(
+//! table,
+//! "+-----+-----+-----+-----+\n\
+//! | FreeBSD | 1993 | William and Lynne Jolitz | ? |\n\
+//! |-----+-----+-----+-----|\n\
+//! | OpenBSD | 1995 | Theo de Raadt | |\n\
+//! |-----+-----+-----+-----|\n\
+//! | HardenedBSD | 2014 | Oliver Pinter and Shawn Webb | |\n\
+//! +-----+-----+-----+-----+"
+//! );
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use core::cmp::max;
+use core::fmt;
+
+use crate::{
+ grid::{
+ config::{AlignmentHorizontal, CompactConfig, Indent, Sides},
+ dimension::{ConstDimension, ConstSize, Dimension},
+ records::{
+ into_records::{LimitColumns, LimitRows},
+ IntoRecords, IterRecords,
+ },
+ util::string::string_width,
+ CompactGrid,
+ },
+ settings::{Style, TableOption},
+};
+
+/// A table which consumes an [`IntoRecords`] iterator.
+/// It assumes that the content has only single line.
+#[derive(Debug, Clone)]
+pub struct CompactTable<I, D> {
+ records: I,
+ cfg: CompactConfig,
+ dims: D,
+ count_columns: usize,
+ count_rows: Option<usize>,
+}
+
+impl<I> CompactTable<I, ConstDimension<0, 0>> {
+ /// Creates a new [`CompactTable`] structure with a width dimension for all columns.
+ pub const fn new(iter: I) -> Self
+ where
+ I: IntoRecords,
+ {
+ Self {
+ records: iter,
+ cfg: create_config(),
+ count_columns: 0,
+ count_rows: None,
+ dims: ConstDimension::new(ConstSize::Value(2), ConstSize::Value(1)),
+ }
+ }
+}
+
+impl<I, const ROWS: usize, const COLS: usize> CompactTable<I, ConstDimension<COLS, ROWS>> {
+ /// Set a height for each row.
+ pub fn height<S: Into<ConstSize<COUNT_ROWS>>, const COUNT_ROWS: usize>(
+ self,
+ size: S,
+ ) -> CompactTable<I, ConstDimension<COLS, COUNT_ROWS>> {
+ let (width, _) = self.dims.into();
+ CompactTable {
+ dims: ConstDimension::new(width, size.into()),
+ records: self.records,
+ cfg: self.cfg,
+ count_columns: self.count_columns,
+ count_rows: self.count_rows,
+ }
+ }
+
+ /// Set a width for each column.
+ pub fn width<S: Into<ConstSize<COUNT_COLUMNS>>, const COUNT_COLUMNS: usize>(
+ self,
+ size: S,
+ ) -> CompactTable<I, ConstDimension<COUNT_COLUMNS, ROWS>> {
+ let (_, height) = self.dims.into();
+ CompactTable {
+ dims: ConstDimension::new(size.into(), height),
+ records: self.records,
+ cfg: self.cfg,
+ count_columns: self.count_columns,
+ count_rows: self.count_rows,
+ }
+ }
+}
+
+impl<I, D> CompactTable<I, D> {
+ /// Creates a new [`CompactTable`] structure with a known dimension.
+ ///
+ /// Notice that the function wont call [`Estimate`].
+ ///
+ /// [`Estimate`]: crate::grid::dimension::Estimate
+ pub fn with_dimension(iter: I, dimension: D) -> Self
+ where
+ I: IntoRecords,
+ {
+ Self {
+ records: iter,
+ dims: dimension,
+ cfg: create_config(),
+ count_columns: 0,
+ count_rows: None,
+ }
+ }
+
+ /// With is a generic function which applies options to the [`CompactTable`].
+ pub fn with<O>(mut self, option: O) -> Self
+ where
+ for<'a> O: TableOption<IterRecords<&'a I>, D, CompactConfig>,
+ {
+ let mut records = IterRecords::new(&self.records, self.count_columns, self.count_rows);
+ option.change(&mut records, &mut self.cfg, &mut self.dims);
+
+ self
+ }
+
+ /// Limit a number of rows.
+ pub const fn rows(mut self, count_rows: usize) -> Self {
+ self.count_rows = Some(count_rows);
+ self
+ }
+
+ /// Limit a number of columns.
+ pub const fn columns(mut self, count: usize) -> Self {
+ self.count_columns = count;
+ self
+ }
+
+ /// Returns a table config.
+ pub fn get_config(&self) -> &CompactConfig {
+ &self.cfg
+ }
+
+ /// Returns a table config.
+ pub fn get_config_mut(&mut self) -> &mut CompactConfig {
+ &mut self.cfg
+ }
+
+ /// Format table into [fmt::Write]er.
+ pub fn fmt<W>(self, writer: W) -> fmt::Result
+ where
+ I: IntoRecords,
+ D: Dimension,
+ W: fmt::Write,
+ {
+ build_grid(
+ writer,
+ self.records,
+ self.dims,
+ self.cfg,
+ self.count_columns,
+ self.count_rows,
+ )
+ }
+
+ /// Format table into a writer.
+ #[cfg(feature = "std")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+ pub fn build<W>(self, writer: W) -> std::io::Result<()>
+ where
+ I: IntoRecords,
+ D: Dimension,
+ W: std::io::Write,
+ {
+ let writer = super::util::utf8_writer::UTF8Writer::new(writer);
+ self.fmt(writer)
+ .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))
+ }
+
+ /// Build a string.
+ ///
+ /// We can't implement [`std::string::ToString`] cause it does takes `&self` reference.
+ #[allow(clippy::inherent_to_string)]
+ #[cfg(feature = "std")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+ pub fn to_string(self) -> String
+ where
+ I: IntoRecords,
+ D: Dimension,
+ {
+ let mut buf = String::new();
+ self.fmt(&mut buf).unwrap();
+ buf
+ }
+}
+
+impl<T, const ROWS: usize, const COLS: usize> From<[[T; COLS]; ROWS]>
+ for CompactTable<[[T; COLS]; ROWS], ConstDimension<COLS, ROWS>>
+where
+ T: AsRef<str>,
+{
+ fn from(mat: [[T; COLS]; ROWS]) -> Self {
+ let mut width = [0; COLS];
+ for row in mat.iter() {
+ for (col, text) in row.iter().enumerate() {
+ let text = text.as_ref();
+ let text_width = string_width(text);
+ width[col] = max(width[col], text_width);
+ }
+ }
+
+ // add padding
+ for w in &mut width {
+ *w += 2;
+ }
+
+ let dims = ConstDimension::new(ConstSize::List(width), ConstSize::Value(1));
+ Self::with_dimension(mat, dims).columns(COLS).rows(ROWS)
+ }
+}
+
+fn build_grid<W: fmt::Write, I: IntoRecords, D: Dimension>(
+ writer: W,
+ records: I,
+ dims: D,
+ config: CompactConfig,
+ cols: usize,
+ rows: Option<usize>,
+) -> Result<(), fmt::Error> {
+ match rows {
+ Some(limit) => {
+ let records = LimitRows::new(records, limit);
+ let records = LimitColumns::new(records, cols);
+ let records = IterRecords::new(records, cols, rows);
+ CompactGrid::new(records, dims, config).build(writer)
+ }
+ None => {
+ let records = LimitColumns::new(records, cols);
+ let records = IterRecords::new(records, cols, rows);
+ CompactGrid::new(records, dims, config).build(writer)
+ }
+ }
+}
+
+const fn create_config() -> CompactConfig {
+ CompactConfig::empty()
+ .set_padding(Sides::new(
+ Indent::spaced(1),
+ Indent::spaced(1),
+ Indent::zero(),
+ Indent::zero(),
+ ))
+ .set_alignment_horizontal(AlignmentHorizontal::Left)
+ .set_borders(*Style::ascii().get_borders())
+}
+
+impl<R, D> TableOption<R, D, CompactConfig> for CompactConfig {
+ fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
+ *cfg = self;
+ }
+}
diff --git a/vendor/tabled/src/tables/extended.rs b/vendor/tabled/src/tables/extended.rs
new file mode 100644
index 000000000..478505c4b
--- /dev/null
+++ b/vendor/tabled/src/tables/extended.rs
@@ -0,0 +1,338 @@
+//! This module contains an [`ExtendedTable`] structure which is useful in cases where
+//! a structure has a lot of fields.
+//!
+#![cfg_attr(feature = "derive", doc = "```")]
+#![cfg_attr(not(feature = "derive"), doc = "```ignore")]
+//! use tabled::{Tabled, tables::ExtendedTable};
+//!
+//! #[derive(Tabled)]
+//! struct Language {
+//! name: &'static str,
+//! designed_by: &'static str,
+//! invented_year: usize,
+//! }
+//!
+//! let languages = vec![
+//! Language{
+//! name: "C",
+//! designed_by: "Dennis Ritchie",
+//! invented_year: 1972
+//! },
+//! Language{
+//! name: "Rust",
+//! designed_by: "Graydon Hoare",
+//! invented_year: 2010
+//! },
+//! Language{
+//! name: "Go",
+//! designed_by: "Rob Pike",
+//! invented_year: 2009
+//! },
+//! ];
+//!
+//! let table = ExtendedTable::new(languages).to_string();
+//!
+//! let expected = "-[ RECORD 0 ]-+---------------\n\
+//! name | C\n\
+//! designed_by | Dennis Ritchie\n\
+//! invented_year | 1972\n\
+//! -[ RECORD 1 ]-+---------------\n\
+//! name | Rust\n\
+//! designed_by | Graydon Hoare\n\
+//! invented_year | 2010\n\
+//! -[ RECORD 2 ]-+---------------\n\
+//! name | Go\n\
+//! designed_by | Rob Pike\n\
+//! invented_year | 2009";
+//!
+//! assert_eq!(table, expected);
+//! ```
+
+use std::borrow::Cow;
+use std::fmt::{self, Display};
+
+use crate::grid::util::string::string_width;
+use crate::Tabled;
+
+/// `ExtendedTable` display data in a 'expanded display mode' from postgresql.
+/// It may be useful for a large data sets with a lot of fields.
+///
+/// See 'Examples' in <https://www.postgresql.org/docs/current/app-psql.html>.
+///
+/// It escapes strings to resolve a multi-line ones.
+/// Because of that ANSI sequences will be not be rendered too so colores will not be showed.
+///
+/// ```
+/// use tabled::tables::ExtendedTable;
+///
+/// let data = vec!["Hello", "2021"];
+/// let table = ExtendedTable::new(&data).to_string();
+///
+/// assert_eq!(
+/// table,
+/// concat!(
+/// "-[ RECORD 0 ]-\n",
+/// "&str | Hello\n",
+/// "-[ RECORD 1 ]-\n",
+/// "&str | 2021",
+/// )
+/// );
+/// ```
+#[derive(Debug, Clone)]
+pub struct ExtendedTable {
+ fields: Vec<String>,
+ records: Vec<Vec<String>>,
+}
+
+impl ExtendedTable {
+ /// Creates a new instance of `ExtendedTable`
+ pub fn new<T>(iter: impl IntoIterator<Item = T>) -> Self
+ where
+ T: Tabled,
+ {
+ let data = iter
+ .into_iter()
+ .map(|i| {
+ i.fields()
+ .into_iter()
+ .map(|s| s.escape_debug().to_string())
+ .collect()
+ })
+ .collect();
+ let header = T::headers()
+ .into_iter()
+ .map(|s| s.escape_debug().to_string())
+ .collect();
+
+ Self {
+ records: data,
+ fields: header,
+ }
+ }
+
+ /// Truncates table to a set width value for a table.
+ /// It returns a success inticator, where `false` means it's not possible to set the table width,
+ /// because of the given arguments.
+ ///
+ /// It tries to not affect fields, but if there's no enough space all records will be deleted and fields will be cut.
+ ///
+ /// The minimum width is 14.
+ pub fn truncate(&mut self, max: usize, suffix: &str) -> bool {
+ // -[ RECORD 0 ]-
+ let teplate_width = self.records.len().to_string().len() + 13;
+ let min_width = teplate_width;
+ if max < min_width {
+ return false;
+ }
+
+ let suffix_width = string_width(suffix);
+ if max < suffix_width {
+ return false;
+ }
+
+ let max = max - suffix_width;
+
+ let fields_max_width = self
+ .fields
+ .iter()
+ .map(|s| string_width(s))
+ .max()
+ .unwrap_or_default();
+
+ // 3 is a space for ' | '
+ let fields_affected = max < fields_max_width + 3;
+ if fields_affected {
+ if max < 3 {
+ return false;
+ }
+
+ let max = max - 3;
+
+ if max < suffix_width {
+ return false;
+ }
+
+ let max = max - suffix_width;
+
+ truncate_fields(&mut self.fields, max, suffix);
+ truncate_records(&mut self.records, 0, suffix);
+ } else {
+ let max = max - fields_max_width - 3 - suffix_width;
+ truncate_records(&mut self.records, max, suffix);
+ }
+
+ true
+ }
+}
+
+impl From<Vec<Vec<String>>> for ExtendedTable {
+ fn from(mut data: Vec<Vec<String>>) -> Self {
+ if data.is_empty() {
+ return Self {
+ fields: vec![],
+ records: vec![],
+ };
+ }
+
+ let fields = data.remove(0);
+
+ Self {
+ fields,
+ records: data,
+ }
+ }
+}
+
+impl Display for ExtendedTable {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.records.is_empty() {
+ return Ok(());
+ }
+
+ // It's possible that field|header can be a multiline string so
+ // we escape it and trim \" chars.
+ let fields = self.fields.iter().collect::<Vec<_>>();
+
+ let max_field_width = fields
+ .iter()
+ .map(|s| string_width(s))
+ .max()
+ .unwrap_or_default();
+
+ let max_values_length = self
+ .records
+ .iter()
+ .map(|record| record.iter().map(|s| string_width(s)).max())
+ .max()
+ .unwrap_or_default()
+ .unwrap_or_default();
+
+ for (i, records) in self.records.iter().enumerate() {
+ write_header_template(f, i, max_field_width, max_values_length)?;
+
+ for (value, field) in records.iter().zip(fields.iter()) {
+ writeln!(f)?;
+ write_record(f, field, value, max_field_width)?;
+ }
+
+ let is_last_record = i + 1 == self.records.len();
+ if !is_last_record {
+ writeln!(f)?;
+ }
+ }
+
+ Ok(())
+ }
+}
+
+fn truncate_records(records: &mut Vec<Vec<String>>, max_width: usize, suffix: &str) {
+ for fields in records {
+ truncate_fields(fields, max_width, suffix);
+ }
+}
+
+fn truncate_fields(records: &mut Vec<String>, max_width: usize, suffix: &str) {
+ for text in records {
+ truncate(text, max_width, suffix);
+ }
+}
+
+fn write_header_template(
+ f: &mut fmt::Formatter<'_>,
+ index: usize,
+ max_field_width: usize,
+ max_values_length: usize,
+) -> fmt::Result {
+ let mut template = format!("-[ RECORD {index} ]-");
+ let default_template_length = template.len();
+
+ // 3 - is responsible for ' | ' formatting
+ let max_line_width = std::cmp::max(
+ max_field_width + 3 + max_values_length,
+ default_template_length,
+ );
+ let rest_to_print = max_line_width - default_template_length;
+ if rest_to_print > 0 {
+ // + 1 is a space after field name and we get a next pos so its +2
+ if max_field_width + 2 > default_template_length {
+ let part1 = (max_field_width + 1) - default_template_length;
+ let part2 = rest_to_print - part1 - 1;
+
+ template.extend(
+ std::iter::repeat('-')
+ .take(part1)
+ .chain(std::iter::once('+'))
+ .chain(std::iter::repeat('-').take(part2)),
+ );
+ } else {
+ template.extend(std::iter::repeat('-').take(rest_to_print));
+ }
+ }
+
+ write!(f, "{template}")?;
+
+ Ok(())
+}
+
+fn write_record(
+ f: &mut fmt::Formatter<'_>,
+ field: &str,
+ value: &str,
+ max_field_width: usize,
+) -> fmt::Result {
+ write!(f, "{field:max_field_width$} | {value}")
+}
+
+fn truncate(text: &mut String, max: usize, suffix: &str) {
+ let original_len = text.len();
+
+ if max == 0 || text.is_empty() {
+ *text = String::new();
+ } else {
+ *text = cut_str_basic(text, max).into_owned();
+ }
+
+ let cut_was_done = text.len() < original_len;
+ if !suffix.is_empty() && cut_was_done {
+ text.push_str(suffix);
+ }
+}
+
+fn cut_str_basic(s: &str, width: usize) -> Cow<'_, str> {
+ const REPLACEMENT: char = '\u{FFFD}';
+
+ let (length, count_unknowns, _) = split_at_pos(s, width);
+ let buf = &s[..length];
+ if count_unknowns == 0 {
+ return Cow::Borrowed(buf);
+ }
+
+ let mut buf = buf.to_owned();
+ buf.extend(std::iter::repeat(REPLACEMENT).take(count_unknowns));
+
+ Cow::Owned(buf)
+}
+
+fn split_at_pos(s: &str, pos: usize) -> (usize, usize, usize) {
+ let mut length = 0;
+ let mut i = 0;
+ for c in s.chars() {
+ if i == pos {
+ break;
+ };
+
+ let c_width = unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
+
+ // We cut the chars which takes more then 1 symbol to display,
+ // in order to archive the necessary width.
+ if i + c_width > pos {
+ let count = pos - i;
+ return (length, count, c.len_utf8());
+ }
+
+ i += c_width;
+ length += c.len_utf8();
+ }
+
+ (length, 0, 0)
+}
diff --git a/vendor/tabled/src/tables/iter.rs b/vendor/tabled/src/tables/iter.rs
new file mode 100644
index 000000000..af485ac08
--- /dev/null
+++ b/vendor/tabled/src/tables/iter.rs
@@ -0,0 +1,344 @@
+//! This module contains a [`IterTable`] table.
+//!
+//! In contrast to [`Table`] [`IterTable`] does no allocations but it consumes an iterator.
+//! It's useful when you don't want to re/allocate a buffer for your data.
+//!
+//! # Example
+//!
+//! ```
+//! use tabled::{grid::records::IterRecords, tables::IterTable};
+//!
+//! let iterator = vec![vec!["First", "row"], vec!["Second", "row"]];
+//! let records = IterRecords::new(iterator, 2, Some(2));
+//! let table = IterTable::new(records);
+//!
+//! let s = table.to_string();
+//!
+//! assert_eq!(
+//! s,
+//! "+--------+-----+\n\
+//! | First | row |\n\
+//! +--------+-----+\n\
+//! | Second | row |\n\
+//! +--------+-----+",
+//! );
+//! ```
+//!
+//! [`Table`]: crate::Table
+
+use std::{fmt, io};
+
+use crate::{
+ grid::{
+ colors::NoColors,
+ config::{AlignmentHorizontal, CompactConfig, Indent, Sides, SpannedConfig},
+ dimension::{CompactGridDimension, DimensionValue, StaticDimension},
+ records::{
+ into_records::{
+ truncate_records::ExactValue, BufColumns, BufRows, LimitColumns, LimitRows,
+ TruncateContent,
+ },
+ IntoRecords, IterRecords,
+ },
+ Grid,
+ },
+ settings::{Style, TableOption},
+};
+
+use super::util::utf8_writer::UTF8Writer;
+
+/// A table which consumes an [`IntoRecords`] iterator.
+///
+/// To be able to build table we need a dimensions.
+/// If no width and count_columns is set, [`IterTable`] will sniff the records, by
+/// keeping a number of rows buffered (You can set the number via [`IterTable::sniff`]).
+#[derive(Debug, Clone)]
+pub struct IterTable<I> {
+ records: I,
+ cfg: CompactConfig,
+ table: Settings,
+}
+
+#[derive(Debug, Clone)]
+struct Settings {
+ sniff: usize,
+ count_columns: Option<usize>,
+ count_rows: Option<usize>,
+ width: Option<usize>,
+ height: Option<usize>,
+}
+
+impl<I> IterTable<I> {
+ /// Creates a new [`IterTable`] structure.
+ pub fn new(iter: I) -> Self
+ where
+ I: IntoRecords,
+ {
+ Self {
+ records: iter,
+ cfg: create_config(),
+ table: Settings {
+ sniff: 1000,
+ count_columns: None,
+ count_rows: None,
+ height: None,
+ width: None,
+ },
+ }
+ }
+
+ /// With is a generic function which applies options to the [`IterTable`].
+ pub fn with<O>(mut self, option: O) -> Self
+ where
+ for<'a> O: TableOption<IterRecords<&'a I>, StaticDimension, CompactConfig>,
+ {
+ let count_columns = self.table.count_columns.unwrap_or(0);
+ let mut records = IterRecords::new(&self.records, count_columns, self.table.count_rows);
+ let mut dims = StaticDimension::new(DimensionValue::Exact(0), DimensionValue::Exact(1));
+ option.change(&mut records, &mut self.cfg, &mut dims);
+
+ self
+ }
+
+ /// Limit a number of columns.
+ pub fn columns(mut self, count_columns: usize) -> Self {
+ self.table.count_columns = Some(count_columns);
+ self
+ }
+
+ /// Limit a number of rows.
+ pub fn rows(mut self, count_rows: usize) -> Self {
+ self.table.count_rows = Some(count_rows);
+ self
+ }
+
+ /// Limit an amount of rows will be read for dimension estimations.
+ pub fn sniff(mut self, count: usize) -> Self {
+ self.table.sniff = count;
+ self
+ }
+
+ /// Set a height for each row.
+ pub fn height(mut self, size: usize) -> Self {
+ self.table.height = Some(size);
+ self
+ }
+
+ /// Set a width for each column.
+ pub fn width(mut self, size: usize) -> Self {
+ self.table.width = Some(size);
+ self
+ }
+
+ /// Build a string.
+ ///
+ /// We can't implement [`std::string::ToString`] cause it does takes `&self` reference.
+ #[allow(clippy::inherent_to_string)]
+ pub fn to_string(self) -> String
+ where
+ I: IntoRecords,
+ {
+ let mut buf = String::new();
+ self.fmt(&mut buf).expect("safe");
+
+ buf
+ }
+
+ /// Format table into [`io::Write`]r.
+ pub fn build<W>(self, writer: W) -> io::Result<()>
+ where
+ I: IntoRecords,
+ W: io::Write,
+ {
+ let writer = UTF8Writer::new(writer);
+ self.fmt(writer)
+ .map_err(|err| io::Error::new(io::ErrorKind::Other, err))
+ }
+
+ /// Format table into [fmt::Write]er.
+ pub fn fmt<W>(self, writer: W) -> fmt::Result
+ where
+ I: IntoRecords,
+ W: fmt::Write,
+ {
+ build_grid(writer, self.records, self.cfg, self.table)
+ }
+}
+
+fn build_grid<W: fmt::Write, I: IntoRecords>(
+ f: W,
+ iter: I,
+ cfg: CompactConfig,
+ opts: Settings,
+) -> fmt::Result {
+ let dont_sniff = opts.width.is_some() && opts.count_columns.is_some();
+ if dont_sniff {
+ build_table_with_static_dims(f, iter, cfg, opts)
+ } else if opts.width.is_none() {
+ build_table_sniffing_with_unknown_width(f, iter, cfg, opts)
+ } else {
+ build_table_sniffing_with_known_width(f, iter, cfg, opts)
+ }
+}
+
+fn build_table_with_static_dims<W, I>(
+ f: W,
+ iter: I,
+ cfg: CompactConfig,
+ opts: Settings,
+) -> fmt::Result
+where
+ W: fmt::Write,
+ I: IntoRecords,
+{
+ let count_columns = opts.count_columns.unwrap();
+ let width = opts.width.unwrap();
+ let height = opts.height.unwrap_or(1);
+ let contentw = ExactValue::Exact(width);
+ let pad = cfg.get_padding();
+ let w = DimensionValue::Exact(width + pad.left.size + pad.right.size);
+ let h = DimensionValue::Exact(height + pad.top.size + pad.bottom.size);
+ let dims = StaticDimension::new(w, h);
+ let cfg = SpannedConfig::from(cfg);
+
+ match opts.count_rows {
+ Some(limit) => {
+ let records = LimitRows::new(iter, limit);
+ let records = build_records(records, contentw, count_columns, Some(limit));
+ Grid::new(records, dims, cfg, NoColors).build(f)
+ }
+ None => {
+ let records = build_records(iter, contentw, count_columns, None);
+ Grid::new(records, dims, cfg, NoColors).build(f)
+ }
+ }
+}
+
+fn build_table_sniffing_with_unknown_width<W, I>(
+ f: W,
+ iter: I,
+ cfg: CompactConfig,
+ opts: Settings,
+) -> fmt::Result
+where
+ W: fmt::Write,
+ I: IntoRecords,
+{
+ let records = BufRows::new(iter, opts.sniff);
+ let records = BufColumns::from(records);
+
+ let count_columns = get_count_columns(&opts, records.as_slice());
+
+ let (mut width, height) = {
+ let records = LimitColumns::new(records.as_slice(), count_columns);
+ let records = IterRecords::new(records, count_columns, None);
+ CompactGridDimension::dimension(records, &cfg)
+ };
+
+ let padding = cfg.get_padding();
+ let pad = padding.left.size + padding.right.size;
+ let padv = padding.top.size + padding.bottom.size;
+
+ if opts.sniff == 0 {
+ width = std::iter::repeat(pad)
+ .take(count_columns)
+ .collect::<Vec<_>>();
+ }
+
+ let content_width = ExactValue::List(width.iter().map(|i| i.saturating_sub(pad)).collect());
+ let dims_width = DimensionValue::List(width);
+
+ let height_exact = opts.height.unwrap_or(1) + padv;
+ let mut dims_height = DimensionValue::Partial(height, height_exact);
+
+ if opts.height.is_some() {
+ dims_height = DimensionValue::Exact(height_exact);
+ }
+
+ let dims = StaticDimension::new(dims_width, dims_height);
+ let cfg = SpannedConfig::from(cfg);
+
+ match opts.count_rows {
+ Some(limit) => {
+ let records = LimitRows::new(records, limit);
+ let records = build_records(records, content_width, count_columns, Some(limit));
+ Grid::new(records, dims, cfg, NoColors).build(f)
+ }
+ None => {
+ let records = build_records(records, content_width, count_columns, None);
+ Grid::new(records, dims, cfg, NoColors).build(f)
+ }
+ }
+}
+
+fn build_table_sniffing_with_known_width<W, I>(
+ f: W,
+ iter: I,
+ cfg: CompactConfig,
+ opts: Settings,
+) -> fmt::Result
+where
+ W: fmt::Write,
+ I: IntoRecords,
+{
+ let records = BufRows::new(iter, opts.sniff);
+ let records = BufColumns::from(records);
+
+ let count_columns = get_count_columns(&opts, records.as_slice());
+
+ let width = opts.width.unwrap();
+ let contentw = ExactValue::Exact(width);
+
+ let padding = cfg.get_padding();
+ let pad = padding.left.size + padding.right.size;
+ let padv = padding.top.size + padding.bottom.size;
+
+ let height = opts.height.unwrap_or(1) + padv;
+ let dimsh = DimensionValue::Exact(height);
+ let dimsw = DimensionValue::Exact(width + pad);
+ let dims = StaticDimension::new(dimsw, dimsh);
+
+ let cfg = SpannedConfig::from(cfg);
+
+ match opts.count_rows {
+ Some(limit) => {
+ let records = LimitRows::new(records, limit);
+ let records = build_records(records, contentw, count_columns, Some(limit));
+ Grid::new(records, dims, cfg, NoColors).build(f)
+ }
+ None => {
+ let records = build_records(records, contentw, count_columns, None);
+ Grid::new(records, dims, cfg, NoColors).build(f)
+ }
+ }
+}
+
+fn get_count_columns(opts: &Settings, buf: &[Vec<String>]) -> usize {
+ match opts.count_columns {
+ Some(size) => size,
+ None => buf.iter().map(|row| row.len()).max().unwrap_or(0),
+ }
+}
+
+fn create_config() -> CompactConfig {
+ CompactConfig::default()
+ .set_padding(Sides::new(
+ Indent::spaced(1),
+ Indent::spaced(1),
+ Indent::default(),
+ Indent::default(),
+ ))
+ .set_alignment_horizontal(AlignmentHorizontal::Left)
+ .set_borders(*Style::ascii().get_borders())
+}
+
+fn build_records<I: IntoRecords>(
+ records: I,
+ width: ExactValue<'_>,
+ count_columns: usize,
+ count_rows: Option<usize>,
+) -> IterRecords<LimitColumns<TruncateContent<'_, I>>> {
+ let records = TruncateContent::new(records, width);
+ let records = LimitColumns::new(records, count_columns);
+ IterRecords::new(records, count_columns, count_rows)
+}
diff --git a/vendor/tabled/src/tables/mod.rs b/vendor/tabled/src/tables/mod.rs
new file mode 100644
index 000000000..e0c4bf794
--- /dev/null
+++ b/vendor/tabled/src/tables/mod.rs
@@ -0,0 +1,48 @@
+//! Module contains a list of table representatives.
+//!
+//! ## [`Table`]
+//!
+//! A default table implementation.
+//!
+//! ## [`IterTable`]
+//!
+//! Just like [`Table`] but it's API is a bit different to serve better in context
+//! where there is a memory limit.
+//!
+//! ## [`ExtendedTable`]
+//!
+//! It's a table which is useful for large amount of data.
+//!
+//! ## [`PoolTable`]
+//!
+//! A table with a greather controll of a layout.
+
+mod compact;
+mod util;
+
+#[cfg(feature = "std")]
+mod extended;
+#[cfg(feature = "std")]
+mod iter;
+#[cfg(feature = "std")]
+mod table;
+#[cfg(feature = "std")]
+mod table_pool;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use table::Table;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use iter::IterTable;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use extended::ExtendedTable;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub use table_pool::{PoolTable, TableValue};
+
+pub use compact::CompactTable;
diff --git a/vendor/tabled/src/tables/table.rs b/vendor/tabled/src/tables/table.rs
new file mode 100644
index 000000000..d6ff37661
--- /dev/null
+++ b/vendor/tabled/src/tables/table.rs
@@ -0,0 +1,464 @@
+//! This module contains a main table representation [`Table`].
+
+use core::ops::DerefMut;
+use std::{borrow::Cow, fmt, iter::FromIterator};
+
+use crate::{
+ builder::Builder,
+ grid::{
+ colors::NoColors,
+ config::{
+ AlignmentHorizontal, ColorMap, ColoredConfig, CompactConfig, Entity, Formatting,
+ Indent, Sides, SpannedConfig,
+ },
+ dimension::{CompleteDimensionVecRecords, Dimension, Estimate, PeekableDimension},
+ records::{
+ vec_records::{CellInfo, VecRecords},
+ ExactRecords, Records,
+ },
+ PeekableGrid,
+ },
+ settings::{Style, TableOption},
+ Tabled,
+};
+
+/// The structure provides an interface for building a table for types that implements [`Tabled`].
+///
+/// To build a string representation of a table you must use a [`std::fmt::Display`].
+/// Or simply call `.to_string()` method.
+///
+/// The default table [`Style`] is [`Style::ascii`],
+/// with a 1 left and right [`Padding`].
+///
+/// ## Example
+///
+/// ### Basic usage
+///
+/// ```rust,no_run
+/// use tabled::Table;
+///
+/// let table = Table::new(&["Year", "2021"]);
+/// ```
+///
+/// ### With settings
+///
+/// ```rust,no_run
+/// use tabled::{Table, settings::{Style, Alignment}};
+///
+/// let data = vec!["Hello", "2021"];
+/// let mut table = Table::new(&data);
+/// table.with(Style::psql()).with(Alignment::left());
+///
+/// println!("{}", table);
+/// ```
+///
+/// [`Padding`]: crate::settings::Padding
+/// [`Style`]: crate::settings::Style
+/// [`Style::ascii`]: crate::settings::Style::ascii
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Table {
+ records: VecRecords<CellInfo<String>>,
+ config: ColoredConfig,
+ dimension: CompleteDimensionVecRecords<'static>,
+}
+
+impl Table {
+ /// New creates a Table instance.
+ ///
+ /// If you use a reference iterator you'd better use [`FromIterator`] instead.
+ /// As it has a different lifetime constraints and make less copies therefore.
+ pub fn new<I, T>(iter: I) -> Self
+ where
+ I: IntoIterator<Item = T>,
+ T: Tabled,
+ {
+ let mut header = Vec::with_capacity(T::LENGTH);
+ for text in T::headers() {
+ let text = text.into_owned();
+ let cell = CellInfo::new(text);
+ header.push(cell);
+ }
+
+ let mut records = vec![header];
+ for row in iter.into_iter() {
+ let mut list = Vec::with_capacity(T::LENGTH);
+ for text in row.fields().into_iter() {
+ let text = text.into_owned();
+ let cell = CellInfo::new(text);
+
+ list.push(cell);
+ }
+
+ records.push(list);
+ }
+
+ let records = VecRecords::new(records);
+
+ Self {
+ records,
+ config: ColoredConfig::new(configure_grid()),
+ dimension: CompleteDimensionVecRecords::default(),
+ }
+ }
+
+ /// Creates a builder from a data set given.
+ ///
+ /// # Example
+ ///
+ ///
+ #[cfg_attr(feature = "derive", doc = "```")]
+ #[cfg_attr(not(feature = "derive"), doc = "```ignore")]
+ /// use tabled::{
+ /// Table, Tabled,
+ /// settings::{object::Segment, Modify, Alignment}
+ /// };
+ ///
+ /// #[derive(Tabled)]
+ /// struct User {
+ /// name: &'static str,
+ /// #[tabled(inline("device::"))]
+ /// device: Device,
+ /// }
+ ///
+ /// #[derive(Tabled)]
+ /// enum Device {
+ /// PC,
+ /// Mobile
+ /// }
+ ///
+ /// let data = vec![
+ /// User { name: "Vlad", device: Device::Mobile },
+ /// User { name: "Dimitry", device: Device::PC },
+ /// User { name: "John", device: Device::PC },
+ /// ];
+ ///
+ /// let mut table = Table::builder(data)
+ /// .index()
+ /// .column(0)
+ /// .transpose()
+ /// .build()
+ /// .with(Modify::new(Segment::new(1.., 1..)).with(Alignment::center()))
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "+----------------+------+---------+------+\n\
+ /// | name | Vlad | Dimitry | John |\n\
+ /// +----------------+------+---------+------+\n\
+ /// | device::PC | | + | + |\n\
+ /// +----------------+------+---------+------+\n\
+ /// | device::Mobile | + | | |\n\
+ /// +----------------+------+---------+------+"
+ /// )
+ /// ```
+ pub fn builder<I, T>(iter: I) -> Builder
+ where
+ T: Tabled,
+ I: IntoIterator<Item = T>,
+ {
+ let mut records = Vec::new();
+ for row in iter {
+ let mut list = Vec::with_capacity(T::LENGTH);
+ for text in row.fields().into_iter() {
+ list.push(text.into_owned());
+ }
+
+ records.push(list);
+ }
+
+ let mut b = Builder::from(records);
+ let _ = b.set_header(T::headers()).hint_column_size(T::LENGTH);
+
+ b
+ }
+
+ /// With is a generic function which applies options to the [`Table`].
+ ///
+ /// It applies settings immediately.
+ pub fn with<O>(&mut self, option: O) -> &mut Self
+ where
+ O: TableOption<
+ VecRecords<CellInfo<String>>,
+ CompleteDimensionVecRecords<'static>,
+ ColoredConfig,
+ >,
+ {
+ self.dimension.clear_width();
+ self.dimension.clear_height();
+
+ option.change(&mut self.records, &mut self.config, &mut self.dimension);
+
+ self
+ }
+
+ /// Returns a table shape (count rows, count columns).
+ pub fn shape(&self) -> (usize, usize) {
+ (self.count_rows(), self.count_columns())
+ }
+
+ /// Returns an amount of rows in the table.
+ pub fn count_rows(&self) -> usize {
+ self.records.count_rows()
+ }
+
+ /// Returns an amount of columns in the table.
+ pub fn count_columns(&self) -> usize {
+ self.records.count_columns()
+ }
+
+ /// Returns a table shape (count rows, count columns).
+ pub fn is_empty(&self) -> bool {
+ let (count_rows, count_cols) = self.shape();
+ count_rows == 0 || count_cols == 0
+ }
+
+ /// Returns total widths of a table, including margin and horizontal lines.
+ pub fn total_height(&self) -> usize {
+ let mut dims = CompleteDimensionVecRecords::from_origin(&self.dimension);
+ dims.estimate(&self.records, self.config.as_ref());
+
+ let total = (0..self.count_rows())
+ .map(|row| dims.get_height(row))
+ .sum::<usize>();
+ let counth = self.config.count_horizontal(self.count_rows());
+
+ let margin = self.config.get_margin();
+
+ total + counth + margin.top.size + margin.bottom.size
+ }
+
+ /// Returns total widths of a table, including margin and vertical lines.
+ pub fn total_width(&self) -> usize {
+ let mut dims = CompleteDimensionVecRecords::from_origin(&self.dimension);
+ dims.estimate(&self.records, self.config.as_ref());
+
+ let total = (0..self.count_columns())
+ .map(|col| dims.get_width(col))
+ .sum::<usize>();
+ let countv = self.config.count_vertical(self.count_columns());
+
+ let margin = self.config.get_margin();
+
+ total + countv + margin.left.size + margin.right.size
+ }
+
+ /// Returns a table config.
+ pub fn get_config(&self) -> &ColoredConfig {
+ &self.config
+ }
+
+ /// Returns a table config.
+ pub fn get_config_mut(&mut self) -> &mut ColoredConfig {
+ &mut self.config
+ }
+
+ /// Returns a used records.
+ pub fn get_records(&self) -> &VecRecords<CellInfo<String>> {
+ &self.records
+ }
+
+ /// Returns a used records.
+ pub fn get_records_mut(&mut self) -> &mut VecRecords<CellInfo<String>> {
+ &mut self.records
+ }
+}
+
+impl Default for Table {
+ fn default() -> Self {
+ Self {
+ records: VecRecords::default(),
+ config: ColoredConfig::new(configure_grid()),
+ dimension: CompleteDimensionVecRecords::default(),
+ }
+ }
+}
+
+impl fmt::Display for Table {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.is_empty() {
+ return Ok(());
+ }
+
+ let config = use_format_configuration(f, self);
+ let colors = self.config.get_colors();
+
+ if !self.dimension.is_empty() {
+ let mut dims = self.dimension.clone();
+ dims.estimate(&self.records, config.as_ref());
+
+ print_grid(f, &self.records, &config, &dims, colors)
+ } else {
+ let mut dims = PeekableDimension::default();
+ dims.estimate(&self.records, &config);
+
+ print_grid(f, &self.records, &config, &dims, colors)
+ }
+ }
+}
+
+impl<T, V> FromIterator<T> for Table
+where
+ T: IntoIterator<Item = V>,
+ V: Into<String>,
+{
+ fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
+ Builder::from_iter(iter.into_iter().map(|i| i.into_iter().map(|s| s.into()))).build()
+ }
+}
+
+impl From<Builder> for Table {
+ fn from(builder: Builder) -> Self {
+ let data: Vec<Vec<CellInfo<String>>> = builder.into();
+ let records = VecRecords::new(data);
+
+ Self {
+ records,
+ config: ColoredConfig::new(configure_grid()),
+ dimension: CompleteDimensionVecRecords::default(),
+ }
+ }
+}
+
+impl From<Table> for Builder {
+ fn from(val: Table) -> Self {
+ let count_columns = val.count_columns();
+ let data: Vec<Vec<CellInfo<String>>> = val.records.into();
+ let mut builder = Builder::from(data);
+ let _ = builder.hint_column_size(count_columns);
+ builder
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for CompactConfig {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ *cfg.deref_mut() = self.into();
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for ColoredConfig {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ *cfg = self;
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for SpannedConfig {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ *cfg.deref_mut() = self;
+ }
+}
+
+impl<R, D> TableOption<R, D, ColoredConfig> for &SpannedConfig {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ *cfg.deref_mut() = self.clone();
+ }
+}
+
+fn convert_fmt_alignment(alignment: fmt::Alignment) -> AlignmentHorizontal {
+ match alignment {
+ fmt::Alignment::Left => AlignmentHorizontal::Left,
+ fmt::Alignment::Right => AlignmentHorizontal::Right,
+ fmt::Alignment::Center => AlignmentHorizontal::Center,
+ }
+}
+
+fn table_padding(alignment: fmt::Alignment, available: usize) -> (usize, usize) {
+ match alignment {
+ fmt::Alignment::Left => (available, 0),
+ fmt::Alignment::Right => (0, available),
+ fmt::Alignment::Center => {
+ let left = available / 2;
+ let right = available - left;
+ (left, right)
+ }
+ }
+}
+
+fn configure_grid() -> SpannedConfig {
+ let mut cfg = SpannedConfig::default();
+ cfg.set_padding(
+ Entity::Global,
+ Sides::new(
+ Indent::spaced(1),
+ Indent::spaced(1),
+ Indent::default(),
+ Indent::default(),
+ ),
+ );
+ cfg.set_alignment_horizontal(Entity::Global, AlignmentHorizontal::Left);
+ cfg.set_formatting(Entity::Global, Formatting::new(false, false, false));
+ cfg.set_borders(*Style::ascii().get_borders());
+
+ cfg
+}
+
+fn use_format_configuration<'a>(
+ f: &mut fmt::Formatter<'_>,
+ table: &'a Table,
+) -> Cow<'a, SpannedConfig> {
+ if f.align().is_some() || f.width().is_some() {
+ let mut cfg = table.config.as_ref().clone();
+
+ set_align_table(f, &mut cfg);
+ set_width_table(f, &mut cfg, table);
+
+ Cow::Owned(cfg)
+ } else {
+ Cow::Borrowed(table.config.as_ref())
+ }
+}
+
+fn set_align_table(f: &fmt::Formatter<'_>, cfg: &mut SpannedConfig) {
+ if let Some(alignment) = f.align() {
+ let alignment = convert_fmt_alignment(alignment);
+ cfg.set_alignment_horizontal(Entity::Global, alignment);
+ }
+}
+
+fn set_width_table(f: &fmt::Formatter<'_>, cfg: &mut SpannedConfig, table: &Table) {
+ if let Some(width) = f.width() {
+ let total_width = table.total_width();
+ if total_width >= width {
+ return;
+ }
+
+ let mut fill = f.fill();
+ if fill == char::default() {
+ fill = ' ';
+ }
+
+ let available = width - total_width;
+ let alignment = f.align().unwrap_or(fmt::Alignment::Left);
+ let (left, right) = table_padding(alignment, available);
+
+ let mut margin = cfg.get_margin();
+ margin.left.size += left;
+ margin.right.size += right;
+
+ if (margin.left.size > 0 && margin.left.fill == char::default()) || fill != char::default()
+ {
+ margin.left.fill = fill;
+ }
+
+ if (margin.right.size > 0 && margin.right.fill == char::default())
+ || fill != char::default()
+ {
+ margin.right.fill = fill;
+ }
+
+ cfg.set_margin(margin);
+ }
+}
+
+fn print_grid<F: fmt::Write, D: Dimension>(
+ f: &mut F,
+ records: &VecRecords<CellInfo<String>>,
+ cfg: &SpannedConfig,
+ dims: D,
+ colors: &ColorMap,
+) -> fmt::Result {
+ if !colors.is_empty() {
+ PeekableGrid::new(records, cfg, &dims, colors).build(f)
+ } else {
+ PeekableGrid::new(records, cfg, &dims, NoColors).build(f)
+ }
+}
diff --git a/vendor/tabled/src/tables/table_pool.rs b/vendor/tabled/src/tables/table_pool.rs
new file mode 100644
index 000000000..07d2a5437
--- /dev/null
+++ b/vendor/tabled/src/tables/table_pool.rs
@@ -0,0 +1,1607 @@
+use core::fmt::{self, Display, Formatter};
+
+use crate::{
+ grid::{
+ config::{AlignmentHorizontal, CompactMultilineConfig, Indent, Sides},
+ dimension::{DimensionPriority, PoolTableDimension},
+ records::EmptyRecords,
+ records::IntoRecords,
+ },
+ settings::{Style, TableOption},
+};
+
+/// [`PoolTable`] is a table which allows a greater set of possibilities for cell alignment.
+/// It's data is not aligned in any way by default.
+///
+/// It works similar to the main [`Table`] by default.
+///
+///
+/// ```
+/// use tabled::tables::PoolTable;
+///
+/// let data = vec![
+/// vec!["Hello", "World", "!"],
+/// vec!["Salve", "mondo", "!"],
+/// vec!["Hola", "mundo", "!"],
+/// ];
+///
+/// let table = PoolTable::new(data).to_string();
+///
+/// assert_eq!(
+/// table,
+/// "+-------+-------+---+\n\
+/// | Hello | World | ! |\n\
+/// +-------+-------+---+\n\
+/// | Salve | mondo | ! |\n\
+/// +-------+-------+---+\n\
+/// | Hola | mundo | ! |\n\
+/// +-------+-------+---+"
+/// )
+/// ```
+///
+/// But it allows you to have a different number of columns inside the rows.
+///
+/// ```
+/// use tabled::tables::PoolTable;
+///
+/// let data = vec![
+/// vec!["Hello", "World", "!"],
+/// vec!["Salve, mondo!"],
+/// vec!["Hola", "mundo", "", "", "!"],
+/// ];
+///
+/// let table = PoolTable::new(data).to_string();
+///
+/// assert_eq!(
+/// table,
+/// "+---------+---------+----+\n\
+/// | Hello | World | ! |\n\
+/// +---------+---------+----+\n\
+/// | Salve, mondo! |\n\
+/// +------+-------+--+--+---+\n\
+/// | Hola | mundo | | | ! |\n\
+/// +------+-------+--+--+---+"
+/// )
+/// ```
+///
+/// Notice that you also can build a custom table layout by using [`TableValue`].
+///
+/// ```
+/// use tabled::tables::{PoolTable, TableValue};
+///
+/// let message = "Hello\nWorld";
+///
+/// let data = TableValue::Column(vec![
+/// TableValue::Row(vec![
+/// TableValue::Column(vec![
+/// TableValue::Cell(String::from(message)),
+/// ]),
+/// TableValue::Column(vec![
+/// TableValue::Cell(String::from(message)),
+/// TableValue::Row(vec![
+/// TableValue::Cell(String::from(message)),
+/// TableValue::Cell(String::from(message)),
+/// TableValue::Cell(String::from(message)),
+/// ])
+/// ]),
+/// ]),
+/// TableValue::Cell(String::from(message)),
+/// ]);
+///
+/// let table = PoolTable::from(data).to_string();
+///
+/// assert_eq!(
+/// table,
+/// "+-------+-----------------------+\n\
+/// | Hello | Hello |\n\
+/// | World | World |\n\
+/// | +-------+-------+-------+\n\
+/// | | Hello | Hello | Hello |\n\
+/// | | World | World | World |\n\
+/// +-------+-------+-------+-------+\n\
+/// | Hello |\n\
+/// | World |\n\
+/// +-------------------------------+"
+/// )
+/// ```
+///
+/// [`Table`]: crate::Table
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct PoolTable {
+ config: CompactMultilineConfig,
+ dims: PoolTableDimension,
+ value: TableValue,
+}
+
+impl PoolTable {
+ /// Creates a [`PoolTable`] out from a record iterator.
+ pub fn new<I: IntoRecords>(iter: I) -> Self {
+ let value = TableValue::Column(
+ iter.iter_rows()
+ .into_iter()
+ .map(|row| {
+ TableValue::Row(
+ row.into_iter()
+ .map(|cell| cell.as_ref().to_string())
+ .map(TableValue::Cell)
+ .collect(),
+ )
+ })
+ .collect(),
+ );
+
+ Self {
+ config: configure_grid(),
+ dims: PoolTableDimension::new(DimensionPriority::List, DimensionPriority::List),
+ value,
+ }
+ }
+
+ /// A is a generic function which applies options to the [`PoolTable`] configuration.
+ ///
+ /// Notice that it has a limited support of options.
+ ///
+ /// ```
+ /// use tabled::tables::PoolTable;
+ /// use tabled::settings::{Style, Padding};
+ ///
+ /// let data = vec![
+ /// vec!["Hello", "World", "!"],
+ /// vec!["Salve", "mondo", "!"],
+ /// vec!["Hola", "mundo", "!"],
+ /// ];
+ ///
+ /// let table = PoolTable::new(data)
+ /// .with(Style::extended())
+ /// .with(Padding::zero())
+ /// .to_string();
+ ///
+ /// assert_eq!(
+ /// table,
+ /// "╔═════╦═════╦═╗\n\
+ /// ║Hello║World║!║\n\
+ /// ╠═════╬═════╬═╣\n\
+ /// ║Salve║mondo║!║\n\
+ /// ╠═════╬═════╬═╣\n\
+ /// ║Hola ║mundo║!║\n\
+ /// ╚═════╩═════╩═╝"
+ /// )
+ /// ```
+ pub fn with<O>(&mut self, option: O) -> &mut Self
+ where
+ O: TableOption<EmptyRecords, PoolTableDimension, CompactMultilineConfig>,
+ {
+ let mut records = EmptyRecords::default();
+ option.change(&mut records, &mut self.config, &mut self.dims);
+
+ self
+ }
+}
+
+impl From<TableValue> for PoolTable {
+ fn from(value: TableValue) -> Self {
+ Self {
+ config: configure_grid(),
+ dims: PoolTableDimension::new(DimensionPriority::List, DimensionPriority::List),
+ value,
+ }
+ }
+}
+
+impl Display for PoolTable {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ print::build_table(&self.value, &self.config, self.dims).fmt(f)
+ }
+}
+
+/// [`TableValue`] a structure which is responsible for a [`PoolTable`] layout.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum TableValue {
+ /// A horizontal row.
+ Row(Vec<TableValue>),
+ /// A vertical column.
+ Column(Vec<TableValue>),
+ /// A single cell.
+ Cell(String),
+}
+
+fn configure_grid() -> CompactMultilineConfig {
+ let pad = Sides::new(
+ Indent::spaced(1),
+ Indent::spaced(1),
+ Indent::default(),
+ Indent::default(),
+ );
+
+ CompactMultilineConfig::default()
+ .set_padding(pad)
+ .set_alignment_horizontal(AlignmentHorizontal::Left)
+ .set_borders(*Style::ascii().get_borders())
+}
+
+impl<R, C> TableOption<R, PoolTableDimension, C> for PoolTableDimension {
+ fn change(self, _: &mut R, _: &mut C, dimension: &mut PoolTableDimension) {
+ *dimension = self;
+ }
+}
+
+impl<R, D> TableOption<R, D, CompactMultilineConfig> for CompactMultilineConfig {
+ fn change(self, _: &mut R, config: &mut CompactMultilineConfig, _: &mut D) {
+ *config = self;
+ }
+}
+
+mod print {
+ use std::{cmp::max, collections::HashMap, iter::repeat};
+
+ use papergrid::{
+ color::StaticColor,
+ config::{Border, Borders},
+ util::string::string_width_multiline,
+ };
+
+ use crate::{
+ builder::Builder,
+ grid::{
+ config::{
+ AlignmentHorizontal, AlignmentVertical, ColoredConfig, CompactMultilineConfig,
+ Indent, Offset, Sides,
+ },
+ dimension::{Dimension, DimensionPriority, Estimate, PoolTableDimension},
+ records::Records,
+ util::string::{count_lines, get_lines, string_dimension, string_width},
+ },
+ settings::{Padding, Style, TableOption},
+ };
+
+ use super::TableValue;
+
+ #[derive(Debug, Default)]
+ struct PrintContext {
+ pos: usize,
+ is_last_col: bool,
+ is_last_row: bool,
+ is_first_col: bool,
+ is_first_row: bool,
+ kv: bool,
+ kv_is_first: bool,
+ list: bool,
+ list_is_first: bool,
+ no_left: bool,
+ no_right: bool,
+ no_bottom: bool,
+ lean_top: bool,
+ top_intersection: bool,
+ top_left: bool,
+ intersections_horizontal: Vec<usize>,
+ intersections_vertical: Vec<usize>,
+ size: Dim,
+ }
+
+ struct CellData {
+ content: String,
+ intersections_horizontal: Vec<usize>,
+ intersections_vertical: Vec<usize>,
+ }
+
+ impl CellData {
+ fn new(content: String, i_horizontal: Vec<usize>, i_vertical: Vec<usize>) -> Self {
+ Self {
+ content,
+ intersections_horizontal: i_horizontal,
+ intersections_vertical: i_vertical,
+ }
+ }
+ }
+
+ pub(super) fn build_table(
+ val: &TableValue,
+ cfg: &CompactMultilineConfig,
+ dims_priority: PoolTableDimension,
+ ) -> String {
+ let dims = collect_table_dimensions(val, cfg);
+ let ctx = PrintContext {
+ is_last_col: true,
+ is_last_row: true,
+ is_first_col: true,
+ is_first_row: true,
+ size: *dims.all.get(&0).unwrap(),
+ ..Default::default()
+ };
+
+ let data = _build_table(val, cfg, &dims, dims_priority, ctx);
+ let mut table = data.content;
+
+ let margin = cfg.get_margin();
+ let has_margin = margin.top.size > 0
+ || margin.bottom.size > 0
+ || margin.left.size > 0
+ || margin.right.size > 0;
+ if has_margin {
+ let color = convert_border_colors(cfg.get_margin_color());
+ table = set_margin(&table, *margin, color);
+ }
+
+ table
+ }
+
+ fn _build_table(
+ val: &TableValue,
+ cfg: &CompactMultilineConfig,
+ dims: &Dimensions,
+ priority: PoolTableDimension,
+ ctx: PrintContext,
+ ) -> CellData {
+ match val {
+ TableValue::Cell(text) => generate_value_cell(text, cfg, ctx),
+ TableValue::Row(list) => {
+ if list.is_empty() {
+ return generate_value_cell("", cfg, ctx);
+ }
+
+ generate_table_row(list, cfg, dims, priority, ctx)
+ }
+ TableValue::Column(list) => {
+ if list.is_empty() {
+ return generate_value_cell("", cfg, ctx);
+ }
+
+ generate_table_column(list, cfg, dims, priority, ctx)
+ }
+ }
+ }
+
+ fn generate_table_column(
+ list: &Vec<TableValue>,
+ cfg: &CompactMultilineConfig,
+ dims: &Dimensions,
+ priority: PoolTableDimension,
+ ctx: PrintContext,
+ ) -> CellData {
+ let array_dims = dims.arrays.get(&ctx.pos).unwrap();
+
+ let height = dims.all.get(&ctx.pos).unwrap().height;
+ let additional_height = ctx.size.height - height;
+ let (chunk_height, mut rest_height) = split_value(additional_height, list.len());
+
+ let mut intersections_horizontal = ctx.intersections_horizontal;
+ let mut intersections_vertical = ctx.intersections_vertical;
+ let mut next_vsplit = false;
+ let mut next_intersections_vertical = vec![];
+
+ let mut builder = Builder::new();
+ for (i, val) in list.iter().enumerate() {
+ let val_pos = *array_dims.index.get(&i).unwrap();
+
+ let mut height = dims.all.get(&val_pos).unwrap().height;
+ match priority.height() {
+ DimensionPriority::First => {
+ if i == 0 {
+ height += additional_height;
+ }
+ }
+ DimensionPriority::Last => {
+ if i + 1 == list.len() {
+ height += additional_height;
+ }
+ }
+ DimensionPriority::List => {
+ height += chunk_height;
+
+ if rest_height > 0 {
+ height += 1;
+ rest_height -= 1; // must be safe
+ }
+ }
+ }
+
+ let size = Dim::new(ctx.size.width, height);
+
+ let (split, intersections_vertical) =
+ short_splits3(&mut intersections_vertical, size.height);
+ let old_split = next_vsplit;
+ next_vsplit = split;
+
+ let is_prev_list_not_first = ctx.list && !ctx.list_is_first;
+ let valctx = PrintContext {
+ pos: val_pos,
+ is_last_col: ctx.is_last_col,
+ is_last_row: ctx.is_last_row && i + 1 == list.len(),
+ is_first_col: ctx.is_first_col,
+ is_first_row: ctx.is_first_row && i == 0,
+ kv: ctx.kv,
+ kv_is_first: ctx.kv_is_first,
+ list: true,
+ list_is_first: i == 0 && !is_prev_list_not_first,
+ no_left: ctx.no_left,
+ no_right: ctx.no_right,
+ no_bottom: ctx.no_bottom && i + 1 == list.len(),
+ lean_top: ctx.lean_top && i == 0,
+ top_intersection: (ctx.top_intersection && i == 0) || old_split,
+ top_left: ctx.top_left || i > 0,
+ intersections_horizontal,
+ intersections_vertical,
+ size,
+ };
+
+ let data = _build_table(val, cfg, dims, priority, valctx);
+ intersections_horizontal = data.intersections_horizontal;
+ next_intersections_vertical.extend(data.intersections_vertical);
+
+ let _ = builder.push_record([data.content]);
+ }
+
+ let table = builder
+ .build()
+ .with(Style::empty())
+ .with(Padding::zero())
+ .to_string();
+
+ CellData::new(table, intersections_horizontal, next_intersections_vertical)
+ }
+
+ fn generate_table_row(
+ list: &Vec<TableValue>,
+ cfg: &CompactMultilineConfig,
+ dims: &Dimensions,
+ priority: PoolTableDimension,
+ ctx: PrintContext,
+ ) -> CellData {
+ let array_dims = dims.arrays.get(&ctx.pos).unwrap();
+
+ let list_width = dims.all.get(&ctx.pos).unwrap().width;
+ let additional_width = ctx.size.width - list_width;
+ let (chunk_width, mut rest_width) = split_value(additional_width, list.len());
+
+ let mut intersections_horizontal = ctx.intersections_horizontal;
+ let mut intersections_vertical = ctx.intersections_vertical;
+ let mut new_intersections_horizontal = vec![];
+ let mut split_next = false;
+
+ let mut buf = Vec::with_capacity(list.len());
+ for (i, val) in list.iter().enumerate() {
+ let val_pos = *array_dims.index.get(&i).unwrap();
+
+ let mut width = dims.all.get(&val_pos).unwrap().width;
+ match priority.width() {
+ DimensionPriority::First => {
+ if i == 0 {
+ width += additional_width;
+ }
+ }
+ DimensionPriority::Last => {
+ if i + 1 == list.len() {
+ width += additional_width;
+ }
+ }
+ DimensionPriority::List => {
+ width += chunk_width;
+
+ if rest_width > 0 {
+ width += 1;
+ rest_width -= 1; // must be safe
+ }
+ }
+ }
+
+ let size = Dim::new(width, ctx.size.height);
+
+ let (split, intersections_horizontal) =
+ short_splits3(&mut intersections_horizontal, width);
+ let old_split = split_next;
+ split_next = split;
+
+ let is_prev_list_not_first = ctx.list && !ctx.list_is_first;
+ let valctx = PrintContext {
+ pos: val_pos,
+ is_first_col: ctx.is_first_col && i == 0,
+ is_last_col: ctx.is_last_col && i + 1 == list.len(),
+ is_last_row: ctx.is_last_row,
+ is_first_row: ctx.is_first_row,
+ kv: false,
+ kv_is_first: false,
+ list: false,
+ list_is_first: !is_prev_list_not_first,
+ no_left: false,
+ no_right: !(ctx.is_last_col && i + 1 == list.len()),
+ no_bottom: false,
+ lean_top: !(ctx.is_first_col && i == 0),
+ top_intersection: (ctx.top_intersection && i == 0) || old_split,
+ top_left: ctx.top_left && i == 0,
+ intersections_horizontal,
+ intersections_vertical,
+ size,
+ };
+
+ let val = _build_table(val, cfg, dims, priority, valctx);
+ intersections_vertical = val.intersections_vertical;
+ new_intersections_horizontal.extend(val.intersections_horizontal.iter());
+ let value = val.content;
+
+ buf.push(value);
+ }
+
+ let mut b = Builder::with_capacity(1);
+ let _ = b.hint_column_size(buf.len()).push_record(buf);
+ let table = b
+ .build()
+ .with(Style::empty())
+ .with(Padding::zero())
+ .to_string();
+
+ CellData::new(table, new_intersections_horizontal, intersections_vertical)
+ }
+
+ fn generate_value_cell(
+ text: &str,
+ cfg: &CompactMultilineConfig,
+ ctx: PrintContext,
+ ) -> CellData {
+ let width = ctx.size.width;
+ let height = ctx.size.height;
+ let table = generate_value_table(text, cfg, ctx);
+ CellData::new(table, vec![width], vec![height])
+ }
+
+ fn generate_value_table(
+ text: &str,
+ cfg: &CompactMultilineConfig,
+ mut ctx: PrintContext,
+ ) -> String {
+ if ctx.size.width == 0 || ctx.size.height == 0 {
+ return String::new();
+ }
+
+ let halignment = cfg.get_alignment_horizontal();
+ let valignment = cfg.get_alignment_vertical();
+ let pad = cfg.get_padding();
+ let pad_color = cfg.get_padding_color();
+ let pad_color = convert_border_colors(pad_color);
+ let lines_alignemnt = cfg.get_formatting().allow_lines_alignment;
+
+ let mut borders = *cfg.get_borders();
+
+ let bottom_intesection = cfg.get_borders().bottom_intersection.unwrap_or(' ');
+ let mut horizontal_splits = short_splits(&mut ctx.intersections_horizontal, ctx.size.width);
+ squash_splits(&mut horizontal_splits);
+
+ let right_intersection = borders.right_intersection.unwrap_or(' ');
+ let mut vertical_splits = short_splits(&mut ctx.intersections_vertical, ctx.size.height);
+ squash_splits(&mut vertical_splits);
+
+ config_borders(&mut borders, &ctx);
+ let border = create_border(borders);
+
+ let borders_colors = *cfg.get_borders_color();
+ let border_color = create_border(borders_colors);
+
+ let mut height = ctx.size.height;
+ height -= pad.top.size + pad.bottom.size;
+
+ let mut width = ctx.size.width;
+ width -= pad.left.size + pad.right.size;
+
+ let count_lines = count_lines(text);
+ let (top, bottom) = indent_vertical(valignment, height, count_lines);
+
+ let mut buf = String::new();
+ print_top_line(
+ &mut buf,
+ border,
+ border_color,
+ &horizontal_splits,
+ bottom_intesection,
+ ctx.size.width,
+ );
+
+ let mut line_index = 0;
+ let mut vertical_splits = &vertical_splits[..];
+
+ for _ in 0..top {
+ let mut border = border;
+ if vertical_splits.first() == Some(&line_index) {
+ border.left = Some(right_intersection);
+ vertical_splits = &vertical_splits[1..];
+ }
+
+ print_line(&mut buf, border, border_color, None, ' ', ctx.size.width);
+ line_index += 1;
+ }
+
+ for _ in 0..pad.top.size {
+ let mut border = border;
+ if vertical_splits.first() == Some(&line_index) {
+ border.left = Some(right_intersection);
+ vertical_splits = &vertical_splits[1..];
+ }
+
+ print_line(
+ &mut buf,
+ border,
+ border_color,
+ pad_color.top,
+ pad.top.fill,
+ ctx.size.width,
+ );
+ line_index += 1;
+ }
+
+ if lines_alignemnt {
+ for line in get_lines(text) {
+ let line_width = string_width(&line);
+ let (left, right) = indent_horizontal(halignment, width, line_width);
+
+ if border.has_left() {
+ let mut c = border.left.unwrap_or(' ');
+ if vertical_splits.first() == Some(&line_index) {
+ c = right_intersection;
+ vertical_splits = &vertical_splits[1..];
+ }
+
+ print_char(&mut buf, c, border_color.left);
+ }
+
+ print_chars(&mut buf, pad.left.fill, pad_color.left, pad.left.size);
+ buf.extend(repeat(' ').take(left));
+ buf.push_str(&line);
+ buf.extend(repeat(' ').take(right));
+ print_chars(&mut buf, pad.right.fill, pad_color.right, pad.right.size);
+
+ if border.has_right() {
+ print_char(&mut buf, border.right.unwrap_or(' '), border_color.right);
+ }
+
+ buf.push('\n');
+
+ line_index += 1;
+ }
+ } else {
+ let text_width = string_width_multiline(text);
+ let (left, _) = indent_horizontal(halignment, width, text_width);
+
+ for line in get_lines(text) {
+ let line_width = string_width(&line);
+ let right = width - line_width - left;
+
+ if border.has_left() {
+ let mut c = border.left.unwrap_or(' ');
+ if vertical_splits.first() == Some(&line_index) {
+ c = right_intersection;
+ vertical_splits = &vertical_splits[1..];
+ }
+
+ print_char(&mut buf, c, border_color.left);
+ }
+
+ print_chars(&mut buf, pad.left.fill, pad_color.left, pad.left.size);
+ buf.extend(repeat(' ').take(left));
+ buf.push_str(&line);
+ buf.extend(repeat(' ').take(right));
+ print_chars(&mut buf, pad.right.fill, pad_color.right, pad.right.size);
+
+ if border.has_right() {
+ print_char(&mut buf, border.right.unwrap_or(' '), border_color.right);
+ }
+
+ buf.push('\n');
+
+ line_index += 1;
+ }
+ }
+
+ for _ in 0..pad.bottom.size {
+ let mut border = border;
+ if vertical_splits.first() == Some(&line_index) {
+ border.left = Some(right_intersection);
+ vertical_splits = &vertical_splits[1..];
+ }
+
+ print_line(
+ &mut buf,
+ border,
+ border_color,
+ pad_color.bottom,
+ pad.bottom.fill,
+ ctx.size.width,
+ );
+
+ line_index += 1;
+ }
+
+ for _ in 0..bottom {
+ let mut border = border;
+ if vertical_splits.first() == Some(&line_index) {
+ border.left = Some(right_intersection);
+ vertical_splits = &vertical_splits[1..];
+ }
+
+ print_line(&mut buf, border, border_color, None, ' ', ctx.size.width);
+ line_index += 1;
+ }
+
+ print_bottom_line(&mut buf, border, border_color, ctx.size.width);
+
+ let _ = buf.remove(buf.len() - 1);
+
+ buf
+ }
+
+ fn print_chars(buf: &mut String, c: char, color: Option<StaticColor>, width: usize) {
+ match color {
+ Some(color) => {
+ buf.push_str(color.get_prefix());
+ buf.extend(repeat(c).take(width));
+ buf.push_str(color.get_suffix());
+ }
+ None => buf.extend(repeat(c).take(width)),
+ }
+ }
+
+ fn print_char(buf: &mut String, c: char, color: Option<StaticColor>) {
+ match color {
+ Some(color) => {
+ buf.push_str(color.get_prefix());
+ buf.push(c);
+ buf.push_str(color.get_suffix());
+ }
+ None => buf.push(c),
+ }
+ }
+
+ fn print_line(
+ buf: &mut String,
+ border: Border<char>,
+ border_color: Border<StaticColor>,
+ color: Option<StaticColor>,
+ c: char,
+ width: usize,
+ ) {
+ if border.has_left() {
+ let c = border.left.unwrap_or(' ');
+ print_char(buf, c, border_color.left);
+ }
+
+ print_chars(buf, c, color, width);
+
+ if border.has_right() {
+ let c = border.right.unwrap_or(' ');
+ print_char(buf, c, border_color.right);
+ }
+
+ buf.push('\n');
+ }
+
+ fn print_top_line(
+ buf: &mut String,
+ border: Border<char>,
+ color: Border<StaticColor>,
+ splits: &[usize],
+ split_char: char,
+ width: usize,
+ ) {
+ if !border.has_top() {
+ return;
+ }
+
+ let mut used_color: Option<StaticColor> = None;
+
+ if border.has_left() {
+ if let Some(color) = color.left_top_corner {
+ used_color = Some(color);
+ buf.push_str(color.get_prefix());
+ }
+
+ let c = border.left_top_corner.unwrap_or(' ');
+ buf.push(c);
+ }
+
+ if let Some(color) = color.top {
+ match used_color {
+ Some(used) => {
+ if used != color {
+ buf.push_str(used.get_suffix());
+ buf.push_str(color.get_prefix());
+ }
+ }
+ None => {
+ buf.push_str(color.get_prefix());
+ used_color = Some(color);
+ }
+ }
+ }
+
+ let c = border.top.unwrap_or(' ');
+ if splits.is_empty() {
+ buf.extend(repeat(c).take(width));
+ } else {
+ let mut splits = splits;
+ for i in 0..width {
+ if splits.first() == Some(&i) {
+ buf.push(split_char);
+ splits = &splits[1..];
+ } else {
+ buf.push(c);
+ }
+ }
+ }
+
+ if border.has_right() {
+ if let Some(color) = color.right_top_corner {
+ match used_color {
+ Some(used) => {
+ if used != color {
+ buf.push_str(used.get_suffix());
+ buf.push_str(color.get_prefix());
+ }
+ }
+ None => {
+ buf.push_str(color.get_prefix());
+ used_color = Some(color);
+ }
+ }
+ }
+
+ let c = border.right_top_corner.unwrap_or(' ');
+ buf.push(c);
+ }
+
+ if let Some(used) = used_color {
+ buf.push_str(used.get_suffix());
+ }
+
+ buf.push('\n');
+ }
+
+ fn print_bottom_line(
+ buf: &mut String,
+ border: Border<char>,
+ color: Border<StaticColor>,
+ width: usize,
+ ) {
+ if !border.has_bottom() {
+ return;
+ }
+
+ let mut used_color: Option<StaticColor> = None;
+
+ if border.has_left() {
+ if let Some(color) = color.left_bottom_corner {
+ used_color = Some(color);
+ buf.push_str(color.get_prefix());
+ }
+
+ let c = border.left_bottom_corner.unwrap_or(' ');
+ buf.push(c);
+ }
+
+ if let Some(color) = color.bottom {
+ match used_color {
+ Some(used) => {
+ if used != color {
+ buf.push_str(used.get_suffix());
+ buf.push_str(color.get_prefix());
+ }
+ }
+ None => {
+ buf.push_str(color.get_prefix());
+ used_color = Some(color);
+ }
+ }
+ }
+
+ let c = border.bottom.unwrap_or(' ');
+ buf.extend(repeat(c).take(width));
+
+ if border.has_right() {
+ if let Some(color) = color.right_bottom_corner {
+ match used_color {
+ Some(used) => {
+ if used != color {
+ buf.push_str(used.get_suffix());
+ buf.push_str(color.get_prefix());
+ }
+ }
+ None => {
+ buf.push_str(color.get_prefix());
+ used_color = Some(color);
+ }
+ }
+ }
+
+ let c = border.right_bottom_corner.unwrap_or(' ');
+ buf.push(c);
+ }
+
+ if let Some(used) = used_color {
+ buf.push_str(used.get_suffix());
+ }
+
+ buf.push('\n');
+ }
+
+ fn create_border<T>(borders: Borders<T>) -> Border<T> {
+ Border {
+ top: borders.top,
+ bottom: borders.bottom,
+ left: borders.left,
+ right: borders.right,
+ left_top_corner: borders.top_left,
+ left_bottom_corner: borders.bottom_left,
+ right_top_corner: borders.top_right,
+ right_bottom_corner: borders.bottom_right,
+ }
+ }
+
+ fn config_borders(borders: &mut Borders<char>, ctx: &PrintContext) {
+ // set top_left
+ {
+ if ctx.kv && ctx.kv_is_first {
+ borders.top_left = borders.top_intersection;
+ }
+
+ if ctx.kv && !ctx.kv_is_first {
+ borders.top_left = borders.intersection;
+ }
+
+ if ctx.kv && ctx.list && !ctx.list_is_first {
+ borders.top_left = borders.left_intersection;
+ }
+
+ if ctx.is_first_col && !ctx.is_first_row {
+ borders.top_left = borders.left_intersection;
+ }
+
+ if ctx.lean_top {
+ borders.top_left = borders.top_intersection;
+ }
+
+ if ctx.top_left {
+ borders.top_left = borders.left_intersection;
+ }
+
+ if ctx.top_intersection {
+ borders.top_left = borders.intersection;
+ }
+ }
+
+ if ctx.is_last_col && !ctx.is_first_row {
+ borders.top_right = borders.right_intersection;
+ }
+
+ if !ctx.is_first_col && ctx.is_last_row {
+ borders.bottom_left = borders.bottom_intersection;
+ }
+
+ if !ctx.is_last_row || ctx.no_bottom {
+ cfg_no_bottom_borders(borders);
+ }
+
+ if ctx.no_right {
+ cfg_no_right_borders(borders);
+ }
+ }
+
+ struct ConfigCell(PrintContext);
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for ConfigCell {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ {
+ // we set a horizontal lines to borders to not complicate logic with cleaning it
+
+ let mut borders = *cfg.get_borders();
+ if let Some(line) = cfg.get_horizontal_line(0) {
+ borders.top = line.main;
+ borders.top_left = line.left;
+ borders.top_right = line.right;
+ }
+
+ if let Some(line) = cfg.get_horizontal_line(1) {
+ borders.bottom = line.main;
+ borders.bottom_left = line.left;
+ borders.bottom_right = line.right;
+ }
+
+ cfg.clear_theme();
+ cfg.set_borders(borders);
+ }
+
+ let mut ctx = self.0;
+
+ let has_vertical = cfg.get_borders().has_left();
+ if !ctx.intersections_horizontal.is_empty() && has_vertical {
+ let mut splits = short_splits(&mut ctx.intersections_horizontal, ctx.size.width);
+ squash_splits(&mut splits);
+
+ let c = cfg.get_borders().bottom_intersection.unwrap_or(' ');
+ cfg_set_top_chars(cfg, &splits, c)
+ }
+
+ let has_horizontal = cfg.get_borders().has_top();
+ if !ctx.intersections_vertical.is_empty() && has_horizontal {
+ let mut splits = short_splits(&mut ctx.intersections_vertical, ctx.size.height);
+ squash_splits(&mut splits);
+
+ let c = cfg.get_borders().right_intersection.unwrap_or(' ');
+ cfg_set_left_chars(cfg, &splits, c)
+ }
+
+ let mut borders = *cfg.get_borders();
+
+ // set top_left
+ {
+ if ctx.kv && ctx.kv_is_first {
+ borders.top_left = borders.top_intersection;
+ }
+
+ if ctx.kv && !ctx.kv_is_first {
+ borders.top_left = borders.intersection;
+ }
+
+ if ctx.kv && ctx.list && !ctx.list_is_first {
+ borders.top_left = borders.left_intersection;
+ }
+
+ if ctx.is_first_col && !ctx.is_first_row {
+ borders.top_left = borders.left_intersection;
+ }
+
+ if ctx.lean_top {
+ borders.top_left = borders.top_intersection;
+ }
+
+ if ctx.top_left {
+ borders.top_left = borders.left_intersection;
+ }
+
+ if ctx.top_intersection {
+ borders.top_left = borders.intersection;
+ }
+ }
+
+ if ctx.is_last_col && !ctx.is_first_row {
+ borders.top_right = borders.right_intersection;
+ }
+
+ if !ctx.is_first_col && ctx.is_last_row {
+ borders.bottom_left = borders.bottom_intersection;
+ }
+
+ if !ctx.is_last_row || ctx.no_bottom {
+ cfg_no_bottom_borders(&mut borders);
+ }
+
+ if ctx.no_right {
+ cfg_no_right_borders(&mut borders);
+ }
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ fn cfg_no_bottom_borders(borders: &mut Borders<char>) {
+ borders.bottom = None;
+ borders.bottom_intersection = None;
+ borders.bottom_left = None;
+ borders.bottom_right = None;
+ borders.horizontal = None;
+ }
+
+ fn cfg_no_right_borders(borders: &mut Borders<char>) {
+ borders.right = None;
+ borders.right_intersection = None;
+ borders.top_right = None;
+ borders.bottom_right = None;
+ borders.vertical = None;
+ }
+
+ fn cfg_set_top_chars(cfg: &mut ColoredConfig, list: &[usize], c: char) {
+ for &split in list {
+ let offset = split;
+ cfg.set_horizontal_char((0, 0), c, Offset::Begin(offset));
+ }
+ }
+
+ fn cfg_set_left_chars(cfg: &mut ColoredConfig, list: &[usize], c: char) {
+ for &offset in list {
+ cfg.set_vertical_char((0, 0), c, Offset::Begin(offset));
+ }
+ }
+
+ struct NoTopBorders;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for NoTopBorders {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.top = None;
+ borders.top_intersection = None;
+ borders.top_left = None;
+ borders.top_right = None;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct NoBottomBorders;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for NoBottomBorders {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.bottom = None;
+ borders.bottom_intersection = None;
+ borders.bottom_left = None;
+ borders.bottom_right = None;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct NoRightBorders;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for NoRightBorders {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.top_right = None;
+ borders.bottom_right = None;
+ borders.right = None;
+ borders.right_intersection = None;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct NoLeftBorders;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for NoLeftBorders {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.top_left = None;
+ borders.bottom_left = None;
+ borders.left = None;
+ borders.left_intersection = None;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct TopLeftChangeTopIntersection;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for TopLeftChangeTopIntersection {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.top_left = borders.top_intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct TopLeftChangeIntersection;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for TopLeftChangeIntersection {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.top_left = borders.intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct TopLeftChangeToLeft;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for TopLeftChangeToLeft {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.top_left = borders.left_intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct TopRightChangeToRight;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for TopRightChangeToRight {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.top_right = borders.right_intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct BottomLeftChangeSplit;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for BottomLeftChangeSplit {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.bottom_left = borders.left_intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct BottomLeftChangeSplitToIntersection;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for BottomLeftChangeSplitToIntersection {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.bottom_left = borders.intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct BottomRightChangeToRight;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for BottomRightChangeToRight {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.bottom_right = borders.right_intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct BottomLeftChangeToBottomIntersection;
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for BottomLeftChangeToBottomIntersection {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ let mut borders = *cfg.get_borders();
+ borders.bottom_left = borders.bottom_intersection;
+
+ cfg.set_borders(borders);
+ }
+ }
+
+ struct SetBottomChars<'a>(&'a [usize], char);
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for SetBottomChars<'_>
+ where
+ R: Records,
+ for<'a> &'a R: Records,
+ for<'a> D: Dimension + Estimate<&'a R, ColoredConfig>,
+ {
+ fn change(self, records: &mut R, cfg: &mut ColoredConfig, dims: &mut D) {
+ dims.estimate(&*records, cfg);
+
+ let table_width = (0..records.count_columns())
+ .map(|col| dims.get_width(col))
+ .sum::<usize>()
+ + cfg.count_vertical(records.count_columns());
+ let mut current_width = 0;
+
+ for pos in self.0 {
+ current_width += pos;
+ if current_width > table_width {
+ break;
+ }
+
+ let split_char = self.1;
+ cfg.set_horizontal_char((1, 0), split_char, Offset::Begin(current_width));
+
+ current_width += 1;
+ }
+ }
+ }
+
+ struct SetTopChars<'a>(&'a [usize], char);
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for SetTopChars<'_> {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ for &split in self.0 {
+ let offset = split;
+ cfg.set_horizontal_char((0, 0), self.1, Offset::Begin(offset));
+ }
+ }
+ }
+
+ struct SetLeftChars<'a>(&'a [usize], char);
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for SetLeftChars<'_> {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ for &offset in self.0 {
+ cfg.set_vertical_char((0, 0), self.1, Offset::Begin(offset));
+ }
+ }
+ }
+
+ struct GetTopIntersection(char);
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for &mut GetTopIntersection {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ self.0 = cfg.get_borders().top_intersection.unwrap_or(' ');
+ }
+ }
+
+ struct GetBottomIntersection(char);
+
+ impl<R, D> TableOption<R, D, ColoredConfig> for &mut GetBottomIntersection {
+ fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
+ self.0 = cfg.get_borders().bottom_intersection.unwrap_or(' ');
+ }
+ }
+
+ #[derive(Debug, Default)]
+ struct Dimensions {
+ all: HashMap<usize, Dim>,
+ arrays: HashMap<usize, ArrayDimensions>,
+ }
+
+ #[derive(Debug, Default, Clone, Copy)]
+ struct Dim {
+ width: usize,
+ height: usize,
+ }
+
+ impl Dim {
+ fn new(width: usize, height: usize) -> Self {
+ Self { width, height }
+ }
+ }
+
+ #[derive(Debug, Default)]
+ struct ArrayDimensions {
+ max: Dim,
+ index: HashMap<usize, usize>,
+ }
+
+ fn collect_table_dimensions(val: &TableValue, cfg: &CompactMultilineConfig) -> Dimensions {
+ let mut buf = Dimensions::default();
+ let (dim, _) = __collect_table_dims(&mut buf, val, cfg, 0);
+ let _ = buf.all.insert(0, dim);
+ buf
+ }
+
+ fn __collect_table_dims(
+ buf: &mut Dimensions,
+ val: &TableValue,
+ cfg: &CompactMultilineConfig,
+ pos: usize,
+ ) -> (Dim, usize) {
+ match val {
+ TableValue::Cell(text) => (str_dimension(text, cfg), 0),
+ TableValue::Row(list) => {
+ if list.is_empty() {
+ return (empty_dimension(cfg), 0);
+ }
+
+ let mut index = ArrayDimensions {
+ max: Dim::default(),
+ index: HashMap::with_capacity(list.len()),
+ };
+
+ let mut total_width = 0;
+
+ let mut count_elements = list.len();
+ let mut val_pos = pos + 1;
+ for (i, value) in list.iter().enumerate() {
+ let (dim, elements) = __collect_table_dims(buf, value, cfg, val_pos);
+ count_elements += elements;
+
+ total_width += dim.width;
+
+ index.max.width = max(index.max.width, dim.width);
+ index.max.height = max(index.max.height, dim.height);
+
+ let _ = buf.all.insert(val_pos, dim);
+
+ let _ = index.index.insert(i, val_pos);
+
+ val_pos += 1 + elements;
+ }
+
+ let max_height = index.max.height;
+
+ let _ = buf.arrays.insert(pos, index);
+
+ let has_vertical = cfg.get_borders().has_left();
+ total_width += has_vertical as usize * (list.len() - 1);
+
+ (Dim::new(total_width, max_height), count_elements)
+ }
+ TableValue::Column(list) => {
+ if list.is_empty() {
+ return (empty_dimension(cfg), 0);
+ }
+
+ let mut index = ArrayDimensions {
+ max: Dim::default(),
+ index: HashMap::with_capacity(list.len()),
+ };
+
+ let mut total_height = 0;
+
+ let mut count_elements = list.len();
+ let mut val_pos = pos + 1;
+ for (i, value) in list.iter().enumerate() {
+ let (dim, elements) = __collect_table_dims(buf, value, cfg, val_pos);
+ count_elements += elements;
+
+ total_height += dim.height;
+
+ index.max.width = max(index.max.width, dim.width);
+ index.max.height = max(index.max.height, dim.height);
+
+ let _ = buf.all.insert(val_pos, dim);
+
+ let _ = index.index.insert(i, val_pos);
+
+ val_pos += 1 + elements;
+ }
+
+ let max_width = index.max.width;
+
+ let _ = buf.arrays.insert(pos, index);
+
+ let has_horizontal = cfg.get_borders().has_top();
+ total_height += has_horizontal as usize * (list.len() - 1);
+
+ (Dim::new(max_width, total_height), count_elements)
+ }
+ }
+ }
+
+ fn empty_dimension(cfg: &CompactMultilineConfig) -> Dim {
+ Dim::new(get_padding_horizontal(cfg), 1 + get_padding_vertical(cfg))
+ }
+
+ fn str_dimension(text: &str, cfg: &CompactMultilineConfig) -> Dim {
+ let (count_lines, width) = string_dimension(text);
+ let w = width + get_padding_horizontal(cfg);
+ let h = count_lines + get_padding_vertical(cfg);
+ Dim::new(w, h)
+ }
+
+ fn get_padding_horizontal(cfg: &CompactMultilineConfig) -> usize {
+ let pad = cfg.get_padding();
+ pad.left.size + pad.right.size
+ }
+
+ fn get_padding_vertical(cfg: &CompactMultilineConfig) -> usize {
+ let pad = cfg.get_padding();
+ pad.top.size + pad.bottom.size
+ }
+
+ fn split_value(value: usize, by: usize) -> (usize, usize) {
+ let val = value / by;
+ let rest = value - val * by;
+ (val, rest)
+ }
+
+ fn indent_vertical(al: AlignmentVertical, available: usize, real: usize) -> (usize, usize) {
+ let top = indent_top(al, available, real);
+ let bottom = available - real - top;
+ (top, bottom)
+ }
+
+ fn indent_horizontal(al: AlignmentHorizontal, available: usize, real: usize) -> (usize, usize) {
+ let top = indent_left(al, available, real);
+ let right = available - real - top;
+ (top, right)
+ }
+
+ fn indent_top(al: AlignmentVertical, available: usize, real: usize) -> usize {
+ match al {
+ AlignmentVertical::Top => 0,
+ AlignmentVertical::Bottom => available - real,
+ AlignmentVertical::Center => (available - real) / 2,
+ }
+ }
+
+ fn indent_left(al: AlignmentHorizontal, available: usize, real: usize) -> usize {
+ match al {
+ AlignmentHorizontal::Left => 0,
+ AlignmentHorizontal::Right => available - real,
+ AlignmentHorizontal::Center => (available - real) / 2,
+ }
+ }
+
+ fn short_splits(splits: &mut Vec<usize>, width: usize) -> Vec<usize> {
+ if splits.is_empty() {
+ return Vec::new();
+ }
+
+ let mut out = Vec::new();
+ let mut pos = 0;
+ for &split in splits.iter() {
+ if pos + split >= width {
+ break;
+ }
+
+ pos += split;
+ out.push(pos);
+ }
+
+ let _ = splits.drain(..out.len());
+
+ if !splits.is_empty() && pos <= width {
+ let rest = width - pos;
+ splits[0] -= rest;
+ }
+
+ out
+ }
+
+ fn short_splits3(splits: &mut Vec<usize>, width: usize) -> (bool, Vec<usize>) {
+ if splits.is_empty() {
+ return (false, Vec::new());
+ }
+
+ let mut out = Vec::new();
+ let mut pos = 0;
+ for &split in splits.iter() {
+ if pos + split >= width {
+ break;
+ }
+
+ pos += split + 1;
+ out.push(split);
+ }
+
+ let _ = splits.drain(..out.len());
+
+ if splits.is_empty() {
+ return (false, out);
+ }
+
+ if pos <= width {
+ splits[0] -= width - pos;
+ if splits[0] > 0 {
+ splits[0] -= 1;
+ } else {
+ let _ = splits.remove(0);
+ return (true, out);
+ }
+ }
+
+ (false, out)
+ }
+
+ fn squash_splits(splits: &mut [usize]) {
+ splits.iter_mut().enumerate().for_each(|(i, s)| *s += i);
+ }
+
+ fn set_margin(table: &str, margin: Sides<Indent>, color: Sides<Option<StaticColor>>) -> String {
+ if table.is_empty() {
+ return String::new();
+ }
+
+ let mut buf = String::new();
+ let width = string_width_multiline(table);
+ let top_color = color.top;
+ let bottom_color = color.bottom;
+ let left_color = color.left;
+ let right_color = color.right;
+ for _ in 0..margin.top.size {
+ print_chars(&mut buf, margin.left.fill, left_color, margin.left.size);
+ print_chars(&mut buf, margin.top.fill, top_color, width);
+ print_chars(&mut buf, margin.right.fill, right_color, margin.right.size);
+ buf.push('\n');
+ }
+
+ for line in get_lines(table) {
+ print_chars(&mut buf, margin.left.fill, left_color, margin.left.size);
+ buf.push_str(&line);
+ print_chars(&mut buf, margin.right.fill, right_color, margin.right.size);
+ buf.push('\n');
+ }
+
+ for _ in 0..margin.bottom.size {
+ print_chars(&mut buf, margin.left.fill, left_color, margin.left.size);
+ print_chars(&mut buf, margin.bottom.fill, bottom_color, width);
+ print_chars(&mut buf, margin.right.fill, right_color, margin.right.size);
+ buf.push('\n');
+ }
+
+ let _ = buf.remove(buf.len() - 1);
+
+ buf
+ }
+
+ fn convert_border_colors(pad_color: Sides<StaticColor>) -> Sides<Option<StaticColor>> {
+ Sides::new(
+ (!pad_color.left.is_empty()).then(|| pad_color.left),
+ (!pad_color.right.is_empty()).then(|| pad_color.right),
+ (!pad_color.top.is_empty()).then(|| pad_color.top),
+ (!pad_color.bottom.is_empty()).then(|| pad_color.bottom),
+ )
+ }
+}
diff --git a/vendor/tabled/src/tables/util/mod.rs b/vendor/tabled/src/tables/util/mod.rs
new file mode 100644
index 000000000..ea5453b8f
--- /dev/null
+++ b/vendor/tabled/src/tables/util/mod.rs
@@ -0,0 +1,2 @@
+#[cfg(feature = "std")]
+pub(crate) mod utf8_writer;
diff --git a/vendor/tabled/src/tables/util/utf8_writer.rs b/vendor/tabled/src/tables/util/utf8_writer.rs
new file mode 100644
index 000000000..d4fc515df
--- /dev/null
+++ b/vendor/tabled/src/tables/util/utf8_writer.rs
@@ -0,0 +1,29 @@
+use std::fmt;
+use std::io;
+
+pub(crate) struct UTF8Writer<W>(W);
+
+impl<W> UTF8Writer<W> {
+ pub(crate) fn new(writer: W) -> Self {
+ Self(writer)
+ }
+}
+
+impl<W> fmt::Write for UTF8Writer<W>
+where
+ W: io::Write,
+{
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ let mut buf = s.as_bytes();
+ loop {
+ let n = self.0.write(buf).map_err(|_| fmt::Error)?;
+ if n == buf.len() {
+ break;
+ }
+
+ buf = &buf[n..];
+ }
+
+ Ok(())
+ }
+}
diff --git a/vendor/tabled/tests/core/builder_test.rs b/vendor/tabled/tests/core/builder_test.rs
new file mode 100644
index 000000000..7751df12d
--- /dev/null
+++ b/vendor/tabled/tests/core/builder_test.rs
@@ -0,0 +1,748 @@
+#![cfg(feature = "std")]
+
+use std::iter::FromIterator;
+
+use tabled::builder::Builder;
+
+use testing_table::test_table;
+
+test_table!(
+ push_record,
+ {
+ let mut b = Builder::default();
+ b.push_record(["1", "2", "3"]);
+ b.push_record(["a", "b", "c"]);
+ b.push_record(["d", "e", "f"]);
+ b.build()
+ },
+ "+---+---+---+"
+ "| 1 | 2 | 3 |"
+ "+---+---+---+"
+ "| a | b | c |"
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+);
+
+test_table!(
+ set_header,
+ {
+ let mut b = Builder::default();
+ b.set_header(["1", "2", "3"]);
+ b.push_record(["a", "b", "c"]);
+ b.push_record(["d", "e", "f"]);
+ b.build()
+ },
+ "+---+---+---+"
+ "| 1 | 2 | 3 |"
+ "+---+---+---+"
+ "| a | b | c |"
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+);
+
+test_table!(
+ header_remove_0,
+ {
+ let mut b = Builder::default();
+ b.set_header(["1", "2", "3"]);
+ b.push_record(["a", "b", "c"]);
+ b.push_record(["d", "e", "f"]);
+ b.remove_header();
+ b.build()
+ },
+ "+---+---+---+"
+ "| a | b | c |"
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+);
+
+test_table!(
+ header_remove_1,
+ {
+ let mut b = Builder::default();
+ b.set_header(["1", "2", "3", "4", "5"]);
+ b.push_record(["a", "b", "c"]);
+ b.push_record(["d", "e", "f"]);
+ b.remove_header();
+ b.build()
+ },
+ "+---+---+---+"
+ "| a | b | c |"
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+);
+
+test_table!(
+ from_iter,
+ Builder::from_iter([["n", "name"], ["0", "Dmitriy"], ["1", "Vladislav"]]).build(),
+ "+---+-----------+"
+ "| n | name |"
+ "+---+-----------+"
+ "| 0 | Dmitriy |"
+ "+---+-----------+"
+ "| 1 | Vladislav |"
+ "+---+-----------+"
+);
+
+test_table!(
+ used_with_different_number_of_columns_0,
+ {
+ let mut b = Builder::default();
+ b.set_header(["1", "2"]);
+ b.push_record(["a", "b", "c"]);
+ b.push_record(["d"]);
+ b.build()
+ },
+ "+---+---+---+"
+ "| 1 | 2 | |"
+ "+---+---+---+"
+ "| a | b | c |"
+ "+---+---+---+"
+ "| d | | |"
+ "+---+---+---+"
+);
+
+test_table!(
+ used_with_different_number_of_columns_1,
+ {
+ let mut b = Builder::default();
+ b.set_header(["1", "2", "3"]);
+ b.push_record(["a", "b"]);
+ b.push_record(["d"]);
+ b.build()
+ },
+ "+---+---+---+"
+ "| 1 | 2 | 3 |"
+ "+---+---+---+"
+ "| a | b | |"
+ "+---+---+---+"
+ "| d | | |"
+ "+---+---+---+"
+);
+
+test_table!(
+ used_with_different_number_of_columns_2,
+ {
+ let mut b = Builder::default();
+ b.set_header(["1"]);
+ b.push_record(["a", "b"]);
+ b.push_record(["d", "e", "f"]);
+ b.build()
+ },
+ "+---+---+---+"
+ "| 1 | | |"
+ "+---+---+---+"
+ "| a | b | |"
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+);
+
+test_table!(
+ with_default_cell_0,
+ {
+ let mut b = Builder::default();
+ b.set_header(["1", "2"]).set_default_text("NaN").push_record(["a", "b", "c"]).push_record(["d"]);
+ b.build()
+ },
+ "+---+-----+-----+"
+ "| 1 | 2 | NaN |"
+ "+---+-----+-----+"
+ "| a | b | c |"
+ "+---+-----+-----+"
+ "| d | NaN | NaN |"
+ "+---+-----+-----+"
+);
+
+test_table!(
+ with_default_cell_1,
+ {
+ let mut b = Builder::default();
+ b.set_header(["1"]).set_default_text("NaN").push_record(["a", "b"]).push_record(["d", "e", "f"]);
+ b.build()
+ },
+ "+---+-----+-----+"
+ "| 1 | NaN | NaN |"
+ "+---+-----+-----+"
+ "| a | b | NaN |"
+ "+---+-----+-----+"
+ "| d | e | f |"
+ "+---+-----+-----+"
+);
+
+test_table!(
+ extend,
+ {
+ let mut b = Builder::default();
+ b.extend(["1", "2", "3"]);
+ b.extend(["a", "b", "c"]);
+ b.extend(["d", "e", "f"]);
+ b.build()
+ },
+ "+---+---+---+"
+ "| 1 | 2 | 3 |"
+ "+---+---+---+"
+ "| a | b | c |"
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+);
+
+test_table!(
+ from_vector_0,
+ Builder::from_iter(vec![
+ vec!["1".to_string(), "2".to_string(), "3".to_string()],
+ vec!["a".to_string(), "b".to_string(), "c".to_string()],
+ vec!["d".to_string(), "e".to_string(), "f".to_string()],
+ ])
+ .build(),
+ "+---+---+---+"
+ "| 1 | 2 | 3 |"
+ "+---+---+---+"
+ "| a | b | c |"
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+);
+
+test_table!(
+ from_with_empty_lines_0,
+ Builder::from_iter(vec![
+ vec!["1".to_string(), "2".to_string(), "3".to_string()],
+ vec![],
+ vec![],
+ vec!["d".to_string(), "e".to_string(), "f".to_string()],
+ ])
+ .build(),
+ "+---+---+---+"
+ "| 1 | 2 | 3 |"
+ "+---+---+---+"
+ "| | | |"
+ "+---+---+---+"
+ "| | | |"
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+);
+
+test_table!(
+ from_with_empty_lines_1,
+ Builder::from_iter(vec![
+ vec!["1".to_string(), "2".to_string(), "3".to_string()],
+ vec![],
+ vec!["d".to_string(), "e".to_string(), "f".to_string()],
+ ])
+ .build(),
+ "+---+---+---+"
+ "| 1 | 2 | 3 |"
+ "+---+---+---+"
+ "| | | |"
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+);
+
+test_table!(
+ from_with_empty_lines_2,
+ Builder::from_iter(vec![
+ vec![],
+ vec!["1".to_string(), "2".to_string(), "3".to_string()],
+ vec!["d".to_string(), "e".to_string(), "f".to_string()],
+ ])
+ .build(),
+ "+---+---+---+"
+ "| | | |"
+ "+---+---+---+"
+ "| 1 | 2 | 3 |"
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+);
+
+test_table!(
+ from_with_empty_lines_3,
+ Builder::from_iter(vec![
+ vec!["1".to_string(), "2".to_string(), "3".to_string()],
+ vec!["d".to_string(), "e".to_string(), "f".to_string()],
+ vec![],
+ ])
+ .build(),
+ "+---+---+---+"
+ "| 1 | 2 | 3 |"
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+ "| | | |"
+ "+---+---+---+"
+);
+
+test_table!(
+ clean_0,
+ clean(Builder::from_iter([["1", "2", "3"], ["a", "b", "c"], ["d", "e", "f"]])),
+ "+---+---+---+"
+ "| 1 | 2 | 3 |"
+ "+---+---+---+"
+ "| a | b | c |"
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+);
+
+test_table!(
+ clean_1,
+ clean(Builder::from_iter([["", "2", "3"], ["", "b", "c"], ["", "e", "f"]])),
+ "+---+---+"
+ "| 2 | 3 |"
+ "+---+---+"
+ "| b | c |"
+ "+---+---+"
+ "| e | f |"
+ "+---+---+"
+);
+
+test_table!(
+ clean_2,
+ clean(Builder::from_iter([["1", "", "3"], ["a", "", "c"], ["d", "", "f"]])),
+ "+---+---+"
+ "| 1 | 3 |"
+ "+---+---+"
+ "| a | c |"
+ "+---+---+"
+ "| d | f |"
+ "+---+---+"
+);
+
+test_table!(
+ clean_3,
+ clean(Builder::from_iter([["1", "2", ""], ["a", "b", ""], ["d", "e", ""]])),
+ "+---+---+"
+ "| 1 | 2 |"
+ "+---+---+"
+ "| a | b |"
+ "+---+---+"
+ "| d | e |"
+ "+---+---+"
+);
+
+test_table!(
+ clean_4,
+ clean(Builder::from_iter([["", "", "3"], ["", "", "c"], ["", "", "f"]])),
+ "+---+"
+ "| 3 |"
+ "+---+"
+ "| c |"
+ "+---+"
+ "| f |"
+ "+---+"
+);
+
+test_table!(
+ clean_5,
+ clean(Builder::from_iter([["1", "", ""], ["a", "", ""], ["d", "", ""]])),
+ "+---+"
+ "| 1 |"
+ "+---+"
+ "| a |"
+ "+---+"
+ "| d |"
+ "+---+"
+);
+
+test_table!(
+ clean_6,
+ clean(Builder::from_iter([["", "2", ""], ["", "b", ""], ["", "e", ""]])),
+ "+---+"
+ "| 2 |"
+ "+---+"
+ "| b |"
+ "+---+"
+ "| e |"
+ "+---+"
+);
+
+test_table!(
+ clean_7,
+ clean(Builder::from_iter([["", "", ""], ["a", "b", "c"], ["d", "e", "f"]])),
+ "+---+---+---+"
+ "| a | b | c |"
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+);
+
+test_table!(
+ clean_8,
+ clean(Builder::from_iter([["1", "2", "3"], ["", "", ""], ["d", "e", "f"]])),
+ "+---+---+---+"
+ "| 1 | 2 | 3 |"
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+);
+
+test_table!(
+ clean_9,
+ clean(Builder::from_iter([["1", "2", "3"], ["a", "b", "c"], ["", "", ""]])),
+ "+---+---+---+"
+ "| 1 | 2 | 3 |"
+ "+---+---+---+"
+ "| a | b | c |"
+ "+---+---+---+"
+);
+
+test_table!(
+ clean_10,
+ clean(Builder::from_iter([["", "", ""], ["", "", ""], ["d", "e", "f"]])),
+ "+---+---+---+"
+ "| d | e | f |"
+ "+---+---+---+"
+);
+
+test_table!(
+ clean_11,
+ clean(Builder::from_iter([["1", "2", "3"], ["", "", ""], ["", "", ""]])),
+ "+---+---+---+"
+ "| 1 | 2 | 3 |"
+ "+---+---+---+"
+);
+
+test_table!(
+ clean_12,
+ clean(Builder::from_iter([["", "", ""], ["a", "b", "c"], ["", "", ""]])),
+ "+---+---+---+"
+ "| a | b | c |"
+ "+---+---+---+"
+);
+
+test_table!(
+ clean_13,
+ clean(Builder::from_iter([["1", "", "3"], ["", "", ""], ["d", "", "f"]])),
+ "+---+---+"
+ "| 1 | 3 |"
+ "+---+---+"
+ "| d | f |"
+ "+---+---+"
+);
+
+test_table!(
+ clean_with_columns_0,
+ clean(Builder::from_iter([["1", "2", "3"], ["a", "b", "c"], ["d", "e", "f"]]).set_header(["col1", "col2", "col3"]).clone()),
+ "+------+------+------+"
+ "| col1 | col2 | col3 |"
+ "+------+------+------+"
+ "| 1 | 2 | 3 |"
+ "+------+------+------+"
+ "| a | b | c |"
+ "+------+------+------+"
+ "| d | e | f |"
+ "+------+------+------+"
+);
+
+test_table!(
+ clean_with_columns_1,
+ clean(Builder::from_iter([["", "2", "3"], ["", "b", "c"], ["", "e", "f"]]).set_header(["col1", "col2", "col3"]).clone()),
+ "+------+------+"
+ "| col2 | col3 |"
+ "+------+------+"
+ "| 2 | 3 |"
+ "+------+------+"
+ "| b | c |"
+ "+------+------+"
+ "| e | f |"
+ "+------+------+"
+);
+
+test_table!(
+ clean_with_columns_2,
+ clean(Builder::from_iter([["1", "", "3"], ["a", "", "c"], ["d", "", "f"]]).set_header(["col1", "col2", "col3"]).clone()),
+ "+------+------+"
+ "| col1 | col3 |"
+ "+------+------+"
+ "| 1 | 3 |"
+ "+------+------+"
+ "| a | c |"
+ "+------+------+"
+ "| d | f |"
+ "+------+------+"
+);
+
+test_table!(
+ clean_with_columns_3,
+ clean(Builder::from_iter([["1", "2", ""], ["a", "b", ""], ["d", "e", ""]]).set_header(["col1", "col2", "col3"]).clone()),
+ "+------+------+"
+ "| col1 | col2 |"
+ "+------+------+"
+ "| 1 | 2 |"
+ "+------+------+"
+ "| a | b |"
+ "+------+------+"
+ "| d | e |"
+ "+------+------+"
+);
+
+test_table!(
+ clean_with_columns_4,
+ clean(Builder::from_iter([["", "", "3"], ["", "", "c"], ["", "", "f"]]).set_header(["col1", "col2", "col3"]).clone()),
+ "+------+"
+ "| col3 |"
+ "+------+"
+ "| 3 |"
+ "+------+"
+ "| c |"
+ "+------+"
+ "| f |"
+ "+------+"
+);
+
+test_table!(
+ clean_with_columns_5,
+ clean(Builder::from_iter([["1", "", ""], ["a", "", ""], ["d", "", ""]]).set_header(["col1", "col2", "col3"]).clone()),
+ "+------+"
+ "| col1 |"
+ "+------+"
+ "| 1 |"
+ "+------+"
+ "| a |"
+ "+------+"
+ "| d |"
+ "+------+"
+);
+
+test_table!(
+ clean_with_columns_6,
+ clean(Builder::from_iter([["", "2", ""], ["", "b", ""], ["", "e", ""]]).set_header(["col1", "col2", "col3"]).clone()),
+ "+------+"
+ "| col2 |"
+ "+------+"
+ "| 2 |"
+ "+------+"
+ "| b |"
+ "+------+"
+ "| e |"
+ "+------+"
+);
+
+test_table!(
+ clean_with_columns_7,
+ clean(
+ Builder::from_iter([["", "", ""], ["", "", ""], ["", "", ""]])
+ .set_header(["col1", "col2", "col3"])
+ .clone()
+ ),
+ ""
+);
+
+test_table!(
+ clean_with_columns_8,
+ clean(Builder::from_iter([["", "", ""], ["a", "b", "c"], ["d", "e", "f"]]).set_header(["col1", "col2", "col3"]).clone()),
+ "+------+------+------+"
+ "| col1 | col2 | col3 |"
+ "+------+------+------+"
+ "| a | b | c |"
+ "+------+------+------+"
+ "| d | e | f |"
+ "+------+------+------+"
+);
+
+test_table!(
+ clean_with_columns_9,
+ clean(Builder::from_iter([["1", "2", "3"], ["", "", ""], ["d", "e", "f"]]).set_header(["col1", "col2", "col3"]).clone()),
+ "+------+------+------+"
+ "| col1 | col2 | col3 |"
+ "+------+------+------+"
+ "| 1 | 2 | 3 |"
+ "+------+------+------+"
+ "| d | e | f |"
+ "+------+------+------+"
+);
+
+test_table!(
+ clean_with_columns_10,
+ clean(Builder::from_iter([["1", "2", "3"], ["a", "b", "c"], ["", "", ""]]).set_header(["col1", "col2", "col3"]).clone()),
+ "+------+------+------+"
+ "| col1 | col2 | col3 |"
+ "+------+------+------+"
+ "| 1 | 2 | 3 |"
+ "+------+------+------+"
+ "| a | b | c |"
+ "+------+------+------+"
+);
+
+test_table!(
+ clean_with_columns_11,
+ clean(Builder::from_iter([["", "", ""], ["", "", ""], ["d", "e", "f"]]).set_header(["col1", "col2", "col3"]).clone()),
+ "+------+------+------+"
+ "| col1 | col2 | col3 |"
+ "+------+------+------+"
+ "| d | e | f |"
+ "+------+------+------+"
+);
+
+test_table!(
+ clean_with_columns_12,
+ clean(Builder::from_iter([["1", "2", "3"], ["", "", ""], ["", "", ""]]).set_header(["col1", "col2", "col3"]).clone()),
+ "+------+------+------+"
+ "| col1 | col2 | col3 |"
+ "+------+------+------+"
+ "| 1 | 2 | 3 |"
+ "+------+------+------+"
+);
+
+test_table!(
+ clean_with_columns_13,
+ clean(Builder::from_iter([["", "", ""], ["a", "b", "c"], ["", "", ""]]).set_header(["col1", "col2", "col3"]).clone()),
+ "+------+------+------+"
+ "| col1 | col2 | col3 |"
+ "+------+------+------+"
+ "| a | b | c |"
+ "+------+------+------+"
+);
+
+test_table!(
+ clean_with_columns_14,
+ clean(Builder::from_iter([["1", "", "3"], ["", "", ""], ["d", "", "f"]]).set_header(["col1", "col2", "col3"]).clone()),
+ "+------+------+"
+ "| col1 | col3 |"
+ "+------+------+"
+ "| 1 | 3 |"
+ "+------+------+"
+ "| d | f |"
+ "+------+------+"
+);
+
+test_table!(clean_empty_0, clean(Builder::from_iter([[""; 0]; 0])), "");
+
+test_table!(clean_empty_1, clean(Builder::from_iter([[""; 0]; 10])), "");
+
+test_table!(
+ index,
+ Builder::from_iter([["n", "name"], ["0", "Dmitriy"], ["1", "Vladislav"]]).index().build(),
+ "+---+---+-----------+"
+ "| | 0 | 1 |"
+ "+---+---+-----------+"
+ "| 0 | n | name |"
+ "+---+---+-----------+"
+ "| 1 | 0 | Dmitriy |"
+ "+---+---+-----------+"
+ "| 2 | 1 | Vladislav |"
+ "+---+---+-----------+"
+);
+
+test_table!(
+ index_set_name,
+ Builder::from_iter([["n", "name"], ["0", "Dmitriy"], ["1", "Vladislav"]])
+ .index()
+ .name(Some("A index name".into()))
+ .build(),
+ "+--------------+---+-----------+"
+ "| | 0 | 1 |"
+ "+--------------+---+-----------+"
+ "| A index name | | |"
+ "+--------------+---+-----------+"
+ "| 0 | n | name |"
+ "+--------------+---+-----------+"
+ "| 1 | 0 | Dmitriy |"
+ "+--------------+---+-----------+"
+ "| 2 | 1 | Vladislav |"
+ "+--------------+---+-----------+"
+);
+
+test_table!(
+ index_enumeration,
+ Builder::from_iter([["n", "name"], ["0", "Dmitriy"], ["1", "Vladislav"]])
+ .index()
+ .hide()
+ .build(),
+ "+---+-----------+"
+ "| 0 | 1 |"
+ "+---+-----------+"
+ "| n | name |"
+ "+---+-----------+"
+ "| 0 | Dmitriy |"
+ "+---+-----------+"
+ "| 1 | Vladislav |"
+ "+---+-----------+"
+);
+
+test_table!(
+ set_index,
+ Builder::from_iter([["n", "name"], ["0", "Dmitriy"], ["1", "Vladislav"]])
+ .index()
+ .column(1)
+ .build(),
+ "+-----------+---+"
+ "| | 0 |"
+ "+-----------+---+"
+ "| 1 | |"
+ "+-----------+---+"
+ "| name | n |"
+ "+-----------+---+"
+ "| Dmitriy | 0 |"
+ "+-----------+---+"
+ "| Vladislav | 1 |"
+ "+-----------+---+"
+);
+
+test_table!(
+ set_index_and_set_index_name_0,
+ Builder::from_iter([["n", "name"], ["0", "Dmitriy"], ["1", "Vladislav"]])
+ .index()
+ .column(1)
+ .name(Some("Hello".into()))
+ .build(),
+ "+-----------+---+"
+ "| | 0 |"
+ "+-----------+---+"
+ "| Hello | |"
+ "+-----------+---+"
+ "| name | n |"
+ "+-----------+---+"
+ "| Dmitriy | 0 |"
+ "+-----------+---+"
+ "| Vladislav | 1 |"
+ "+-----------+---+"
+);
+
+test_table!(
+ set_index_and_set_index_name_1,
+ Builder::from_iter([["n", "name"], ["0", "Dmitriy"], ["1", "Vladislav"]])
+ .index()
+ .column(1)
+ .name(None)
+ .build(),
+ "+-----------+---+"
+ "| | 0 |"
+ "+-----------+---+"
+ "| name | n |"
+ "+-----------+---+"
+ "| Dmitriy | 0 |"
+ "+-----------+---+"
+ "| Vladislav | 1 |"
+ "+-----------+---+"
+);
+
+test_table!(
+ index_transpose,
+ Builder::from_iter([["n", "name", "zz"], ["0", "Dmitriy", "123"], ["1", "Vladislav", "123"]])
+ .index()
+ .transpose()
+ .build(),
+ "+---+------+---------+-----------+"
+ "| | 0 | 1 | 2 |"
+ "+---+------+---------+-----------+"
+ "| 0 | n | 0 | 1 |"
+ "+---+------+---------+-----------+"
+ "| 1 | name | Dmitriy | Vladislav |"
+ "+---+------+---------+-----------+"
+ "| 2 | zz | 123 | 123 |"
+ "+---+------+---------+-----------+"
+);
+
+fn clean(mut b: Builder) -> String {
+ b.clean();
+ b.build().to_string()
+}
diff --git a/vendor/tabled/tests/core/compact_table.rs b/vendor/tabled/tests/core/compact_table.rs
new file mode 100644
index 000000000..1bdaad998
--- /dev/null
+++ b/vendor/tabled/tests/core/compact_table.rs
@@ -0,0 +1,119 @@
+#![cfg(feature = "std")]
+
+use tabled::{
+ grid::{
+ config::CompactConfig, dimension::CompactGridDimension, dimension::Estimate,
+ records::IterRecords,
+ },
+ tables::CompactTable,
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+test_table!(
+ compact_new,
+ CompactTable::new(Matrix::new(3, 3).to_vec()).to_string(),
+ ""
+);
+
+test_table!(
+ compact_with_dimension,
+ {
+ let data = Matrix::with_no_frame(3, 3).to_vec();
+ let mut dims = CompactGridDimension::default();
+ dims.estimate(IterRecords::new(&data, 3, None), &CompactConfig::default());
+ CompactTable::with_dimension(data, dims).columns(3).to_string()
+ },
+ "+-----+-----+-----+"
+ "| 0-0 | 0-1 | 0-2 |"
+ "|-----+-----+-----|"
+ "| 1-0 | 1-1 | 1-2 |"
+ "|-----+-----+-----|"
+ "| 2-0 | 2-1 | 2-2 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ compact_width,
+ CompactTable::new(Matrix::with_no_frame(3, 3).to_vec().to_vec()).columns(3).width(5).to_string(),
+ "+-----+-----+-----+"
+ "| 0-0 | 0-1 | 0-2 |"
+ "|-----+-----+-----|"
+ "| 1-0 | 1-1 | 1-2 |"
+ "|-----+-----+-----|"
+ "| 2-0 | 2-1 | 2-2 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ compact_width_pad_not_included,
+ CompactTable::new(Matrix::with_no_frame(3, 3).to_vec()).columns(3).width(3).to_string(),
+ "+---+---+---+"
+ "| 0-0 | 0-1 | 0-2 |"
+ "|---+---+---|"
+ "| 1-0 | 1-1 | 1-2 |"
+ "|---+---+---|"
+ "| 2-0 | 2-1 | 2-2 |"
+ "+---+---+---+"
+);
+
+test_table!(
+ compact_width_bigger,
+ CompactTable::new(Matrix::with_no_frame(3, 3).to_vec()).columns(3).width(10).to_string(),
+ "+----------+----------+----------+"
+ "| 0-0 | 0-1 | 0-2 |"
+ "|----------+----------+----------|"
+ "| 1-0 | 1-1 | 1-2 |"
+ "|----------+----------+----------|"
+ "| 2-0 | 2-1 | 2-2 |"
+ "+----------+----------+----------+"
+);
+
+test_table!(
+ compact_columns,
+ CompactTable::new(Matrix::with_no_frame(3, 3).to_vec()).columns(3).to_string(),
+ "+--+--+--+"
+ "| 0-0 | 0-1 | 0-2 |"
+ "|--+--+--|"
+ "| 1-0 | 1-1 | 1-2 |"
+ "|--+--+--|"
+ "| 2-0 | 2-1 | 2-2 |"
+ "+--+--+--+"
+);
+
+test_table!(
+ compact_cols_zero,
+ CompactTable::new(Matrix::with_no_frame(3, 3).to_vec())
+ .columns(0)
+ .to_string(),
+ ""
+);
+
+test_table!(
+ compact_cols_less,
+ CompactTable::new(Matrix::with_no_frame(3, 3).to_vec())
+ .columns(1)
+ .to_string(),
+ "+--+"
+ "| 0-0 |"
+ "|--|"
+ "| 1-0 |"
+ "|--|"
+ "| 2-0 |"
+ "+--+"
+);
+
+test_table!(
+ compact_cols_more,
+ CompactTable::new(Matrix::with_no_frame(3, 3).to_vec())
+ .columns(5)
+ .to_string(),
+ "+--+--+--+--+--+"
+ "| 0-0 | 0-1 | 0-2 |"
+ "|--+--+--+--+--|"
+ "| 1-0 | 1-1 | 1-2 |"
+ "|--+--+--+--+--|"
+ "| 2-0 | 2-1 | 2-2 |"
+ "+--+--+--+--+--+"
+);
diff --git a/vendor/tabled/tests/core/extended_table_test.rs b/vendor/tabled/tests/core/extended_table_test.rs
new file mode 100644
index 000000000..8e511127d
--- /dev/null
+++ b/vendor/tabled/tests/core/extended_table_test.rs
@@ -0,0 +1,472 @@
+#![cfg(feature = "std")]
+
+#[cfg(feature = "color")]
+use owo_colors::{AnsiColors, OwoColorize};
+
+use tabled::{tables::ExtendedTable, Tabled};
+
+use crate::matrix::Matrix;
+use testing_table::{static_table, test_table};
+
+macro_rules! assert_expanded_display {
+ ( $data:expr, $expected:expr ) => {
+ let table = ExtendedTable::new($data).to_string();
+ assert_eq!(table, $expected);
+ };
+}
+
+macro_rules! build_tabled_type {
+ ( $name:ident, $length:expr, $fields:expr, $headers:expr ) => {
+ #[derive(Debug, Clone, Copy)]
+ struct $name;
+
+ impl Tabled for $name {
+ const LENGTH: usize = $length;
+
+ fn fields(&self) -> Vec<std::borrow::Cow<'_, str>> {
+ $fields.iter().map(|s| s.to_string().into()).collect()
+ }
+
+ fn headers() -> Vec<std::borrow::Cow<'static, str>> {
+ $headers.iter().map(|s| s.to_string().into()).collect()
+ }
+ }
+ };
+}
+
+test_table!(
+ display,
+ ExtendedTable::from(Matrix::vec(3, 3)),
+ "-[ RECORD 0 ]-"
+ "N | 0"
+ "column 0 | 0-0"
+ "column 1 | 0-1"
+ "column 2 | 0-2"
+ "-[ RECORD 1 ]-"
+ "N | 1"
+ "column 0 | 1-0"
+ "column 1 | 1-1"
+ "column 2 | 1-2"
+ "-[ RECORD 2 ]-"
+ "N | 2"
+ "column 0 | 2-0"
+ "column 1 | 2-1"
+ "column 2 | 2-2"
+);
+
+#[test]
+fn display_empty_records() {
+ build_tabled_type!(TestType, 3, ["He", "123", "asd"], ["1", "2", "3"]);
+ let data: Vec<TestType> = vec![];
+ assert_expanded_display!(data, "");
+}
+
+#[test]
+fn display_empty() {
+ build_tabled_type!(
+ TestType,
+ 3,
+ {
+ let d: Vec<String> = vec![];
+ d
+ },
+ {
+ let d: Vec<String> = vec![];
+ d
+ }
+ );
+ let data: Vec<TestType> = vec![];
+ assert_expanded_display!(data, "");
+}
+
+#[test]
+fn display_empty_2() {
+ build_tabled_type!(EmptyType, 0, [""; 0], [""; 0]);
+ assert_expanded_display!(&[EmptyType], "-[ RECORD 0 ]-");
+}
+
+#[test]
+fn display_dynamic_header_template() {
+ {
+ build_tabled_type!(TestType, 3, ["He", "123", "asd"], ["1", "2", "3"]);
+ assert_expanded_display!(
+ &[TestType],
+ static_table!(
+ "-[ RECORD 0 ]-"
+ "1 | He"
+ "2 | 123"
+ "3 | asd"
+ )
+ );
+ }
+ {
+ build_tabled_type!(TestType, 3, ["He", "123", "asd"], ["11", "2222222", "3"]);
+ assert_expanded_display!(
+ &[TestType],
+ static_table!(
+ "-[ RECORD 0 ]-"
+ "11 | He"
+ "2222222 | 123"
+ "3 | asd"
+ )
+ );
+ }
+ {
+ build_tabled_type!(
+ TestType,
+ 3,
+ ["HeheHehe", "123", "asd"],
+ ["11", "2222222", "3"]
+ );
+ assert_expanded_display!(
+ &[TestType],
+ static_table!(
+ "-[ RECORD 0 ]-----"
+ "11 | HeheHehe"
+ "2222222 | 123"
+ "3 | asd"
+ )
+ );
+ }
+ {
+ build_tabled_type!(TestType, 3, ["He", "123", "asd"], ["11111111111", "2", "3"]);
+ assert_expanded_display!(
+ &[TestType],
+ static_table!(
+ "-[ RECORD 0 ]----"
+ "11111111111 | He"
+ "2 | 123"
+ "3 | asd"
+ )
+ );
+ }
+ {
+ build_tabled_type!(
+ TestType,
+ 3,
+ ["He", "123", "asd"],
+ ["1111111111111", "2", "3"]
+ );
+ assert_expanded_display!(
+ &[TestType],
+ static_table!(
+ "-[ RECORD 0 ]-+----"
+ "1111111111111 | He"
+ "2 | 123"
+ "3 | asd"
+ )
+ );
+ }
+ {
+ build_tabled_type!(
+ TestType,
+ 3,
+ ["He", "123", "asd"],
+ ["11111111111111111111111111111", "2", "3"]
+ );
+ assert_expanded_display!(
+ &[TestType],
+ static_table!(
+ "-[ RECORD 0 ]-----------------+----"
+ "11111111111111111111111111111 | He"
+ "2 | 123"
+ "3 | asd"
+ )
+ );
+ }
+ {
+ build_tabled_type!(TestType, 3, ["22"], ["11111111111"]);
+ assert_expanded_display!(
+ std::iter::repeat(TestType).take(11),
+ static_table!(
+ "-[ RECORD 0 ]---"
+ "11111111111 | 22"
+ "-[ RECORD 1 ]---"
+ "11111111111 | 22"
+ "-[ RECORD 2 ]---"
+ "11111111111 | 22"
+ "-[ RECORD 3 ]---"
+ "11111111111 | 22"
+ "-[ RECORD 4 ]---"
+ "11111111111 | 22"
+ "-[ RECORD 5 ]---"
+ "11111111111 | 22"
+ "-[ RECORD 6 ]---"
+ "11111111111 | 22"
+ "-[ RECORD 7 ]---"
+ "11111111111 | 22"
+ "-[ RECORD 8 ]---"
+ "11111111111 | 22"
+ "-[ RECORD 9 ]---"
+ "11111111111 | 22"
+ "-[ RECORD 10 ]--"
+ "11111111111 | 22"
+ )
+ );
+ }
+}
+
+#[test]
+fn display_multiline_field() {
+ build_tabled_type!(TestType, 3, ["1", "2", "3"], ["Hello\nWorld", "123", "asd"]);
+ assert_expanded_display!(
+ [TestType],
+ static_table!(
+ "-[ RECORD 0 ]---"
+ "Hello\\nWorld | 1"
+ "123 | 2"
+ "asd | 3"
+ )
+ );
+}
+
+#[test]
+fn display_multiline_record_value() {
+ let mut data = Matrix::list::<2, 3>();
+ data[0][0] = "Hello\nWorld".to_string();
+ data[0][1] = "123".to_string();
+ data[0][2] = "asd".to_string();
+
+ assert_expanded_display!(
+ data,
+ static_table!(
+ "-[ RECORD 0 ]----------"
+ "N | Hello\\nWorld"
+ "column 0 | 123"
+ "column 1 | asd"
+ "column 2 | 0-2"
+ "-[ RECORD 1 ]----------"
+ "N | 1"
+ "column 0 | 1-0"
+ "column 1 | 1-1"
+ "column 2 | 1-2"
+ )
+ );
+}
+
+test_table!(
+ display_with_truncate,
+ {
+ let data = Matrix::new(3, 3).insert((1, 0), "a long string").to_vec();
+ let mut table = ExtendedTable::from(data);
+ table.truncate(14, "");
+ table.to_string()
+ },
+ "-[ RECORD 0 ]-"
+ "N | a l"
+ "column 0 | 0-0"
+ "column 1 | 0-1"
+ "column 2 | 0-2"
+ "-[ RECORD 1 ]-"
+ "N | 1"
+ "column 0 | 1-0"
+ "column 1 | 1-1"
+ "column 2 | 1-2"
+ "-[ RECORD 2 ]-"
+ "N | 2"
+ "column 0 | 2-0"
+ "column 1 | 2-1"
+ "column 2 | 2-2"
+);
+
+test_table!(
+ truncate_with_suffix,
+ {
+ let data = Matrix::new(3, 3).insert((1, 0), "a long string").to_vec();
+ let mut table = ExtendedTable::from(data);
+ table.truncate(15, "..");
+ table.to_string()
+ },
+ "-[ RECORD 0 ]-"
+ "N | .."
+ "column 0 | .."
+ "column 1 | .."
+ "column 2 | .."
+ "-[ RECORD 1 ]-"
+ "N | .."
+ "column 0 | .."
+ "column 1 | .."
+ "column 2 | .."
+ "-[ RECORD 2 ]-"
+ "N | .."
+ "column 0 | .."
+ "column 1 | .."
+ "column 2 | .."
+);
+
+#[test]
+fn truncate_big_fields() {
+ build_tabled_type!(
+ TestType,
+ 3,
+ ["1", "2", "3"],
+ ["A quite big field", "123", "asd"]
+ );
+ let data: Vec<TestType> = vec![TestType, TestType];
+
+ let mut table = ExtendedTable::new(&data);
+ table.truncate(14, "..");
+ let table = table.to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "-[ RECORD 0 ]-"
+ "A quite.. | .."
+ "123 | .."
+ "asd | .."
+ "-[ RECORD 1 ]-"
+ "A quite.. | .."
+ "123 | .."
+ "asd | .."
+ )
+ );
+
+ let mut table = ExtendedTable::new(&data);
+ table.truncate(15, "..");
+ let table = table.to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "-[ RECORD 0 ]--"
+ "A quite .. | .."
+ "123 | .."
+ "asd | .."
+ "-[ RECORD 1 ]--"
+ "A quite .. | .."
+ "123 | .."
+ "asd | .."
+ )
+ );
+
+ let mut table = ExtendedTable::new(&data);
+ table.truncate(0, "..");
+ let table = table.to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "-[ RECORD 0 ]-----+--"
+ "A quite big field | 1"
+ "123 | 2"
+ "asd | 3"
+ "-[ RECORD 1 ]-----+--"
+ "A quite big field | 1"
+ "123 | 2"
+ "asd | 3"
+ )
+ );
+
+ let mut table = ExtendedTable::new(&data);
+ table.truncate(20, "......");
+ let table = table.to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "-[ RECORD 0 ]-------"
+ "A qui...... | ......"
+ "123 | ......"
+ "asd | ......"
+ "-[ RECORD 1 ]-------"
+ "A qui...... | ......"
+ "123 | ......"
+ "asd | ......"
+ )
+ );
+}
+
+test_table!(
+ truncate_too_small,
+ {
+ let data = Matrix::new(3, 3).insert((1, 0), "a long string").to_vec();
+ let mut table = ExtendedTable::from(data);
+ let success = table.truncate(2, "");
+ assert!(!success);
+ table
+ },
+ "-[ RECORD 0 ]-----------"
+ "N | a long string"
+ "column 0 | 0-0"
+ "column 1 | 0-1"
+ "column 2 | 0-2"
+ "-[ RECORD 1 ]-----------"
+ "N | 1"
+ "column 0 | 1-0"
+ "column 1 | 1-1"
+ "column 2 | 1-2"
+ "-[ RECORD 2 ]-----------"
+ "N | 2"
+ "column 0 | 2-0"
+ "column 1 | 2-1"
+ "column 2 | 2-2"
+);
+
+#[cfg(feature = "color")]
+#[test]
+fn display_colored() {
+ let mut data = Matrix::list::<3, 3>();
+ data[0][2] = "https://getfedora.org/"
+ .red()
+ .on_color(AnsiColors::Blue)
+ .to_string();
+ data[1][2] = "https://www.opensuse.org/"
+ .green()
+ .on_color(AnsiColors::Black)
+ .to_string();
+ data[2][2] = "https://endeavouros.com/".blue().underline().to_string();
+
+ assert_expanded_display!(
+ data,
+ static_table!(
+ "-[ RECORD 0 ]------------------------------------------------------------"
+ "N | 0"
+ "column 0 | 0-0"
+ "column 1 | \\u{1b}[31;44mhttps://getfedora.org/\\u{1b}[0m"
+ "column 2 | 0-2"
+ "-[ RECORD 1 ]------------------------------------------------------------"
+ "N | 1"
+ "column 0 | 1-0"
+ "column 1 | \\u{1b}[32;40mhttps://www.opensuse.org/\\u{1b}[0m"
+ "column 2 | 1-2"
+ "-[ RECORD 2 ]------------------------------------------------------------"
+ "N | 2"
+ "column 0 | 2-0"
+ "column 1 | \\u{1b}[4m\\u{1b}[34mhttps://endeavouros.com/\\u{1b}[39m\\u{1b}[0m"
+ "column 2 | 2-2"
+ )
+ );
+}
+
+#[cfg(feature = "color")]
+#[test]
+fn display_with_truncate_colored() {
+ let mut data = Matrix::list::<2, 3>();
+ data[0][2] = "https://getfedora.org/".red().to_string();
+ data[1][1] = "https://endeavouros.com/"
+ .white()
+ .on_color(AnsiColors::Black)
+ .to_string();
+ data[1][2] = "https://www.opensuse.org/".to_string();
+
+ let mut table = ExtendedTable::new(&data);
+ table.truncate(20, "");
+ let table = table.to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "-[ RECORD 0 ]-------"
+ "N | 0"
+ "column 0 | 0-0"
+ "column 1 | \\u{1b}[31"
+ "column 2 | 0-2"
+ "-[ RECORD 1 ]-------"
+ "N | 1"
+ "column 0 | \\u{1b}[37"
+ "column 1 | https://w"
+ "column 2 | 1-2"
+ )
+ );
+}
diff --git a/vendor/tabled/tests/core/index_test.rs b/vendor/tabled/tests/core/index_test.rs
new file mode 100644
index 000000000..f6e107a5a
--- /dev/null
+++ b/vendor/tabled/tests/core/index_test.rs
@@ -0,0 +1,194 @@
+#![cfg(feature = "std")]
+
+use std::iter::FromIterator;
+
+use crate::matrix::Matrix;
+use tabled::{builder::Builder, Table};
+use testing_table::test_table;
+
+test_table!(
+ builder_index,
+ Table::builder(Matrix::list::<3, 2>()).index().build(),
+ "+---+---+----------+----------+"
+ "| | N | column 0 | column 1 |"
+ "+---+---+----------+----------+"
+ "| 0 | 0 | 0-0 | 0-1 |"
+ "+---+---+----------+----------+"
+ "| 1 | 1 | 1-0 | 1-1 |"
+ "+---+---+----------+----------+"
+ "| 2 | 2 | 2-0 | 2-1 |"
+ "+---+---+----------+----------+"
+);
+
+test_table!(
+ builder_index_transpose,
+ Table::builder(Matrix::list::<4, 2>()).index().transpose().build(),
+ "+----------+-----+-----+-----+-----+"
+ "| | 0 | 1 | 2 | 3 |"
+ "+----------+-----+-----+-----+-----+"
+ "| N | 0 | 1 | 2 | 3 |"
+ "+----------+-----+-----+-----+-----+"
+ "| column 0 | 0-0 | 1-0 | 2-0 | 3-0 |"
+ "+----------+-----+-----+-----+-----+"
+ "| column 1 | 0-1 | 1-1 | 2-1 | 3-1 |"
+ "+----------+-----+-----+-----+-----+"
+);
+
+test_table!(
+ builder_index_0,
+ Table::builder(Matrix::list::<4, 2>()).index().column(0).build(),
+ "+---+----------+----------+"
+ "| | column 0 | column 1 |"
+ "+---+----------+----------+"
+ "| N | | |"
+ "+---+----------+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+ "| 2 | 2-0 | 2-1 |"
+ "+---+----------+----------+"
+ "| 3 | 3-0 | 3-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ builder_index_0_no_name,
+ Table::builder(Matrix::list::<4, 2>()).index().column(0).name(None).build(),
+ "+---+----------+----------+"
+ "| | column 0 | column 1 |"
+ "+---+----------+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+ "| 2 | 2-0 | 2-1 |"
+ "+---+----------+----------+"
+ "| 3 | 3-0 | 3-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ builder_index_0_name,
+ Table::builder(Matrix::list::<4, 2>()).index().column(0).name(Some("Hello World".into())).build(),
+ "+-------------+----------+----------+"
+ "| | column 0 | column 1 |"
+ "+-------------+----------+----------+"
+ "| Hello World | | |"
+ "+-------------+----------+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+-------------+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+-------------+----------+----------+"
+ "| 2 | 2-0 | 2-1 |"
+ "+-------------+----------+----------+"
+ "| 3 | 3-0 | 3-1 |"
+ "+-------------+----------+----------+"
+);
+
+test_table!(
+ builder_index_0_name_transpose,
+ Table::builder(Matrix::list::<4, 2>()).index().column(0).name(Some("Hello World".into())).transpose().build(),
+ "+-------------+-----+-----+-----+-----+"
+ "| Hello World | 0 | 1 | 2 | 3 |"
+ "+-------------+-----+-----+-----+-----+"
+ "| column 0 | 0-0 | 1-0 | 2-0 | 3-0 |"
+ "+-------------+-----+-----+-----+-----+"
+ "| column 1 | 0-1 | 1-1 | 2-1 | 3-1 |"
+ "+-------------+-----+-----+-----+-----+"
+);
+
+test_table!(
+ builder_index_with_no_columns,
+ Builder::from_iter([["1", "2", "3"], ["a", "b", "c"], ["d", "e", "f"]]).index().build(),
+ "+---+---+---+---+"
+ "| | 0 | 1 | 2 |"
+ "+---+---+---+---+"
+ "| 0 | 1 | 2 | 3 |"
+ "+---+---+---+---+"
+ "| 1 | a | b | c |"
+ "+---+---+---+---+"
+ "| 2 | d | e | f |"
+ "+---+---+---+---+"
+);
+
+test_table!(
+ builder_index_with_no_columns_and_name,
+ Builder::from_iter([["1", "2", "3"], ["a", "b", "c"], ["d", "e", "f"]])
+ .index()
+ .name(Some("Hello World".into()))
+ .build(),
+ "+-------------+---+---+---+"
+ "| | 0 | 1 | 2 |"
+ "+-------------+---+---+---+"
+ "| Hello World | | | |"
+ "+-------------+---+---+---+"
+ "| 0 | 1 | 2 | 3 |"
+ "+-------------+---+---+---+"
+ "| 1 | a | b | c |"
+ "+-------------+---+---+---+"
+ "| 2 | d | e | f |"
+ "+-------------+---+---+---+"
+);
+
+test_table!(
+ builder_index_with_no_columns_transpose,
+ Builder::from_iter([["1", "2", "3"], ["a", "b", "c"], ["d", "e", "f"]])
+ .index()
+ .transpose()
+ .build(),
+ "+---+---+---+---+"
+ "| | 0 | 1 | 2 |"
+ "+---+---+---+---+"
+ "| 0 | 1 | a | d |"
+ "+---+---+---+---+"
+ "| 1 | 2 | b | e |"
+ "+---+---+---+---+"
+ "| 2 | 3 | c | f |"
+ "+---+---+---+---+"
+);
+
+test_table!(builder_index_empty, Builder::default().index().build(), "");
+
+test_table!(
+ builder_index_transpose_empty,
+ Builder::default().index().transpose().build(),
+ ""
+);
+
+test_table!(
+ builder_index_invalid_dosnt_panic,
+ Builder::default().index().column(100).build(),
+ ""
+);
+
+test_table!(
+ builder_index_name_doesnt_shown_when_empty,
+ Builder::default()
+ .index()
+ .name(Some("Hello World".into()))
+ .build(),
+ ""
+);
+
+#[test]
+fn builder_index_transpose_transpose() {
+ let data = Matrix::list::<4, 2>();
+ let builder = Table::builder(data).index();
+
+ let orig_table = builder.clone().build().to_string();
+ let two_times_transposed_table = builder.transpose().transpose().build().to_string();
+
+ assert_eq!(orig_table, two_times_transposed_table,);
+}
+
+#[test]
+fn builder_index_no_name_transpose_transpose() {
+ let data = Matrix::list::<4, 2>();
+ let builder = Table::builder(data).index().name(None);
+
+ let orig_table = builder.clone().build().to_string();
+ let two_times_transposed_table = builder.transpose().transpose().build().to_string();
+
+ assert_eq!(orig_table, two_times_transposed_table,);
+}
diff --git a/vendor/tabled/tests/core/iter_table.rs b/vendor/tabled/tests/core/iter_table.rs
new file mode 100644
index 000000000..dd38ec71c
--- /dev/null
+++ b/vendor/tabled/tests/core/iter_table.rs
@@ -0,0 +1,214 @@
+#![cfg(feature = "std")]
+
+use tabled::tables::IterTable;
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+test_table!(
+ iter_table,
+ IterTable::new(Matrix::with_no_frame(3, 3).to_vec()),
+ "+-----+-----+-----+"
+ "| 0-0 | 0-1 | 0-2 |"
+ "+-----+-----+-----+"
+ "| 1-0 | 1-1 | 1-2 |"
+ "+-----+-----+-----+"
+ "| 2-0 | 2-1 | 2-2 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ iter_table_cols,
+ IterTable::new(Matrix::with_no_frame(3, 3).to_vec()).columns(3),
+ "+-----+-----+-----+"
+ "| 0-0 | 0-1 | 0-2 |"
+ "+-----+-----+-----+"
+ "| 1-0 | 1-1 | 1-2 |"
+ "+-----+-----+-----+"
+ "| 2-0 | 2-1 | 2-2 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ iter_table_cols_less,
+ IterTable::new(Matrix::with_no_frame(3, 3).to_vec()).columns(2),
+ "+-----+-----+"
+ "| 0-0 | 0-1 |"
+ "+-----+-----+"
+ "| 1-0 | 1-1 |"
+ "+-----+-----+"
+ "| 2-0 | 2-1 |"
+ "+-----+-----+"
+);
+
+test_table!(
+ iter_table_cols_zero,
+ IterTable::new(Matrix::with_no_frame(3, 3).to_vec()).columns(0),
+ ""
+);
+
+test_table!(
+ iter_table_iterator,
+ {
+ let mut buf = String::new();
+ IterTable::new((0..3).map(|i: usize| (0..5).map(move |j: usize| format!("{i},{j}")))).fmt(&mut buf).unwrap();
+ buf
+ },
+ "+-----+-----+-----+-----+-----+"
+ "| 0,0 | 0,1 | 0,2 | 0,3 | 0,4 |"
+ "+-----+-----+-----+-----+-----+"
+ "| 1,0 | 1,1 | 1,2 | 1,3 | 1,4 |"
+ "+-----+-----+-----+-----+-----+"
+ "| 2,0 | 2,1 | 2,2 | 2,3 | 2,4 |"
+ "+-----+-----+-----+-----+-----+"
+);
+
+test_table!(
+ iter_table_width,
+ IterTable::new(Matrix::with_no_frame(3, 3).to_vec()).width(2),
+ "+----+----+----+"
+ "| 0- | 0- | 0- |"
+ "+----+----+----+"
+ "| 1- | 1- | 1- |"
+ "+----+----+----+"
+ "| 2- | 2- | 2- |"
+ "+----+----+----+"
+);
+
+test_table!(
+ iter_table_height_does_not_work,
+ IterTable::new(Matrix::with_no_frame(3, 3).to_vec()).height(5),
+ "+-----+-----+-----+"
+ "| 0-0 | 0-1 | 0-2 |"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "+-----+-----+-----+"
+ "| 1-0 | 1-1 | 1-2 |"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "+-----+-----+-----+"
+ "| 2-0 | 2-1 | 2-2 |"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ iter_table_sniff_0,
+ IterTable::new(Matrix::with_no_frame(3, 3).to_vec()).sniff(0),
+ ""
+);
+
+test_table!(
+ iter_table_multiline,
+ IterTable::new(
+ vec![
+ vec!["0", "1", "2", "3"],
+ vec!["0\n1\n2\n3\n4", "0\n1\n2\n\n\n3\n4", "0\n1\n2\n3\n4\n\n\n", "0\n1\n2\n\n\n3\n4\n"]
+ ]
+ ),
+ "+---+---+---+---+"
+ "| 0 | 1 | 2 | 3 |"
+ "+---+---+---+---+"
+ "| 0 | 0 | 0 | 0 |"
+ "| 1 | 1 | 1 | 1 |"
+ "| 2 | 2 | 2 | 2 |"
+ "| 3 | | 3 | |"
+ "| 4 | | 4 | |"
+ "| | 3 | | 3 |"
+ "| | 4 | | 4 |"
+ "| | | | |"
+ "+---+---+---+---+"
+);
+
+test_table!(
+ iter_table_multiline_sniff_1,
+ IterTable::new(
+ vec![
+ vec!["0", "1", "2", "3"],
+ vec!["0\n1\n2\n3\n4", "0\n1\n2\n\n\n3\n4", "0\n1\n2\n3\n4\n\n\n", "0\n1\n2\n\n\n3\n4\n"]
+ ]
+ )
+ .sniff(1),
+ "+---+---+---+---+\n| 0 | 1 | 2 | 3 |\n+---+---+---+---+\n| 0\n1\n2\n3\n4 | 0\n1\n2\n\n\n3\n4 | 0\n1\n2\n3\n4\n\n\n | 0\n1\n2\n\n\n3\n4\n |\n+---+---+---+---+"
+);
+
+test_table!(
+ iter_table_multiline_sniff_2,
+ IterTable::new(
+ vec![
+ vec!["0", "1", "2", "3"],
+ vec!["0\n1\n2\n3\n4", "0\n1\n2\n\n\n3\n4", "0\n1\n2\n3\n4\n\n\n", "0\n1\n2\n\n\n3\n4\n"],
+ vec!["0\n1\n2\n3\n4", "0\n1\n2\n\n\n3\n4", "0\n1\n2\n3\n4\n\n\n", "0\n1\n2\n\n\n3\n4\n"],
+ ]
+ )
+ .sniff(2),
+ "+---+---+---+---+\n| 0 | 1 | 2 | 3 |\n+---+---+---+---+\n| 0 | 0 | 0 | 0 |\n| 1 | 1 | 1 | 1 |\n| 2 | 2 | 2 | 2 |\n| 3 | | 3 | |\n| 4 | | 4 | |\n| | 3 | | 3 |\n| | 4 | | 4 |\n| | | | |\n+---+---+---+---+\n| 0\n1\n2\n3\n4 | 0\n1\n2\n\n\n3\n4 | 0\n1\n2\n3\n4\n\n\n | 0\n1\n2\n\n\n3\n4\n |\n+---+---+---+---+"
+);
+
+test_table!(
+ iter_table_multiline_height_work,
+ IterTable::new(
+ vec![
+ vec!["0", "1", "2", "3"],
+ vec!["0\n1\n2\n3\n4", "0\n1\n2\n\n\n3\n4", "0\n1\n2\n3\n4\n\n\n", "0\n1\n2\n\n\n3\n4\n"]
+ ]
+ )
+ .height(3)
+ ,
+ "+---+---+---+---+"
+ "| 0 | 1 | 2 | 3 |"
+ "| | | | |"
+ "| | | | |"
+ "+---+---+---+---+"
+ "| 0 | 0 | 0 | 0 |"
+ "| 1 | 1 | 1 | 1 |"
+ "| 2 | 2 | 2 | 2 |"
+ "+---+---+---+---+"
+);
+
+test_table!(
+ iter_table_sniff_cut,
+ IterTable::new(
+ vec![
+ vec!["12", "12", "22", "32"],
+ vec!["0", "0", "0", "0"],
+ vec!["023", "123", "223", "323"],
+ ]
+ )
+ .sniff(2)
+ ,
+ "+----+----+----+----+"
+ "| 12 | 12 | 22 | 32 |"
+ "+----+----+----+----+"
+ "| 0 | 0 | 0 | 0 |"
+ "+----+----+----+----+"
+ "| 02 | 12 | 22 | 32 |"
+ "+----+----+----+----+"
+);
+
+test_table!(
+ iter_table_sniff,
+ IterTable::new(
+ vec![
+ vec!["023", "123", "223", "323"],
+ vec!["12", "12", "22", "32"],
+ vec!["0", "0", "0", "0"],
+ ]
+ )
+ .sniff(2)
+ ,
+ "+-----+-----+-----+-----+"
+ "| 023 | 123 | 223 | 323 |"
+ "+-----+-----+-----+-----+"
+ "| 12 | 12 | 22 | 32 |"
+ "+-----+-----+-----+-----+"
+ "| 0 | 0 | 0 | 0 |"
+ "+-----+-----+-----+-----+"
+);
diff --git a/vendor/tabled/tests/core/mod.rs b/vendor/tabled/tests/core/mod.rs
new file mode 100644
index 000000000..14a5404b7
--- /dev/null
+++ b/vendor/tabled/tests/core/mod.rs
@@ -0,0 +1,7 @@
+mod builder_test;
+mod compact_table;
+mod extended_table_test;
+mod index_test;
+mod iter_table;
+mod pool_table;
+mod table_test;
diff --git a/vendor/tabled/tests/core/pool_table.rs b/vendor/tabled/tests/core/pool_table.rs
new file mode 100644
index 000000000..ee28dae78
--- /dev/null
+++ b/vendor/tabled/tests/core/pool_table.rs
@@ -0,0 +1,634 @@
+#![cfg(feature = "std")]
+
+use tabled::{
+ grid::dimension::{DimensionPriority, PoolTableDimension},
+ settings::{formatting::AlignmentStrategy, Alignment, Margin, Padding, Style},
+ tables::{PoolTable, TableValue},
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+#[cfg(feature = "color")]
+use tabled::grid::color::StaticColor;
+
+test_table!(
+ pool_table,
+ PoolTable::new(Matrix::with_no_frame(3, 3).to_vec()),
+ "+-----+-----+-----+"
+ "| 0-0 | 0-1 | 0-2 |"
+ "+-----+-----+-----+"
+ "| 1-0 | 1-1 | 1-2 |"
+ "+-----+-----+-----+"
+ "| 2-0 | 2-1 | 2-2 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ pool_table_1,
+ PoolTable::new([vec!["111111", "222"], vec!["111", "2233", "1", "2", "3"]]),
+ "+-------------+----------+"
+ "| 111111 | 222 |"
+ "+-----+------++--+---+---+"
+ "| 111 | 2233 | 1 | 2 | 3 |"
+ "+-----+------+---+---+---+"
+);
+
+test_table!(
+ pool_table_2,
+ PoolTable::new([vec!["111", "2233", "1", "2", "3"], vec!["111111", "222"]]),
+ "+-----+------+---+---+---+"
+ "| 111 | 2233 | 1 | 2 | 3 |"
+ "+-----+------++--+---+---+"
+ "| 111111 | 222 |"
+ "+-------------+----------+"
+);
+
+test_table!(
+ pool_table_3,
+ PoolTable::new([vec!["1\n11", "2\n2\n3\n3", "1", "\n2\n", "3"], vec!["11\n111\n1", "2\n2\n2"]]),
+ "+----+---+---+---+---+"
+ "| 1 | 2 | 1 | | 3 |"
+ "| 11 | 2 | | 2 | |"
+ "| | 3 | | | |"
+ "| | 3 | | | |"
+ "+----+---+--++---+---+"
+ "| 11 | 2 |"
+ "| 111 | 2 |"
+ "| 1 | 2 |"
+ "+-----------+--------+"
+);
+
+test_table!(
+ pool_table_4,
+ PoolTable::new([vec!["11\n111\n1", "2\n2\n2"], vec!["1\n11", "2\n2\n3\n3", "1", "\n2\n", "3"]]),
+ "+-----------+--------+"
+ "| 11 | 2 |"
+ "| 111 | 2 |"
+ "| 1 | 2 |"
+ "+----+---+--++---+---+"
+ "| 1 | 2 | 1 | | 3 |"
+ "| 11 | 2 | | 2 | |"
+ "| | 3 | | | |"
+ "| | 3 | | | |"
+ "+----+---+---+---+---+"
+);
+
+test_table!(
+ pool_table_multiline,
+ PoolTable::new([
+ ["1", "2\n2", "3\n3\n3"],
+ ["3\n3\n3", "2\n2", "1"],
+ ["1", "3\n3\n3", "2\n2"]
+ ]),
+ "+---+---+---+"
+ "| 1 | 2 | 3 |"
+ "| | 2 | 3 |"
+ "| | | 3 |"
+ "+---+---+---+"
+ "| 3 | 2 | 1 |"
+ "| 3 | 2 | |"
+ "| 3 | | |"
+ "+---+---+---+"
+ "| 1 | 3 | 2 |"
+ "| | 3 | 2 |"
+ "| | 3 | |"
+ "+---+---+---+"
+);
+
+test_table!(
+ pool_table_value,
+ PoolTable::from(TableValue::Row(vec![
+ TableValue::Column(vec![TableValue::Cell(String::from("0-0")), TableValue::Cell(String::from("0-1")), TableValue::Cell(String::from("0-2"))]),
+ TableValue::Column(vec![TableValue::Cell(String::from("1-0")), TableValue::Cell(String::from("1-1")), TableValue::Cell(String::from("1-2"))]),
+ TableValue::Column(vec![TableValue::Cell(String::from("2-0")), TableValue::Cell(String::from("2-1")), TableValue::Cell(String::from("2-2"))]),
+ ]))
+ .with(Style::modern()),
+ "┌─────┬─────┬─────┐"
+ "│ 0-0 │ 1-0 │ 2-0 │"
+ "├─────┼─────┼─────┤"
+ "│ 0-1 │ 1-1 │ 2-1 │"
+ "├─────┼─────┼─────┤"
+ "│ 0-2 │ 1-2 │ 2-2 │"
+ "└─────┴─────┴─────┘"
+);
+
+test_table!(
+ pool_table_value_1,
+ PoolTable::from(TableValue::Row(vec![
+ TableValue::Column(vec![TableValue::Cell(String::from("0-0")), TableValue::Cell(String::from("0-1")), TableValue::Cell(String::from("0-2"))]),
+ TableValue::Column(vec![TableValue::Cell(String::from("1-0")), TableValue::Cell(String::from("1-1")), TableValue::Cell(String::from("1-2"))]),
+ TableValue::Column(vec![
+ TableValue::Column(vec![TableValue::Cell(String::from("2-01")), TableValue::Cell(String::from("2-02")), TableValue::Cell(String::from("2-03"))]),
+ TableValue::Cell(String::from("2-1")),
+ TableValue::Cell(String::from("2-2")),
+ ]),
+ ]))
+ .with(Style::modern()),
+ "┌─────┬─────┬──────┐"
+ "│ 0-0 │ 1-0 │ 2-01 │"
+ "│ │ ├──────┤"
+ "│ │ │ 2-02 │"
+ "├─────┼─────┼──────┤"
+ "│ 0-1 │ 1-1 │ 2-03 │"
+ "│ │ ├──────┤"
+ "├─────┼─────┤ 2-1 │"
+ "│ 0-2 │ 1-2 ├──────┤"
+ "│ │ │ 2-2 │"
+ "└─────┴─────┴──────┘"
+);
+
+test_table!(
+ pool_table_value_2,
+ PoolTable::from(TableValue::Row(vec![
+ TableValue::Column(vec![TableValue::Cell(String::from("0-0")), TableValue::Cell(String::from("0-1")), TableValue::Cell(String::from("0-2"))]),
+ TableValue::Column(vec![
+ TableValue::Row(vec![TableValue::Cell(String::from("2-01")), TableValue::Cell(String::from("2-02")), TableValue::Cell(String::from("2-03"))]),
+ TableValue::Column(vec![TableValue::Cell(String::from("2-01")), TableValue::Cell(String::from("2-02")), TableValue::Cell(String::from("2-03"))]),
+ TableValue::Cell(String::from("1-1")),
+ TableValue::Cell(String::from("1-2"))
+ ]),
+ TableValue::Column(vec![
+ TableValue::Column(vec![TableValue::Cell(String::from("2-01")), TableValue::Cell(String::from("2-02")), TableValue::Cell(String::from("2-03"))]),
+ TableValue::Cell(String::from("2-1")),
+ TableValue::Cell(String::from("2-2"))
+ ]),
+ ]))
+ .with(Style::modern()),
+ "┌─────┬──────┬──────┬──────┬──────┐"
+ "│ 0-0 │ 2-01 │ 2-02 │ 2-03 │ 2-01 │"
+ "│ ├──────┴──────┴──────┤ │"
+ "│ │ 2-01 ├──────┤"
+ "├─────┼────────────────────┤ 2-02 │"
+ "│ 0-1 │ 2-02 ├──────┤"
+ "│ ├────────────────────┤ 2-03 │"
+ "│ │ 2-03 ├──────┤"
+ "├─────┼────────────────────┤ 2-1 │"
+ "│ 0-2 │ 1-1 │ │"
+ "│ ├────────────────────┼──────┤"
+ "│ │ 1-2 │ 2-2 │"
+ "└─────┴────────────────────┴──────┘"
+);
+
+test_table!(
+ pool_table_value_3,
+ PoolTable::from(TableValue::Row(vec![
+ TableValue::Column(vec![TableValue::Cell(String::from("0-0")), TableValue::Cell(String::from("0-1")), TableValue::Cell(String::from("0-2"))]),
+ TableValue::Column(vec![
+ TableValue::Row(vec![TableValue::Cell(String::from("2-01")), TableValue::Cell(String::from("2-02")), TableValue::Cell(String::from("2-03"))]),
+ TableValue::Column(vec![TableValue::Cell(String::from("2-01")), TableValue::Cell(String::from("2-02")), TableValue::Cell(String::from("2-03"))]),
+ TableValue::Cell(String::from("1-1")),
+ TableValue::Row(vec![TableValue::Cell(String::from("2-01")), TableValue::Cell(String::from("2-02")), TableValue::Cell(String::from("2-03"))]),
+ TableValue::Cell(String::from("1-2"))
+ ]),
+ TableValue::Column(vec![
+ TableValue::Column(vec![TableValue::Cell(String::from("2-\n0\n1")), TableValue::Cell(String::from("2\n-\n0\n2")), TableValue::Cell(String::from("2-03"))]),
+ TableValue::Cell(String::from("2-1")),
+ TableValue::Column(vec![TableValue::Cell(String::from("2-0\n1")), TableValue::Cell(String::from("2-02")), TableValue::Cell(String::from("2-03"))]),
+ TableValue::Cell(String::from("2-2"))
+ ]),
+ ]))
+ .with(Style::modern()),
+ "┌─────┬──────┬──────┬──────┬──────┐"
+ "│ 0-0 │ 2-01 │ 2-02 │ 2-03 │ 2- │"
+ "│ │ │ │ │ 0 │"
+ "│ │ │ │ │ 1 │"
+ "│ ├──────┴──────┴──────┼──────┤"
+ "│ │ 2-01 │ 2 │"
+ "│ │ │ - │"
+ "│ ├────────────────────┤ 0 │"
+ "├─────┤ 2-02 │ 2 │"
+ "│ 0-1 │ ├──────┤"
+ "│ ├────────────────────┤ 2-03 │"
+ "│ │ 2-03 ├──────┤"
+ "│ ├────────────────────┤ 2-1 │"
+ "│ │ 1-1 ├──────┤"
+ "│ │ │ 2-0 │"
+ "├─────┤ │ 1 │"
+ "│ 0-2 ├──────┬──────┬──────┼──────┤"
+ "│ │ 2-01 │ 2-02 │ 2-03 │ 2-02 │"
+ "│ │ │ │ ├──────┤"
+ "│ ├──────┴──────┴──────┤ 2-03 │"
+ "│ │ 1-2 ├──────┤"
+ "│ │ │ 2-2 │"
+ "└─────┴────────────────────┴──────┘"
+);
+
+test_table!(
+ pool_table_example,
+ {
+ let data = vec![
+ vec!["Hello World", "Hello World", "Hello World"],
+ vec!["Hello", "", "Hello"],
+ vec!["W", "o", "r", "l", "d"],
+ ];
+
+ let data = TableValue::Column(
+ data.into_iter()
+ .map(|row| {
+ TableValue::Row(
+ row.into_iter()
+ .map(|text| TableValue::Cell(text.to_owned()))
+ .collect(),
+ )
+ })
+ .collect(),
+ );
+
+ PoolTable::from(data)
+ .with(Style::modern())
+ .with(Alignment::center())
+ .to_string()
+ },
+ "┌─────────────┬─────────────┬─────────────┐"
+ "│ Hello World │ Hello World │ Hello World │"
+ "├─────────────┴─┬──────────┬┴─────────────┤"
+ "│ Hello │ │ Hello │"
+ "├────────┬──────┴─┬───────┬┴──────┬───────┤"
+ "│ W │ o │ r │ l │ d │"
+ "└────────┴────────┴───────┴───────┴───────┘"
+);
+
+test_table!(
+ pool_table_value_empty_row,
+ PoolTable::from(TableValue::Row(vec![]))
+ .with(Style::modern()),
+ "┌──┐"
+ "│ │"
+ "└──┘"
+);
+
+test_table!(
+ pool_table_value_empty_column,
+ PoolTable::from(TableValue::Column(vec![]))
+ .with(Style::modern()),
+ "┌──┐"
+ "│ │"
+ "└──┘"
+);
+
+test_table!(
+ pool_table_value_empty_cell,
+ PoolTable::from(TableValue::Cell(String::from("")))
+ .with(Style::modern()),
+ "┌──┐"
+ "│ │"
+ "└──┘"
+);
+
+test_table!(
+ pool_table_padding,
+ PoolTable::new(Matrix::with_no_frame(3, 3).to_vec()).with(Padding::new(1, 2, 3, 4)),
+ "+------+------+------+"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "| 0-0 | 0-1 | 0-2 |"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "+------+------+------+"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "| 1-0 | 1-1 | 1-2 |"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "+------+------+------+"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "| 2-0 | 2-1 | 2-2 |"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "| | | |"
+ "+------+------+------+"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ pool_table_padding_2,
+ PoolTable::new(Matrix::with_no_frame(3, 3).to_vec())
+ .with(Padding::new(1, 2, 3, 4)
+ .fill('!', '@', '#', '$')
+ .colorize(
+ StaticColor::new("\u{1b}[34m", "\u{1b}[39m"),
+ StaticColor::new("\u{1b}[34m", "\u{1b}[39m"),
+ StaticColor::new("\u{1b}[34m", "\u{1b}[39m"),
+ StaticColor::new("\u{1b}[34m", "\u{1b}[39m"),
+ )
+ ),
+ "+------+------+------+"
+ "|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|"
+ "|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|"
+ "|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|"
+ "|\u{1b}[34m!\u{1b}[39m0-0\u{1b}[34m@@\u{1b}[39m|\u{1b}[34m!\u{1b}[39m0-1\u{1b}[34m@@\u{1b}[39m|\u{1b}[34m!\u{1b}[39m0-2\u{1b}[34m@@\u{1b}[39m|"
+ "|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|"
+ "|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|"
+ "|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|"
+ "|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|"
+ "+------+------+------+"
+ "|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|"
+ "|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|"
+ "|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|"
+ "|\u{1b}[34m!\u{1b}[39m1-0\u{1b}[34m@@\u{1b}[39m|\u{1b}[34m!\u{1b}[39m1-1\u{1b}[34m@@\u{1b}[39m|\u{1b}[34m!\u{1b}[39m1-2\u{1b}[34m@@\u{1b}[39m|"
+ "|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|"
+ "|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|"
+ "|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|"
+ "|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|"
+ "+------+------+------+"
+ "|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|"
+ "|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|"
+ "|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|\u{1b}[34m######\u{1b}[39m|"
+ "|\u{1b}[34m!\u{1b}[39m2-0\u{1b}[34m@@\u{1b}[39m|\u{1b}[34m!\u{1b}[39m2-1\u{1b}[34m@@\u{1b}[39m|\u{1b}[34m!\u{1b}[39m2-2\u{1b}[34m@@\u{1b}[39m|"
+ "|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|"
+ "|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|"
+ "|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|"
+ "|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|\u{1b}[34m$$$$$$\u{1b}[39m|"
+ "+------+------+------+"
+);
+
+test_table!(
+ pool_table_margin,
+ PoolTable::new(Matrix::with_no_frame(3, 3).to_vec()).with(Margin::new(1, 2, 3, 4).fill('!', '@', '#', '$')),
+ "!###################@@"
+ "!###################@@"
+ "!###################@@"
+ "!+-----+-----+-----+@@"
+ "!| 0-0 | 0-1 | 0-2 |@@"
+ "!+-----+-----+-----+@@"
+ "!| 1-0 | 1-1 | 1-2 |@@"
+ "!+-----+-----+-----+@@"
+ "!| 2-0 | 2-1 | 2-2 |@@"
+ "!+-----+-----+-----+@@"
+ "!$$$$$$$$$$$$$$$$$$$@@"
+ "!$$$$$$$$$$$$$$$$$$$@@"
+ "!$$$$$$$$$$$$$$$$$$$@@"
+ "!$$$$$$$$$$$$$$$$$$$@@"
+);
+
+test_table!(
+ pool_table_alignment_bottom,
+ PoolTable::new([
+ ["1", "2\n2", "3\n3\n3"],
+ ["3\n3\n3", "2\n2", "1"],
+ ["1", "3\n3\n3", "2\n2"]
+ ])
+ .with(Alignment::bottom()),
+ "+---+---+---+"
+ "| | | 3 |"
+ "| | 2 | 3 |"
+ "| 1 | 2 | 3 |"
+ "+---+---+---+"
+ "| 3 | | |"
+ "| 3 | 2 | |"
+ "| 3 | 2 | 1 |"
+ "+---+---+---+"
+ "| | 3 | |"
+ "| | 3 | 2 |"
+ "| 1 | 3 | 2 |"
+ "+---+---+---+"
+);
+
+test_table!(
+ pool_table_alignment_center_vertical,
+ PoolTable::new([
+ ["1", "2\n2", "3\n3\n3"],
+ ["3\n3\n3", "2\n2", "1"],
+ ["1", "3\n3\n3", "2\n2"]
+ ])
+ .with(Alignment::center_vertical()),
+ "+---+---+---+"
+ "| | 2 | 3 |"
+ "| 1 | 2 | 3 |"
+ "| | | 3 |"
+ "+---+---+---+"
+ "| 3 | 2 | |"
+ "| 3 | 2 | 1 |"
+ "| 3 | | |"
+ "+---+---+---+"
+ "| | 3 | 2 |"
+ "| 1 | 3 | 2 |"
+ "| | 3 | |"
+ "+---+---+---+"
+);
+
+test_table!(
+ pool_table_alignment_right,
+ PoolTable::new([
+ ["1 ", "2\n2 ", "3\n3\n3"],
+ ["3\n3\n3", "2\n2", "1"],
+ ["1", "3\n3\n3 ", "2\n2"]
+ ])
+ .with(Alignment::right()),
+ "+------------------------+----------+---+"
+ "| 1 | 2 | 3 |"
+ "| | 2 | 3 |"
+ "| | | 3 |"
+ "+-------------+----------+-+--------+---+"
+ "| 3 | 2 | 1 |"
+ "| 3 | 2 | |"
+ "| 3 | | |"
+ "+------+------+------------+-----+------+"
+ "| 1 | 3 | 2 |"
+ "| | 3 | 2 |"
+ "| | 3 | |"
+ "+------+-------------------------+------+"
+);
+
+test_table!(
+ pool_table_alignment_right_per_line,
+ PoolTable::new([
+ ["1 ", "2\n2 ", "3\n3\n3"],
+ ["3\n3\n3", "2\n2", "1"],
+ ["1", "3\n3\n3 ", "2\n2"]
+ ])
+ .with(Alignment::right())
+ .with(AlignmentStrategy::PerLine),
+ "+------------------------+----------+---+"
+ "| 1 | 2 | 3 |"
+ "| | 2 | 3 |"
+ "| | | 3 |"
+ "+-------------+----------+-+--------+---+"
+ "| 3 | 2 | 1 |"
+ "| 3 | 2 | |"
+ "| 3 | | |"
+ "+------+------+------------+-----+------+"
+ "| 1 | 3 | 2 |"
+ "| | 3 | 2 |"
+ "| | 3 | |"
+ "+------+-------------------------+------+"
+);
+
+test_table!(
+ pool_table_alignment_center_horizontal,
+ PoolTable::new([
+ ["1 ", "2\n2 ", "3\n3\n3"],
+ ["3\n3\n3", "2\n2", "1"],
+ ["1", "3\n3\n3 ", "2\n2"]
+ ])
+ .with(Alignment::center()),
+ "+------------------------+----------+---+"
+ "| 1 | 2 | 3 |"
+ "| | 2 | 3 |"
+ "| | | 3 |"
+ "+-------------+----------+-+--------+---+"
+ "| 3 | 2 | 1 |"
+ "| 3 | 2 | |"
+ "| 3 | | |"
+ "+------+------+------------+-----+------+"
+ "| 1 | 3 | 2 |"
+ "| | 3 | 2 |"
+ "| | 3 | |"
+ "+------+-------------------------+------+"
+);
+
+test_table!(
+ pool_table_alignment_center_horizontal_line_strategy,
+ PoolTable::new([
+ ["1 ", "2\n22222", "3\n3\n3"],
+ ["3\n3\n3", "2\n2", "1"],
+ ["1", "3\n3\n3 ", "2\n2"]
+ ])
+ .with(Alignment::center())
+ .with(AlignmentStrategy::PerLine),
+ "+------------------------+-------+---+"
+ "| 1 | 2 | 3 |"
+ "| | 22222 | 3 |"
+ "| | | 3 |"
+ "+------------+-----------+-------+---+"
+ "| 3 | 2 | 1 |"
+ "| 3 | 2 | |"
+ "| 3 | | |"
+ "+-----+------+-----------+-----+-----+"
+ "| 1 | 3 | 2 |"
+ "| | 3 | 2 |"
+ "| | 3 | |"
+ "+-----+------------------------+-----+"
+);
+
+test_table!(
+ pool_table_style_empty,
+ PoolTable::new(Matrix::with_no_frame(3, 3).to_vec()).with(Style::empty()),
+ " 0-0 0-1 0-2 "
+ " 1-0 1-1 1-2 "
+ " 2-0 2-1 2-2 "
+);
+
+test_table!(
+ pool_table_style_markdown,
+ PoolTable::new(Matrix::with_no_frame(3, 3).to_vec()).with(Style::markdown()),
+ "| 0-0 | 0-1 | 0-2 |"
+ "| 1-0 | 1-1 | 1-2 |"
+ "| 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ pool_table_style_rounded,
+ PoolTable::new(Matrix::with_no_frame(3, 3).to_vec()).with(Style::rounded()),
+ "╭─────┬─────┬─────╮"
+ "│ 0-0 │ 0-1 │ 0-2 │"
+ " ───── ───── ───── "
+ "│ 1-0 │ 1-1 │ 1-2 │"
+ " ───── ───── ───── "
+ "│ 2-0 │ 2-1 │ 2-2 │"
+ "╰─────┴─────┴─────╯"
+);
+
+test_table!(
+ pool_table_dim_ctrl_0,
+ PoolTable::from(TableValue::Row(vec![
+ TableValue::Column(vec![TableValue::Cell(String::from("0-0")), TableValue::Cell(String::from("0-1")), TableValue::Cell(String::from("0-2"))]),
+ TableValue::Column(vec![TableValue::Cell(String::from("1-0")), TableValue::Cell(String::from("1-1")), TableValue::Cell(String::from("1-2"))]),
+ TableValue::Column(vec![
+ TableValue::Column(vec![TableValue::Cell(String::from("2-01")), TableValue::Cell(String::from("2-02")), TableValue::Cell(String::from("2-03"))]),
+ TableValue::Cell(String::from("2-1")),
+ TableValue::Cell(String::from("2-2")),
+ ]),
+ ]))
+ .with(PoolTableDimension::new(DimensionPriority::Last, DimensionPriority::Last)),
+ "+-----+-----+------+"
+ "| 0-0 | 1-0 | 2-01 |"
+ "+-----+-----+------+"
+ "| 0-1 | 1-1 | 2-02 |"
+ "+-----+-----+------+"
+ "| 0-2 | 1-2 | 2-03 |"
+ "| | +------+"
+ "| | | 2-1 |"
+ "| | +------+"
+ "| | | 2-2 |"
+ "+-----+-----+------+"
+);
+
+test_table!(
+ pool_table_dim_ctrl_1,
+ PoolTable::new([
+ ["1 ", "2\n2 ", "3\n3\n3"],
+ ["3\n3\n3", "2\n2", "1"],
+ ["1", "3\n3\n3 ", "2\n2"]
+ ])
+ .with(PoolTableDimension::new(DimensionPriority::List, DimensionPriority::List)),
+ "+------------------------+----------+---+"
+ "| 1 | 2 | 3 |"
+ "| | 2 | 3 |"
+ "| | | 3 |"
+ "+-------------+----------+-+--------+---+"
+ "| 3 | 2 | 1 |"
+ "| 3 | 2 | |"
+ "| 3 | | |"
+ "+------+------+------------+-----+------+"
+ "| 1 | 3 | 2 |"
+ "| | 3 | 2 |"
+ "| | 3 | |"
+ "+------+-------------------------+------+"
+);
+
+test_table!(
+ pool_table_2_columns_1_cell,
+ PoolTable::from(TableValue::Row(vec![
+ TableValue::Column(vec![
+ TableValue::Cell(String::from("0-0")),
+ TableValue::Cell(String::from("0-1")),
+ TableValue::Cell(String::from("0-2")),
+ TableValue::Cell(String::from("0-3")),
+ TableValue::Cell(String::from("0-4")),
+ TableValue::Cell(String::from("0-5")),
+ ]),
+ TableValue::Column(vec![
+ TableValue::Cell(String::from("1-0")),
+ TableValue::Cell(String::from("1-1")),
+ TableValue::Cell(String::from("1-2")),
+ TableValue::Cell(String::from("1-3")),
+ TableValue::Cell(String::from("1-4")),
+ TableValue::Cell(String::from("1-6")),
+ TableValue::Cell(String::from("1-7")),
+ TableValue::Cell(String::from("1-8")),
+ TableValue::Cell(String::from("1-9")),
+ ]),
+ TableValue::Cell(String::from("2-0")),
+ ]))
+ .with(PoolTableDimension::new(DimensionPriority::Last, DimensionPriority::Last)),
+ "+-----+-----+-----+"
+ "| 0-0 | 1-0 | 2-0 |"
+ "+-----+-----+ |"
+ "| 0-1 | 1-1 | |"
+ "+-----+-----+ |"
+ "| 0-2 | 1-2 | |"
+ "+-----+-----+ |"
+ "| 0-3 | 1-3 | |"
+ "+-----+-----+ |"
+ "| 0-4 | 1-4 | |"
+ "+-----+-----+ |"
+ "| 0-5 | 1-6 | |"
+ "| +-----+ |"
+ "| | 1-7 | |"
+ "| +-----+ |"
+ "| | 1-8 | |"
+ "| +-----+ |"
+ "| | 1-9 | |"
+ "+-----+-----+-----+"
+);
diff --git a/vendor/tabled/tests/core/table_test.rs b/vendor/tabled/tests/core/table_test.rs
new file mode 100644
index 000000000..b949626bb
--- /dev/null
+++ b/vendor/tabled/tests/core/table_test.rs
@@ -0,0 +1,847 @@
+#![cfg(feature = "std")]
+
+use std::iter::FromIterator;
+
+use tabled::{
+ builder::Builder,
+ settings::{formatting::Charset, Height, Modify, Padding, Settings, Style, Width},
+ Table,
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+mod default_types {
+ use super::*;
+
+ test_table!(
+ table_str_vec,
+ Table::new(vec!["hello", "world"]),
+ "+-------+"
+ "| &str |"
+ "+-------+"
+ "| hello |"
+ "+-------+"
+ "| world |"
+ "+-------+"
+ );
+
+ test_table!(
+ table_char_vec,
+ Table::new(vec!['a', 'b', 'c']),
+ "+------+"
+ "| char |"
+ "+------+"
+ "| a |"
+ "+------+"
+ "| b |"
+ "+------+"
+ "| c |"
+ "+------+"
+ );
+
+ test_table!(
+ table_bool_vec,
+ Table::new(vec![true, false, true]),
+ "+-------+"
+ "| bool |"
+ "+-------+"
+ "| true |"
+ "+-------+"
+ "| false |"
+ "+-------+"
+ "| true |"
+ "+-------+"
+ );
+
+ test_table!(
+ table_usize_vec,
+ Table::new(vec![0usize, 1usize, 2usize]),
+ "+-------+"
+ "| usize |"
+ "+-------+"
+ "| 0 |"
+ "+-------+"
+ "| 1 |"
+ "+-------+"
+ "| 2 |"
+ "+-------+"
+ );
+
+ test_table!(
+ table_isize_vec,
+ Table::new(vec![0isize, 1isize, 2isize]),
+ "+-------+"
+ "| isize |"
+ "+-------+"
+ "| 0 |"
+ "+-------+"
+ "| 1 |"
+ "+-------+"
+ "| 2 |"
+ "+-------+"
+ );
+
+ test_table!(
+ table_u8_vec,
+ Table::new(vec![0u8, 1u8, 2u8]),
+ "+----+"
+ "| u8 |"
+ "+----+"
+ "| 0 |"
+ "+----+"
+ "| 1 |"
+ "+----+"
+ "| 2 |"
+ "+----+"
+ );
+
+ test_table!(
+ table_u16_vec,
+ Table::new(vec![0u16, 1u16, 2u16]),
+ "+-----+"
+ "| u16 |"
+ "+-----+"
+ "| 0 |"
+ "+-----+"
+ "| 1 |"
+ "+-----+"
+ "| 2 |"
+ "+-----+"
+ );
+
+ test_table!(
+ table_u32_vec,
+ Table::new(vec![0u32, 1u32, 2u32]),
+ "+-----+"
+ "| u32 |"
+ "+-----+"
+ "| 0 |"
+ "+-----+"
+ "| 1 |"
+ "+-----+"
+ "| 2 |"
+ "+-----+"
+ );
+
+ test_table!(
+ table_u64_vec,
+ Table::new(vec![0u64, 1u64, 2u64]),
+ "+-----+"
+ "| u64 |"
+ "+-----+"
+ "| 0 |"
+ "+-----+"
+ "| 1 |"
+ "+-----+"
+ "| 2 |"
+ "+-----+"
+ );
+
+ test_table!(
+ table_u128_vec,
+ Table::new(vec![0u128, 1u128, 2u128]),
+ "+------+"
+ "| u128 |"
+ "+------+"
+ "| 0 |"
+ "+------+"
+ "| 1 |"
+ "+------+"
+ "| 2 |"
+ "+------+"
+ );
+
+ test_table!(
+ table_i8_vec,
+ Table::new(vec![0i8, 1i8, 2i8]),
+ "+----+"
+ "| i8 |"
+ "+----+"
+ "| 0 |"
+ "+----+"
+ "| 1 |"
+ "+----+"
+ "| 2 |"
+ "+----+"
+ );
+
+ test_table!(
+ table_i16_vec,
+ Table::new(vec![0i16, 1, 2]),
+ "+-----+"
+ "| i16 |"
+ "+-----+"
+ "| 0 |"
+ "+-----+"
+ "| 1 |"
+ "+-----+"
+ "| 2 |"
+ "+-----+"
+ );
+
+ test_table!(
+ table_i32_vec,
+ Table::new(vec![0i32, 1, 2]),
+ "+-----+"
+ "| i32 |"
+ "+-----+"
+ "| 0 |"
+ "+-----+"
+ "| 1 |"
+ "+-----+"
+ "| 2 |"
+ "+-----+"
+ );
+
+ test_table!(
+ table_i64_vec,
+ Table::new(vec![0i64, 1, 2]),
+ "+-----+"
+ "| i64 |"
+ "+-----+"
+ "| 0 |"
+ "+-----+"
+ "| 1 |"
+ "+-----+"
+ "| 2 |"
+ "+-----+"
+ );
+
+ test_table!(
+ table_i128_vec,
+ Table::new(vec![0i128, 1, 2]),
+ "+------+"
+ "| i128 |"
+ "+------+"
+ "| 0 |"
+ "+------+"
+ "| 1 |"
+ "+------+"
+ "| 2 |"
+ "+------+"
+ );
+
+ test_table!(
+ table_array,
+ Table::new(vec![[0, 1, 2], [3, 4, 5], [6, 7, 8]]),
+ "+---+---+---+"
+ "| 0 | 1 | 2 |"
+ "+---+---+---+"
+ "| 0 | 1 | 2 |"
+ "+---+---+---+"
+ "| 3 | 4 | 5 |"
+ "+---+---+---+"
+ "| 6 | 7 | 8 |"
+ "+---+---+---+"
+ );
+}
+
+test_table!(
+ table_tuple,
+ Table::new(vec![("we are in", 2020)]),
+ "+-----------+------+"
+ "| &str | i32 |"
+ "+-----------+------+"
+ "| we are in | 2020 |"
+ "+-----------+------+"
+);
+
+test_table!(
+ table_single_tuple,
+ Table::new(vec![(2020,)]),
+ "+------+"
+ "| i32 |"
+ "+------+"
+ "| 2020 |"
+ "+------+"
+);
+
+test_table!(
+ table_tuple_vec,
+ #[allow(clippy::needless_borrow)]
+ Table::new(&[(0, "Monday"), (1, "Thursday")]),
+ "+-----+----------+"
+ "| i32 | &str |"
+ "+-----+----------+"
+ "| 0 | Monday |"
+ "+-----+----------+"
+ "| 1 | Thursday |"
+ "+-----+----------+"
+);
+
+test_table!(
+ build_table_from_iterator,
+ Matrix::new(3, 3).with(Style::psql()),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ multiline_table_test_0,
+ Table::new([["This is the 0.19 release of Nushell. If you'd like to read more about it, please check out: https://www.nushell.sh/blog/2020/09/01/nushell_0_19.html\r\n\r\nFor convenience, we are providing full builds for Windows, Linux, and macOS. These are the \"all extra features\" builds, so be sure you have the requirements to enable all capabilities: https://github.com/nushell/book/blob/master/en/installation.md#dependencies\r\n"]])
+ .with(Charset::clean())
+ .with(Style::modern()),
+ r#"┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐"#
+ r#"│ 0 │"#
+ r#"├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤"#
+ r#"│ This is the 0.19 release of Nushell. If you'd like to read more about it, please check out: https://www.nushell.sh/blog/2020/09/01/nushell_0_19.html │"#
+ r#"│ │"#
+ r#"│ For convenience, we are providing full builds for Windows, Linux, and macOS. These are the "all extra features" builds, so be sure you have the requirements to enable all capabilities: https://github.com/nushell/book/blob/master/en/installation.md#dependencies │"#
+ r#"│ │"#
+ r#"└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘"#
+);
+
+test_table!(
+ multiline_table_test_1,
+ Table::new([["This is the 0.19 release of Nushell. If you'd like to read more about it, please check out: https://www.nushell.sh/blog/2020/09/01/nushell_0_19.html\r\n\r\nFor convenience, we are providing full builds for Windows, Linux, and macOS. These are the \"all extra features\" builds, so be sure you have the requirements to enable all capabilities: https://github.com/nushell/book/blob/master/en/installation.md#dependencies\r\n"]])
+ .with(Charset::clean())
+ .with(Style::modern())
+ .with(Width::wrap(100)),
+ "┌──────────────────────────────────────────────────────────────────────────────────────────────────┐"
+ "│ 0 │"
+ "├──────────────────────────────────────────────────────────────────────────────────────────────────┤"
+ "│ This is the 0.19 release of Nushell. If you'd like to read more about it, please check out: http │"
+ "│ s://www.nushell.sh/blog/2020/09/01/nushell_0_19.html │"
+ "│ │"
+ "│ For convenience, we are providing full build │"
+ "│ s for Windows, Linux, and macOS. These are the \"all extra features\" builds, so be sure you have │"
+ "│ the requirements to enable all capabilities: https://github.com/nushell/book/blob/master/en/inst │"
+ "│ allation.md#dependencies │"
+ "│ │"
+ "└──────────────────────────────────────────────────────────────────────────────────────────────────┘"
+);
+
+test_table!(
+ multiline_table_test_2,
+ Table::new([["This is the 0.19 release of Nushell. If you'd like to read more about it, please check out: https://www.nushell.sh/blog/2020/09/01/nushell_0_19.html\r\n\r\nFor convenience, we are providing full builds for Windows, Linux, and macOS. These are the \"all extra features\" builds, so be sure you have the requirements to enable all capabilities: https://github.com/nushell/book/blob/master/en/installation.md#dependencies\r\n"]])
+ .with(Modify::new((1, 0)).with(Charset::clean()))
+ .with(Style::modern())
+ .with(Width::wrap(100)),
+ "┌──────────────────────────────────────────────────────────────────────────────────────────────────┐"
+ "│ 0 │"
+ "├──────────────────────────────────────────────────────────────────────────────────────────────────┤"
+ "│ This is the 0.19 release of Nushell. If you'd like to read more about it, please check out: http │"
+ "│ s://www.nushell.sh/blog/2020/09/01/nushell_0_19.html │"
+ "│ │"
+ "│ For convenience, we are providing full build │"
+ "│ s for Windows, Linux, and macOS. These are the \"all extra features\" builds, so be sure you have │"
+ "│ the requirements to enable all capabilities: https://github.com/nushell/book/blob/master/en/inst │"
+ "│ allation.md#dependencies │"
+ "│ │"
+ "└──────────────────────────────────────────────────────────────────────────────────────────────────┘"
+);
+
+#[cfg(feature = "derive")]
+mod derived {
+ use super::*;
+
+ use std::collections::{BTreeMap, BTreeSet};
+
+ use tabled::Tabled;
+
+ #[derive(Tabled)]
+ struct TestType {
+ f1: u8,
+ f2: &'static str,
+ }
+
+ test_table!(
+ table_vector_structures,
+ Table::new([TestType { f1: 0, f2: "0" }, TestType { f1: 1, f2: "1" }]),
+ "+----+----+"
+ "| f1 | f2 |"
+ "+----+----+"
+ "| 0 | 0 |"
+ "+----+----+"
+ "| 1 | 1 |"
+ "+----+----+"
+ );
+
+ test_table!(
+ table_empty_vector_structures,
+ Table::new({let v: Vec<TestType> = Vec::new(); v}),
+ "+----+----+"
+ "| f1 | f2 |"
+ "+----+----+"
+ );
+
+ test_table!(
+ table_option,
+ Table::new(Some(TestType { f1: 0, f2: "0" })),
+ "+----+----+"
+ "| f1 | f2 |"
+ "+----+----+"
+ "| 0 | 0 |"
+ "+----+----+"
+ );
+
+ test_table!(
+ table_option_none,
+ Table::new(Option::<TestType>::None),
+ "+----+----+"
+ "| f1 | f2 |"
+ "+----+----+"
+ );
+
+ test_table!(
+ table_tuple_with_structure_vec,
+ Table::new([(0, TestType { f1: 0, f2: "0str" }), (1, TestType { f1: 1, f2: "1str" })]),
+ "+-----+----+------+"
+ "| i32 | f1 | f2 |"
+ "+-----+----+------+"
+ "| 0 | 0 | 0str |"
+ "+-----+----+------+"
+ "| 1 | 1 | 1str |"
+ "+-----+----+------+"
+ );
+
+ test_table!(
+ table_vector_structures_with_hidden_tabled,
+ Table::new({
+ #[derive(Tabled)]
+ struct St {
+ #[allow(dead_code)]
+ #[tabled(skip)]
+ f1: u8,
+ f2: &'static str,
+ }
+
+ vec![St { f1: 0, f2: "0" }, St { f1: 1, f2: "1" }]
+ }),
+ "+----+"
+ "| f2 |"
+ "+----+"
+ "| 0 |"
+ "+----+"
+ "| 1 |"
+ "+----+"
+ );
+
+ test_table!(
+ table_enum,
+ Table::new({
+ #[derive(Tabled)]
+ enum Letters {
+ Vowels { character: char, lang: u8 },
+ Consonant(char),
+ Digit,
+ }
+
+ vec![
+ Letters::Vowels {
+ character: 'a',
+ lang: 0,
+ },
+ Letters::Consonant('w'),
+ Letters::Vowels {
+ character: 'b',
+ lang: 1,
+ },
+ Letters::Vowels {
+ character: 'c',
+ lang: 2,
+ },
+ Letters::Digit,
+ ]
+ }),
+ "+--------+-----------+-------+"
+ "| Vowels | Consonant | Digit |"
+ "+--------+-----------+-------+"
+ "| + | | |"
+ "+--------+-----------+-------+"
+ "| | + | |"
+ "+--------+-----------+-------+"
+ "| + | | |"
+ "+--------+-----------+-------+"
+ "| + | | |"
+ "+--------+-----------+-------+"
+ "| | | + |"
+ "+--------+-----------+-------+"
+ );
+
+ test_table!(
+ table_enum_with_hidden_variant,
+ Table::new({
+ #[allow(dead_code)]
+ #[derive(Tabled)]
+ enum Letters {
+ Vowels {
+ character: char,
+ lang: u8,
+ },
+ Consonant(char),
+ #[tabled(skip)]
+ Digit,
+ }
+
+ vec![
+ Letters::Vowels {
+ character: 'a',
+ lang: 0,
+ },
+ Letters::Consonant('w'),
+ Letters::Vowels {
+ character: 'b',
+ lang: 1,
+ },
+ Letters::Vowels {
+ character: 'c',
+ lang: 2,
+ },
+ Letters::Digit,
+ ]
+ }),
+ "+--------+-----------+"
+ "| Vowels | Consonant |"
+ "+--------+-----------+"
+ "| + | |"
+ "+--------+-----------+"
+ "| | + |"
+ "+--------+-----------+"
+ "| + | |"
+ "+--------+-----------+"
+ "| + | |"
+ "+--------+-----------+"
+ "| | |"
+ "+--------+-----------+"
+ );
+
+ test_table!(
+ table_btreemap,
+ Table::new({
+ #[derive(Tabled)]
+ struct A {
+ b: u8,
+ c: &'static str,
+ }
+
+ let mut map = BTreeMap::new();
+ map.insert(0, A { b: 1, c: "s1" });
+ map.insert(1, A { b: 2, c: "s2" });
+ map.insert(3, A { b: 3, c: "s3" });
+
+ map
+ }),
+ "+-----+---+----+"
+ "| i32 | b | c |"
+ "+-----+---+----+"
+ "| 0 | 1 | s1 |"
+ "+-----+---+----+"
+ "| 1 | 2 | s2 |"
+ "+-----+---+----+"
+ "| 3 | 3 | s3 |"
+ "+-----+---+----+"
+ );
+
+ test_table!(
+ table_emojie_utf8_style,
+ Table::new({
+ #[derive(Tabled)]
+ struct Language {
+ name: &'static str,
+ designed_by: &'static str,
+ invented_year: usize,
+ }
+
+ vec![
+ Language {
+ name: "C 💕",
+ designed_by: "Dennis Ritchie",
+ invented_year: 1972,
+ },
+ Language {
+ name: "Rust 👍",
+ designed_by: "Graydon Hoare",
+ invented_year: 2010,
+ },
+ Language {
+ name: "Go 🧋",
+ designed_by: "Rob Pike",
+ invented_year: 2009,
+ },
+ ]
+ }).with(Style::modern().remove_horizontal()),
+ // Note: It doesn't look good in VS Code
+ "┌─────────┬────────────────┬───────────────┐"
+ "│ name │ designed_by │ invented_year │"
+ "│ C 💕 │ Dennis Ritchie │ 1972 │"
+ "│ Rust 👍 │ Graydon Hoare │ 2010 │"
+ "│ Go 🧋 │ Rob Pike │ 2009 │"
+ "└─────────┴────────────────┴───────────────┘"
+ );
+
+ test_table!(
+ table_btreeset,
+ Table::new({
+ #[derive(Tabled, PartialEq, Eq, PartialOrd, Ord)]
+ struct A {
+ b: u8,
+ c: &'static str,
+ }
+
+ let mut map = BTreeSet::new();
+ map.insert(A { b: 1, c: "s1" });
+ map.insert(A { b: 2, c: "s2" });
+ map.insert(A { b: 3, c: "s3" });
+
+ map
+ }),
+ "+---+----+"
+ "| b | c |"
+ "+---+----+"
+ "| 1 | s1 |"
+ "+---+----+"
+ "| 2 | s2 |"
+ "+---+----+"
+ "| 3 | s3 |"
+ "+---+----+"
+ );
+
+ test_table!(
+ table_emojie,
+ Table::new({
+ #[derive(Tabled)]
+ struct Language {
+ name: &'static str,
+ designed_by: &'static str,
+ invented_year: usize,
+ }
+
+ vec![
+ Language {
+ name: "C 💕",
+ designed_by: "Dennis Ritchie",
+ invented_year: 1972,
+ },
+ Language {
+ name: "Rust 👍",
+ designed_by: "Graydon Hoare",
+ invented_year: 2010,
+ },
+ Language {
+ name: "Go 🧋",
+ designed_by: "Rob Pike",
+ invented_year: 2009,
+ },
+ ]
+ }),
+ "+---------+----------------+---------------+"
+ "| name | designed_by | invented_year |"
+ "+---------+----------------+---------------+"
+ "| C 💕 | Dennis Ritchie | 1972 |"
+ "+---------+----------------+---------------+"
+ "| Rust 👍 | Graydon Hoare | 2010 |"
+ "+---------+----------------+---------------+"
+ "| Go 🧋 | Rob Pike | 2009 |"
+ "+---------+----------------+---------------+"
+ );
+
+ test_table!(
+ tuple_combination,
+ Table::new({
+ #[derive(Tabled)]
+ enum Domain {
+ Security,
+ Embedded,
+ Frontend,
+ Unknown,
+ }
+
+ #[derive(Tabled)]
+ struct Developer(#[tabled(rename = "name")] &'static str);
+
+ vec![
+ (Developer("Terri Kshlerin"), Domain::Embedded),
+ (Developer("Catalina Dicki"), Domain::Security),
+ (Developer("Jennie Schmeler"), Domain::Frontend),
+ (Developer("Maxim Zhiburt"), Domain::Unknown),
+ ]
+ }),
+ "+-----------------+----------+----------+----------+---------+"
+ "| name | Security | Embedded | Frontend | Unknown |"
+ "+-----------------+----------+----------+----------+---------+"
+ "| Terri Kshlerin | | + | | |"
+ "+-----------------+----------+----------+----------+---------+"
+ "| Catalina Dicki | + | | | |"
+ "+-----------------+----------+----------+----------+---------+"
+ "| Jennie Schmeler | | | + | |"
+ "+-----------------+----------+----------+----------+---------+"
+ "| Maxim Zhiburt | | | | + |"
+ "+-----------------+----------+----------+----------+---------+"
+
+ );
+
+ test_table!(
+ table_trait,
+ Table::new({
+ #[derive(Tabled)]
+ enum Domain {
+ Security,
+ Embedded,
+ Frontend,
+ Unknown,
+ }
+
+ #[derive(Tabled)]
+ struct Developer(#[tabled(rename = "name")] &'static str);
+
+ vec![
+ (Developer("Terri Kshlerin"), Domain::Embedded),
+ (Developer("Catalina Dicki"), Domain::Security),
+ (Developer("Jennie Schmeler"), Domain::Frontend),
+ (Developer("Maxim Zhiburt"), Domain::Unknown),
+ ]
+ }),
+ "+-----------------+----------+----------+----------+---------+"
+ "| name | Security | Embedded | Frontend | Unknown |"
+ "+-----------------+----------+----------+----------+---------+"
+ "| Terri Kshlerin | | + | | |"
+ "+-----------------+----------+----------+----------+---------+"
+ "| Catalina Dicki | + | | | |"
+ "+-----------------+----------+----------+----------+---------+"
+ "| Jennie Schmeler | | | + | |"
+ "+-----------------+----------+----------+----------+---------+"
+ "| Maxim Zhiburt | | | | + |"
+ "+-----------------+----------+----------+----------+---------+"
+ );
+
+ test_table!(
+ table_emojie_multiline,
+ Table::new({
+ #[derive(Tabled)]
+ struct Article {
+ name: &'static str,
+ author: &'static str,
+ text: &'static str,
+ rating: usize,
+ }
+
+ vec![
+ Article {
+ name: "Rebase vs Merge commit in depth 👋",
+ author: "Rose Kuphal DVM",
+ text: "A multiline\n text with 🤯 😳 🥵 🥶\n a bunch of emojies ☄️ 💥 🔥 🌪",
+ rating: 43,
+ },
+ Article {
+ name: "Keep it simple",
+ author: "Unknown",
+ text: "🍳",
+ rating: 100,
+ },
+ ]
+ }),
+ "+------------------------------------+-----------------+-------------------------------+--------+"
+ "| name | author | text | rating |"
+ "+------------------------------------+-----------------+-------------------------------+--------+"
+ "| Rebase vs Merge commit in depth 👋 | Rose Kuphal DVM | A multiline | 43 |"
+ "| | | text with 🤯 😳 🥵 🥶 | |"
+ "| | | a bunch of emojies ☄\u{fe0f} 💥 🔥 🌪 | |"
+ "+------------------------------------+-----------------+-------------------------------+--------+"
+ "| Keep it simple | Unknown | 🍳 | 100 |"
+ "+------------------------------------+-----------------+-------------------------------+--------+"
+ );
+}
+
+#[cfg(feature = "color")]
+#[test]
+fn multiline_table_test2() {
+ use testing_table::assert_table;
+
+ let data = &[
+ ["\u{1b}[37mThis is the 0.19 release of Nushell. If you'd like to read more about it, please check out: https://www.nushell.sh/blog/2020/09/01/nushell_0_19.html\n\nFor convenience, we are providing full builds for Windows, Linux, and macOS. These are the \"all extra features\" builds, so be sure you have the requirements to enable all capabilities: https://github.com/nushell/book/blob/master/en/installation.md#dependencies\n\u{1b}[0m"],
+ ];
+
+ let mut table = Table::new(data);
+ table.with(Style::modern());
+
+ assert_table!(
+ ansi_str::AnsiStr::ansi_strip(&table.to_string()),
+ r#"┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐"#
+ r#"│ 0 │"#
+ r#"├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤"#
+ r#"│ This is the 0.19 release of Nushell. If you'd like to read more about it, please check out: https://www.nushell.sh/blog/2020/09/01/nushell_0_19.html │"#
+ r#"│ │"#
+ r#"│ For convenience, we are providing full builds for Windows, Linux, and macOS. These are the "all extra features" builds, so be sure you have the requirements to enable all capabilities: https://github.com/nushell/book/blob/master/en/installation.md#dependencies │"#
+ r#"│ │"#
+ r#"└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘"#
+ );
+
+ table.with(Width::wrap(100));
+
+ assert_table!(
+ ansi_str::AnsiStr::ansi_strip(&table.to_string()),
+ "┌──────────────────────────────────────────────────────────────────────────────────────────────────┐"
+ "│ 0 │"
+ "├──────────────────────────────────────────────────────────────────────────────────────────────────┤"
+ "│ This is the 0.19 release of Nushell. If you'd like to read more about it, please check out: http │"
+ "│ s://www.nushell.sh/blog/2020/09/01/nushell_0_19.html │"
+ "│ │"
+ "│ For convenience, we are providing full build │"
+ "│ s for Windows, Linux, and macOS. These are the \"all extra features\" builds, so be sure you have │"
+ "│ the requirements to enable all capabilities: https://github.com/nushell/book/blob/master/en/inst │"
+ "│ allation.md#dependencies │"
+ "│ │"
+ "└──────────────────────────────────────────────────────────────────────────────────────────────────┘"
+ );
+}
+
+test_table!(
+ table_1x1_empty,
+ {
+ Builder::from_iter(vec![vec![""]]).build()
+ .with(Style::modern())
+ .with(Settings::new(Height::limit(0), Width::increase(10)))
+ },
+ "┌────────┐"
+ "└────────┘"
+);
+
+test_table!(
+ table_2x2_empty,
+ {
+ Builder::from_iter(vec![vec![" ", ""], vec![" ", ""]]).build()
+ .with(Style::modern())
+ .with(Padding::zero())
+ .with(Height::list([1, 0]))
+ },
+ "┌─┬┐"
+ "│ ││"
+ "├─┼┤"
+ "└─┴┘"
+);
+
+test_table!(
+ table_2x2_empty_height_list_together_with_width_list_dont_work_0,
+ {
+ Builder::from_iter(vec![vec!["", ""], vec!["", ""]]).build()
+ .with(Style::modern())
+ .with(Padding::zero())
+ .with(Height::list([1, 0]))
+ .with(Width::list([1, 0]))
+ },
+ "┌─┬┐"
+ "│ ││"
+ "├─┼┤"
+ "│ ││"
+ "└─┴┘"
+);
+
+test_table!(
+ table_2x2_empty_height_list_together_with_width_list_dont_work_1,
+ {
+ Builder::from_iter(vec![vec!["", ""], vec!["", ""]]).build()
+ .with(Style::modern())
+ .with(Padding::zero())
+ .with(Width::list([1, 0]))
+ .with(Height::list([1, 0]))
+ },
+ "┌┬┐"
+ "│││"
+ "├┼┤"
+ "└┴┘"
+);
diff --git a/vendor/tabled/tests/derive/derive_test.rs b/vendor/tabled/tests/derive/derive_test.rs
new file mode 100644
index 000000000..d27e43eee
--- /dev/null
+++ b/vendor/tabled/tests/derive/derive_test.rs
@@ -0,0 +1,1090 @@
+#![cfg(feature = "derive")]
+#![cfg(feature = "std")]
+
+use tabled::Tabled;
+
+// https://users.rust-lang.org/t/create-a-struct-from-macro-rules/19829
+macro_rules! test_tuple {
+ (
+ $test_name:ident,
+ t: $(#[$struct_attr:meta])* { $( $(#[$attr:meta])* $ty:ty)* },
+ init: { $($init:expr)* },
+ expected: $headers:expr, $fields:expr,
+ $(pre: { $($init_block:stmt)* })?
+ ) => {
+ #[test]
+ fn $test_name() {
+ $($($init_block)*)?
+
+ #[derive(Tabled)]
+ struct TestType(
+ $( $(#[$attr])* $ty, )*
+ );
+
+ let value = TestType($($init,)*);
+
+ let fields: Vec<&'static str> = $fields.to_vec();
+ let headers: Vec<&'static str> = $headers.to_vec();
+
+ assert_eq!(value.fields(), fields);
+ assert_eq!(TestType::headers(), headers);
+ assert_eq!(<TestType as Tabled>::LENGTH, headers.len());
+ assert_eq!(<TestType as Tabled>::LENGTH, fields.len());
+ }
+ };
+}
+
+macro_rules! test_enum {
+ (
+ $test_name:ident,
+ t: $(#[$struct_attr:meta])* { $( $(#[$var_attr:meta])* $var:ident $({ $( $(#[$attr:meta])* $field:ident: $ty:ty),* $(,)? })? $(( $( $(#[$attr2:meta])* $ty2:ty),* $(,)? ))? )* },
+ $(pre: { $($init_block:stmt)* })?
+ headers: $headers:expr,
+ tests: $($init:expr => $expected:expr,)*
+ ) => {
+ #[allow(dead_code, unused_imports)]
+ #[test]
+ fn $test_name() {
+ $($($init_block)*)?
+
+ #[derive(Tabled)]
+ $(#[$struct_attr])*
+ enum TestType {
+ $(
+ $(#[$var_attr])*
+ $var $({
+ $( $(#[$attr])* $field: $ty,)*
+ })?
+
+ $((
+ $( $(#[$attr2])* $ty2,)*
+ ))?
+ ),*
+ }
+
+ let headers: Vec<&'static str> = $headers.to_vec();
+ assert_eq!(TestType::headers(), headers);
+ assert_eq!(<TestType as Tabled>::LENGTH, headers.len());
+
+ {
+ use TestType::*;
+ $(
+ let variant = $init;
+ let fields: Vec<&'static str> = $expected.to_vec();
+ assert_eq!(variant.fields(), fields);
+ )*
+ }
+ }
+ };
+}
+
+macro_rules! test_struct {
+ (
+ $test_name:ident,
+ t: $(#[$struct_attr:meta])* { $( $(#[$attr:meta])* $field:ident: $ty:ty),* $(,)?}
+ $(pre: { $($init_block:stmt)* })?
+ init: { $( $val_field:ident: $val:expr),* $(,)?}
+ expected: $headers:expr, $fields:expr $(,)?
+ ) => {
+
+ #[allow(dead_code, unused_imports)]
+ #[test]
+ fn $test_name() {
+ $($($init_block)*)?
+
+ #[derive(Tabled)]
+ $(#[$struct_attr])*
+ struct TestType {
+ $(
+ $(#[$attr])*
+ $field: $ty,
+ )*
+ }
+
+ let value = TestType {
+ $($val_field: $val,)*
+ };
+
+ let fields: Vec<&'static str> = $fields.to_vec();
+ let headers: Vec<&'static str> = $headers.to_vec();
+ assert_eq!(TestType::headers(), headers);
+ assert_eq!(value.fields(), fields);
+ assert_eq!(<TestType as Tabled>::LENGTH, headers.len());
+ assert_eq!(<TestType as Tabled>::LENGTH, fields.len());
+ }
+ };
+}
+
+#[allow(non_camel_case_types)]
+type sstr = &'static str;
+
+mod tuple {
+ use super::*;
+
+ test_tuple!(basic, t: { u8 sstr }, init: { 0 "v2" }, expected: ["0", "1"], ["0", "v2"],);
+ test_tuple!(empty, t: { }, init: { }, expected: [], [],);
+
+ test_tuple!(rename, t: { u8 #[tabled(rename = "field 2")] sstr }, init: { 0 "123" }, expected: ["0", "field 2"], ["0", "123"],);
+
+ test_tuple!(skip_0, t: { #[tabled(skip)] u8 #[tabled(rename = "field 2", skip)] sstr sstr }, init: { 0 "v2" "123" }, expected: ["2"], ["123"],);
+ test_tuple!(skip_1, t: { #[tabled(skip)] u8 #[tabled(skip)] #[tabled(rename = "field 2")] sstr sstr }, init: { 0 "v2" "123" }, expected: ["2"], ["123"],);
+
+ test_tuple!(order_0, t: { #[tabled(order = 0)] u8 u8 u8}, init: { 0 1 2 }, expected: ["0", "1", "2"], ["0", "1", "2"],);
+ test_tuple!(order_1, t: { #[tabled(order = 1)] u8 u8 u8}, init: { 0 1 2 }, expected: ["1", "0", "2"], ["1", "0", "2"],);
+ test_tuple!(order_2, t: { #[tabled(order = 2)] u8 u8 u8}, init: { 0 1 2 }, expected: ["1", "2", "0"], ["1", "2", "0"],);
+ test_tuple!(order_3, t: { u8 #[tabled(order = 0)] u8 u8}, init: { 0 1 2 }, expected: ["1", "0", "2"], ["1", "0", "2"],);
+ test_tuple!(order_4, t: { u8 #[tabled(order = 1)] u8 u8}, init: { 0 1 2 }, expected: ["0", "1", "2"], ["0", "1", "2"],);
+ test_tuple!(order_5, t: { u8 #[tabled(order = 2)] u8 u8}, init: { 0 1 2 }, expected: ["0", "2", "1"], ["0", "2", "1"],);
+ test_tuple!(order_6, t: { u8 u8 #[tabled(order = 0)] u8}, init: { 0 1 2 }, expected: ["2", "0", "1"], ["2", "0", "1"],);
+ test_tuple!(order_7, t: { u8 u8 #[tabled(order = 1)] u8}, init: { 0 1 2 }, expected: ["0", "2", "1"], ["0", "2", "1"],);
+ test_tuple!(order_8, t: { u8 u8 #[tabled(order = 2)] u8}, init: { 0 1 2 }, expected: ["0", "1", "2"], ["0", "1", "2"],);
+ test_tuple!(order_9, t: { #[tabled(order = 2)] u8 u8 #[tabled(order = 0)] u8}, init: { 0 1 2 }, expected: ["2", "1", "0"], ["2", "1", "0"],);
+ test_tuple!(order_10, t: { #[tabled(order = 2)] u8 #[tabled(order = 1)] u8 u8}, init: { 0 1 2 }, expected: ["2", "1", "0"], ["2", "1", "0"],);
+ test_tuple!(order_11, t: { #[tabled(order = 2)] u8 #[tabled(order = 2)] u8 #[tabled(order = 1)] u8}, init: { 0 1 2 }, expected: ["0", "2", "1"], ["0", "2", "1"],);
+ test_tuple!(order_12, t: { #[tabled(order = 2)] u8 #[tabled(order = 2)] u8 #[tabled(order = 2)] u8}, init: { 0 1 2 }, expected: ["0", "1", "2"], ["0", "1", "2"],);
+ test_tuple!(order_13, t: { #[tabled(order = 1)] u8 #[tabled(order = 1)] u8 #[tabled(order = 1)] u8}, init: { 0 1 2 }, expected: ["0", "2", "1"], ["0", "2", "1"],);
+ test_tuple!(order_14, t: { #[tabled(order = 2)] u8 #[tabled(order = 1)] u8 #[tabled(order = 0)] u8}, init: { 0 1 2 }, expected: ["2", "1", "0"], ["2", "1", "0"],);
+
+ test_tuple!(rename_all, t: #[tabled(rename_all = "UPPERCASE")] { u8 sstr}, init: { 0 "123" }, expected: ["0", "1"], ["0", "123"],);
+ test_tuple!(rename_all_field, t: { u8 #[tabled(rename_all = "UPPERCASE")] sstr}, init: { 0 "123" }, expected: ["0", "1"], ["0", "123"],);
+ test_tuple!(rename_all_field_with_rename_0, t: { u8 #[tabled(rename_all = "UPPERCASE", rename = "Something")] sstr}, init: { 0 "123" }, expected: ["0", "Something"], ["0", "123"],);
+ test_tuple!(rename_all_field_with_rename_1, t: { u8 #[tabled(rename = "Something")] #[tabled(rename_all = "UPPERCASE")] sstr}, init: { 0 "123" }, expected: ["0", "Something"], ["0", "123"],);
+
+ test_tuple!(
+ display_option,
+ t: { u8 #[tabled(display_with = "display_option")] Option<sstr> },
+ init: { 0 Some("v2") },
+ expected: ["0", "1"], ["0", "some v2"],
+ pre: {
+ fn display_option(o: &Option<sstr>) -> String {
+ match o {
+ Some(s) => format!("some {s}"),
+ None => "none".to_string(),
+ }
+ }
+ }
+ );
+
+ test_tuple!(
+ display_option_args,
+ t: { u8 #[tabled(display_with("display_option", 1, "234"))] Option<sstr> },
+ init: { 0 Some("v2") },
+ expected: ["0", "1"], ["0", "some 1 234"],
+ pre: {
+ fn display_option(val: usize, text: &str) -> String {
+ format!("some {val} {text}")
+ }
+ }
+ );
+
+ test_tuple!(
+ display_option_self,
+ t: { u8 #[tabled(display_with = "Self::display_option")] Option<sstr> },
+ init: { 0 Some("v2") },
+ expected: ["0", "1"], ["0", "some v2"],
+ pre: {
+ impl TestType {
+ fn display_option(o: &Option<sstr>) -> String {
+ match o {
+ Some(s) => format!("some {s}"),
+ None => "none".to_string(),
+ }
+ }
+ }
+ }
+ );
+
+ test_tuple!(
+ display_option_self_2,
+ t: { u8 #[tabled(display_with("Self::display_option", self))] Option<sstr> },
+ init: { 0 Some("v2") },
+ expected: ["0", "1"], ["0", "some v2"],
+ pre: {
+ impl TestType {
+ fn display_option(o: &TestType) -> String {
+ match o.1 {
+ Some(s) => format!("some {s}"),
+ None => "none".to_string(),
+ }
+ }
+ }
+ }
+ );
+
+ test_tuple!(
+ display_option_self_3,
+ t: { u8 #[tabled(display_with("display_option", self))] Option<sstr> },
+ init: { 0 Some("v2") },
+ expected: ["0", "1"], ["0", "some v2"],
+ pre: {
+ fn display_option(o: &TestType) -> String {
+ match o.1 {
+ Some(s) => format!("some {s}"),
+ None => "none".to_string(),
+ }
+ }
+ }
+ );
+
+ // #[test]
+ // fn order_compile_fail_when_order_is_bigger_then_count_fields() {
+ // #[derive(Tabled)]
+ // struct St(#[tabled(order = 3)] u8, u8, u8);
+ // }
+}
+
+mod enum_ {
+ use super::*;
+
+ test_enum!(
+ basic,
+ t: { Security Embedded Frontend Unknown },
+ headers: ["Security", "Embedded", "Frontend", "Unknown"],
+ tests:
+ Security => ["+", "", "", ""],
+ Embedded => ["", "+", "", ""],
+ Frontend => ["", "", "+", ""],
+ Unknown => ["", "", "", "+"],
+ );
+
+ test_enum!(
+ diverse,
+ t: { A { a: u8, b: i32 } B(sstr) K },
+ headers: ["A", "B", "K"],
+ tests:
+ A { a: 1, b: 2 } => ["+", "", ""],
+ B("") => ["", "+", ""],
+ K => ["", "", "+"],
+ );
+
+ test_enum!(
+ rename_variant,
+ t: { #[tabled(rename = "Variant 1")] A { a: u8, b: i32 } #[tabled(rename = "Variant 2")] B(sstr) K },
+ headers: ["Variant 1", "Variant 2", "K"],
+ tests:
+ A { a: 1, b: 2 } => ["+", "", ""],
+ B("") => ["", "+", ""],
+ K => ["", "", "+"],
+ );
+
+ test_enum!(
+ skip_variant,
+ t: { A { a: u8, b: i32 } #[tabled(skip)] B(sstr) K },
+ headers: ["A", "K"],
+ tests:
+ A { a: 1, b: 2 } => ["+", ""],
+ B("") => ["", ""],
+ K => ["", "+"],
+ );
+
+ test_enum!(
+ inline_variant,
+ t: {
+ #[tabled(inline("Auto::"))] Auto { #[tabled(rename = "mod")] model: sstr, engine: sstr }
+ #[tabled(inline)] Bikecycle( #[tabled(rename = "name")] sstr, #[tabled(inline)] Bike )
+ Skateboard
+ },
+ pre: {
+ #[derive(Tabled)]
+ struct Bike { brand: sstr, price: f32 }
+ }
+ headers: ["Auto::mod", "Auto::engine", "name", "brand", "price", "Skateboard"],
+ tests:
+ Skateboard => ["", "", "", "", "", "+"],
+ Auto { model: "Mini", engine: "v8" } => ["Mini", "v8", "", "", "", ""],
+ Bikecycle("A bike", Bike { brand: "Canyon", price: 2000.0 })=> ["", "", "A bike", "Canyon", "2000", ""],
+ );
+
+ test_enum!(
+ inline_field_with_display_function,
+ t: {
+ #[tabled(inline("backend::"))]
+ Backend { #[tabled(display_with = "display", rename = "name")] value: sstr }
+ Frontend
+ },
+ pre: {
+ fn display(_: sstr) -> String {
+ "asd".to_string()
+ }
+ }
+ headers: ["backend::name", "Frontend"],
+ tests:
+ Backend { value: "123" } => ["asd", ""],
+ Frontend => ["", "+"],
+ );
+
+ test_enum!(
+ inline_field_with_display_self_function,
+ t: {
+ #[tabled(inline("backend::"))]
+ Backend { #[tabled()] #[tabled(display_with("display", self), rename = "name")] value: sstr }
+ Frontend
+ },
+ pre: {
+ fn display<T>(_: &T) -> String {
+ "asd".to_string()
+ }
+ }
+ headers: ["backend::name", "Frontend"],
+ tests:
+ Backend { value: "123" } => ["asd", ""],
+ Frontend => ["", "+"],
+ );
+
+ test_enum!(
+ with_display,
+ t: {
+ #[tabled(inline)]
+ A(#[tabled(display_with = "format::<4>")] sstr)
+ B
+ },
+ pre: {
+ fn format<const ID: usize>(_: sstr) -> String {
+ ID.to_string()
+ }
+ }
+ headers: ["0", "B"],
+ tests:
+ A("") => ["4", ""],
+ B => ["", "+"],
+ );
+
+ test_enum!(
+ with_display_self,
+ t: {
+ #[tabled(inline)]
+ A(#[tabled(display_with("Self::format::<4>", self))] sstr)
+ B
+ },
+ pre: {
+ impl TestType {
+ fn format<const ID: usize>(&self) -> String {
+ ID.to_string()
+ }
+ }
+ }
+ headers: ["0", "B"],
+ tests:
+ A("") => ["4", ""],
+ B => ["", "+"],
+ );
+
+ test_enum!(
+ rename_all_variant,
+ t: {
+ #[tabled(rename_all = "snake_case")]
+ VariantName1 { a: u8, b: i32 }
+ #[tabled(rename_all = "UPPERCASE")]
+ VariantName2(String)
+ K
+ },
+ headers: ["variant_name1", "VARIANTNAME2", "K"],
+ tests:
+ );
+
+ test_enum!(
+ rename_all_enum,
+ t: #[tabled(rename_all = "snake_case")] {
+ VariantName1 { a: u8, b: i32 }
+ VariantName2(String)
+ K
+ },
+ headers: ["variant_name1", "variant_name2", "k"],
+ tests:
+ );
+
+ test_enum!(
+ rename_all_enum_inherited_inside_struct_enum,
+ t: #[tabled(rename_all = "snake_case")] {
+ #[tabled(inline)]
+ VariantName1 { some_field_1: u8, some_field_2: i32 }
+ VariantName2(String)
+ K
+ },
+ headers: ["some_field_1", "some_field_2", "variant_name2", "k"],
+ tests:
+ );
+
+ test_enum!(
+ rename_all_enum_inherited_inside_struct_override_by_rename_enum,
+ t: #[tabled(rename_all = "snake_case")] {
+ #[tabled(inline)]
+ VariantName1 {
+ #[tabled(rename = "f1")]
+ some_field_1: u8,
+ #[tabled(rename = "f2")]
+ some_field_2: i32,
+ }
+ VariantName2(String)
+ K
+ },
+ headers: ["f1", "f2", "variant_name2", "k"],
+ tests:
+ );
+
+ test_enum!(
+ rename_all_enum_inherited_inside_struct_override_by_rename_all_enum,
+ t: #[tabled(rename_all = "snake_case")] {
+ #[tabled(inline)]
+ VariantName1 {
+ #[tabled(rename_all = "UPPERCASE")]
+ some_field_1: u8,
+ #[tabled(rename_all = "CamelCase")]
+ some_field_2: i32,
+ }
+ VariantName2(String)
+ K
+ },
+ headers: ["SOMEFIELD1", "someField2", "variant_name2", "k"],
+ tests:
+ );
+
+ test_enum!(
+ rename_all_variant_inherited_inside_struct_enum,
+ t: #[tabled(rename_all = "snake_case")] {
+ #[tabled(inline)]
+ #[tabled(rename_all = "snake_case")]
+ VariantName1 {
+ some_field_1: u8,
+ some_field_2: i32,
+ }
+ VariantName2(String)
+ K
+ },
+ headers: ["some_field_1", "some_field_2", "variant_name2", "k"],
+ tests:
+ );
+
+ test_enum!(
+ rename_all_variant_inherited_inside_struct_enum_overridden_by_rename,
+ t: #[tabled(rename_all = "snake_case")] {
+ #[tabled(inline, rename_all = "snake_case")]
+ VariantName1 {
+ #[tabled(rename = "f1")]
+ some_field_1: u8,
+ #[tabled(rename = "f2")]
+ some_field_2: i32,
+ }
+ VariantName2(String)
+ K
+ },
+ headers: ["f1", "f2", "variant_name2", "k"],
+ tests:
+ );
+
+ test_enum!(
+ rename_all_variant_inherited_inside_struct_override_by_rename_all_enum,
+ t: #[tabled(rename_all = "snake_case")] {
+ #[tabled(rename_all = "snake_case", inline)]
+ VariantName1 {
+ #[tabled(rename_all = "UPPERCASE")]
+ some_field_1: u8,
+ #[tabled(rename_all = "CamelCase")]
+ some_field_2: i32,
+ }
+ VariantName2(String)
+ K
+ },
+ headers: ["SOMEFIELD1", "someField2", "variant_name2", "k"],
+ tests:
+ );
+
+ test_enum!(
+ inline_enum_as_whole,
+ t: #[tabled(inline)] {
+ AbsdEgh { a: u8, b: i32 }
+ B(String)
+ K
+ },
+ headers: ["TestType"],
+ tests:
+ AbsdEgh { a: 0, b: 0 } => ["AbsdEgh"],
+ B(String::new()) => ["B"],
+ K => ["K"],
+ );
+
+ test_enum!(
+ inline_enum_as_whole_and_rename,
+ t:
+ #[tabled(inline, rename_all = "snake_case")]
+ {
+ AbsdEgh { a: u8, b: i32 }
+ B(String)
+ K
+ },
+ headers: ["TestType"],
+ tests:
+ AbsdEgh { a: 0, b: 0 } => ["absd_egh"],
+ B(String::new()) => ["b"],
+ K => ["k"],
+ );
+
+ test_enum!(
+ inline_enum_as_whole_and_rename_inner,
+ t: #[tabled(inline)] {
+ #[tabled(rename_all = "snake_case")]
+ AbsdEgh { a: u8, b: i32 }
+ #[tabled(rename_all = "lowercase")]
+ B(String)
+ K
+ },
+ headers: ["TestType"],
+ tests:
+ AbsdEgh { a: 0, b: 0 } => ["absd_egh"],
+ B(String::new()) => ["b"],
+ K => ["K"],
+ );
+
+ test_enum!(
+ inline_enum_name,
+ t: #[tabled(inline("A struct name"))] {
+ AbsdEgh { a: u8, b: i32 }
+ B(String)
+ K
+ },
+ headers: ["A struct name"],
+ tests:
+ AbsdEgh { a: 0, b: 0 } => ["AbsdEgh"],
+ B(String::new()) => ["B"],
+ K => ["K"],
+ );
+
+ test_enum!(
+ enum_display_with_variant,
+ t: {
+ #[tabled(display_with = "display_variant1")]
+ AbsdEgh { a: u8, b: i32 }
+ #[tabled(display_with = "display_variant2::<200>")]
+ B(String)
+ #[tabled(display_with = "some::bar::display_variant1")]
+ K
+ },
+ pre: {
+ fn display_variant1() -> &'static str {
+ "Hello World"
+ }
+
+ fn display_variant2<const VAL: usize>() -> String {
+ format!("asd {VAL}")
+ }
+
+ pub mod some {
+ pub mod bar {
+ pub fn display_variant1() -> &'static str {
+ "Hello World 123"
+ }
+ }
+ }
+ }
+ headers: ["AbsdEgh", "B", "K"],
+ tests:
+ AbsdEgh { a: 0, b: 0 } => ["Hello World", "", ""],
+ B(String::new()) => ["", "asd 200", ""],
+ K => ["", "", "Hello World 123"],
+ );
+
+ test_enum!(
+ enum_display_with_self_variant,
+ t: {
+ #[tabled(display_with("display_variant1", self))]
+ AbsdEgh { a: u8, b: i32 }
+ #[tabled(display_with("display_variant2::<200, _>", self))]
+ B(String)
+ #[tabled(display_with("some::bar::display_variant1", self))]
+ K
+ },
+ pre: {
+ fn display_variant1<D>(_: &D) -> &'static str {
+ "Hello World"
+ }
+
+ fn display_variant2<const VAL: usize, D>(_: &D) -> String {
+ format!("asd {VAL}")
+ }
+
+ pub mod some {
+ pub mod bar {
+ pub fn display_variant1<D>(_: &D) -> &'static str {
+ "Hello World 123"
+ }
+ }
+ }
+ }
+ headers: ["AbsdEgh", "B", "K"],
+ tests:
+ AbsdEgh { a: 0, b: 0 } => ["Hello World", "", ""],
+ B(String::new()) => ["", "asd 200", ""],
+ K => ["", "", "Hello World 123"],
+ );
+
+ test_enum!(
+ enum_display_with_arguments,
+ t: {
+ #[tabled(display_with("display1", 1, 2, self))]
+ AbsdEgh { a: u8, b: i32 }
+ #[tabled(display_with("display2::<200>", "Hello World"))]
+ B(String)
+ #[tabled(display_with("display1", 100, 200, self))]
+ K
+ },
+ pre: {
+ fn display1<D>(val: usize, val2: usize, _: &D) -> String {
+ format!("{val} {val2}")
+ }
+
+ fn display2<const VAL: usize>(val: &str) -> String {
+ format!("asd {VAL} {val}")
+ }
+ }
+ headers: ["AbsdEgh", "B", "K"],
+ tests:
+ AbsdEgh { a: 0, b: 0 } => ["1 2", "", ""],
+ B(String::new()) => ["", "asd 200 Hello World", ""],
+ K => ["", "", "100 200"],
+ );
+
+ test_enum!(order_0, t: { #[tabled(order = 0)] V1(u8) V2(u8) V3(u8) }, headers: ["V1", "V2", "V3"], tests: V1(0) => ["+", "", ""], V2(0) => ["", "+", ""], V3(0) => ["", "", "+"],);
+ test_enum!(order_1, t: { #[tabled(order = 1)] V1(u8) V2(u8) V3(u8) }, headers: ["V2", "V1", "V3"], tests: V1(0) => ["", "+", ""], V2(0) => ["+", "", ""], V3(0) => ["", "", "+"],);
+ test_enum!(order_2, t: { #[tabled(order = 2)] V1(u8) V2(u8) V3(u8) }, headers: ["V2", "V3", "V1"], tests: V1(0) => ["", "", "+"], V2(0) => ["+", "", ""], V3(0) => ["", "+", ""],);
+ test_enum!(order_3, t: { V1(u8) #[tabled(order = 0)] V2(u8) V3(u8) }, headers: ["V2", "V1", "V3"], tests: V1(0) => ["", "+", ""], V2(0) => ["+", "", ""], V3(0) => ["", "", "+"],);
+ test_enum!(order_4, t: { V1(u8) #[tabled(order = 1)] V2(u8) V3(u8) }, headers: ["V1", "V2", "V3"], tests: V1(0) => ["+", "", ""], V2(0) => ["", "+", ""], V3(0) => ["", "", "+"],);
+ test_enum!(order_5, t: { V1(u8) #[tabled(order = 2)] V2(u8) V3(u8) }, headers: ["V1", "V3", "V2"], tests: V1(0) => ["+", "", ""], V2(0) => ["", "", "+"], V3(0) => ["", "+", ""],);
+ test_enum!(order_6, t: { V1(u8) V2(u8) #[tabled(order = 0)] V3(u8) }, headers: ["V3", "V1", "V2"], tests: V1(0) => ["", "+", ""], V2(0) => ["", "", "+"], V3(0) => ["+", "", ""],);
+ test_enum!(order_7, t: { V1(u8) V2(u8) #[tabled(order = 1)] V3(u8) }, headers: ["V1", "V3", "V2"], tests: V1(0) => ["+", "", ""], V2(0) => ["", "", "+"], V3(0) => ["", "+", ""],);
+ test_enum!(order_8, t: { V1(u8) V2(u8) #[tabled(order = 2)] V3(u8) }, headers: ["V1", "V2", "V3"], tests: V1(0) => ["+", "", ""], V2(0) => ["", "+", ""], V3(0) => ["", "", "+"],);
+ test_enum!(order_9, t: { #[tabled(order = 2)] V1(u8) V2(u8) #[tabled(order = 0)] V3(u8) }, headers: ["V3", "V2", "V1"], tests: V1(0) => ["", "", "+"], V2(0) => ["", "+", ""], V3(0) => ["+", "", ""],);
+ test_enum!(order_10, t: { #[tabled(order = 2)] V1(u8) V2(u8) #[tabled(order = 1)] V3(u8) }, headers: ["V2", "V3", "V1"], tests: V1(0) => ["", "", "+"], V2(0) => ["+", "", ""], V3(0) => ["", "+", ""],);
+ test_enum!(order_11, t: { #[tabled(order = 2)] V1(u8) #[tabled(order = 2)] V2(u8) #[tabled(order = 1)] V3(u8) }, headers: ["V1", "V3", "V2"], tests: V1(0) => ["+", "", ""], V2(0) => ["", "", "+"], V3(0) => ["", "+", ""],);
+ test_enum!(order_12, t: { #[tabled(order = 2)] V1(u8) #[tabled(order = 1)] V2(u8) #[tabled(order = 0)] V3(u8) }, headers: ["V3", "V2", "V1"], tests: V1(0) => ["", "", "+"], V2(0) => ["", "+", ""], V3(0) => ["+", "", ""],);
+ test_enum!(order_13, t: { #[tabled(order = 0)] V1(u8) #[tabled(order = 0)] V2(u8) #[tabled(order = 0)] V3(u8) }, headers: ["V3", "V1", "V2"], tests: V1(0) => ["", "+", ""], V2(0) => ["", "", "+"], V3(0) => ["+", "", ""],);
+ test_enum!(order_14, t: { #[tabled(order = 1)] V1(u8) #[tabled(order = 1)] V2(u8) #[tabled(order = 1)] V3(u8) }, headers: ["V1", "V3", "V2"], tests: V1(0) => ["+", "", ""], V2(0) => ["", "", "+"], V3(0) => ["", "+", ""],);
+ test_enum!(order_15, t: { #[tabled(order = 2)] V1(u8) #[tabled(order = 2)] V2(u8) #[tabled(order = 2)] V3(u8) }, headers: ["V1", "V2", "V3"], tests: V1(0) => ["+", "", ""], V2(0) => ["", "+", ""], V3(0) => ["", "", "+"],);
+
+ test_enum!(order_0_inlined, t: #[tabled(inline)] { #[tabled(order = 1)] V1(u8) V2(u8) V3(u8) }, headers: ["TestType"], tests: V1(0) => ["V1"], V2(0) => ["V2"], V3(0) => ["V3"],);
+}
+
+mod unit {
+ use super::*;
+
+ #[test]
+ fn basic() {
+ #[derive(Tabled)]
+ struct St;
+ let st = St;
+
+ assert!(st.fields().is_empty());
+ assert!(St::headers().is_empty());
+ assert_eq!(St::LENGTH, 0);
+ }
+}
+
+mod structure {
+ use super::*;
+
+ test_struct!(empty, t: { } init: { } expected: [], []);
+ test_struct!(general, t: { f1: u8, f2: sstr } init: { f1: 0, f2: "v2" } expected: ["f1", "f2"], ["0", "v2"]);
+ test_struct!(rename, t: { #[tabled(rename = "field 1")] f1: u8, #[tabled(rename = "field 2")] f2: sstr } init: { f1: 0, f2: "v2" } expected: ["field 1", "field 2"], ["0", "v2"]);
+ test_struct!(skip, t: { #[tabled(skip)] f1: u8, #[tabled(rename = "field 2", skip)] f2: sstr, f3: sstr } init: { f1: 0, f2: "v2", f3: "123" } expected: ["f3"], ["123"]);
+ test_struct!(skip_true, t: { #[tabled(skip = true)] f1: u8, #[tabled(rename = "field 2", skip = true)] f2: sstr, f3: sstr } init: { f1: 0, f2: "v2", f3: "123" } expected: ["f3"], ["123"]);
+ test_struct!(
+ inline,
+ t: {
+ #[tabled(inline = true)]
+ id: u8,
+ name: sstr,
+ #[tabled(inline)]
+ ed: Education
+ }
+ pre: {
+ #[derive(Tabled)]
+ struct Education { uni: sstr, graduated: bool }
+ }
+ init: { id: 0, name: "Maxim", ed: Education { uni: "BNTU", graduated: true }}
+ expected: ["u8", "name","uni","graduated"], ["0", "Maxim", "BNTU", "true"]
+ );
+ test_struct!(
+ inline_with_prefix,
+ t: {
+ #[tabled(rename = "it's an ignored option", inline)]
+ id: u8,
+ name: sstr,
+ #[tabled(inline("education::"))]
+ ed: Education,
+ }
+ pre: {
+ #[derive(Tabled)]
+ struct Education { uni: sstr, graduated: bool }
+ }
+ init: { id: 0, name: "Maxim", ed: Education { uni: "BNTU", graduated: true }}
+ expected: ["u8", "name","education::uni","education::graduated"], ["0", "Maxim", "BNTU", "true"]
+ );
+ test_struct!(
+ display_with,
+ t: {
+ f1: u8,
+ #[tabled(display_with = "display_option")]
+ f2: Option<sstr>,
+ }
+ pre: {
+ fn display_option(o: &Option<sstr>) -> String {
+ match o {
+ Some(s) => format!("some {s}"),
+ None => "none".to_string(),
+ }
+ }
+ }
+ init: { f1: 0, f2: Some("v2") }
+ expected: ["f1", "f2"], ["0", "some v2"]
+ );
+ test_struct!(
+ display_with_args,
+ t: {
+ f1: u8,
+ #[tabled(display_with("display_option", 1, 2, 3))]
+ f2: Option<sstr>,
+ }
+ pre: {
+ fn display_option(v1: usize, v2: usize, v3: usize) -> String {
+ format!("{v1} {v2} {v3}")
+ }
+ }
+ init: { f1: 0, f2: Some("v2") }
+ expected: ["f1", "f2"], ["0", "1 2 3"]
+ );
+ test_struct!(
+ display_with_self_static_method,
+ t: {
+ f1: u8,
+ #[tabled(display_with = "Self::display_option")]
+ f2: Option<sstr>,
+ }
+ pre: {
+ impl TestType {
+ fn display_option(o: &Option<sstr>) -> String {
+ match o {
+ Some(s) => format!("some {s}"),
+ None => "none".to_string(),
+ }
+ }
+ }
+ }
+ init: { f1: 0, f2: Some("v2") }
+ expected: ["f1", "f2"], ["0", "some v2"]
+ );
+ test_struct!(
+ display_with_self_static_method_2,
+ t: {
+ f1: u8,
+ #[tabled(display_with("Self::display_option", self))]
+ f2: Option<sstr>,
+ }
+ pre: {
+ impl TestType {
+ fn display_option(o: &TestType) -> String {
+ match o.f2 {
+ Some(s) => format!("some {s}"),
+ None => "none".to_string(),
+ }
+ }
+ }
+ }
+ init: { f1: 0, f2: Some("v2") }
+ expected: ["f1", "f2"], ["0", "some v2"]
+ );
+ test_struct!(
+ display_with_self_2_self_static_method_2,
+ t: {
+ f1: u8,
+ #[tabled(display_with("Self::display_option", self))]
+ f2: Option<sstr>,
+ }
+ pre: {
+ impl TestType {
+ fn display_option(&self) -> String {
+ match self.f2 {
+ Some(s) => format!("some {s}"),
+ None => "none".to_string(),
+ }
+ }
+ }
+ }
+ init: { f1: 0, f2: Some("v2") }
+ expected: ["f1", "f2"], ["0", "some v2"]
+ );
+ test_struct!(
+ display_with_self_2_self_static_method,
+ t: {
+ f1: u8,
+ #[tabled(display_with("display_option", self))]
+ f2: Option<sstr>,
+ }
+ pre: {
+ fn display_option(o: &TestType) -> String {
+ match o.f2 {
+ Some(s) => format!("some {s}"),
+ None => "none".to_string(),
+ }
+ }
+ }
+ init: { f1: 0, f2: Some("v2") }
+ expected: ["f1", "f2"], ["0", "some v2"]
+ );
+ test_struct!(order_0, t: { #[tabled(order = 0)] f0: u8, f1: u8, f2: u8 } init: { f0: 0, f1: 1, f2: 2 } expected: ["f0", "f1", "f2"], ["0", "1", "2"]);
+ test_struct!(order_1, t: { #[tabled(order = 1)] f0: u8, f1: u8, f2: u8 } init: { f0: 0, f1: 1, f2: 2 } expected: ["f1", "f0", "f2"], ["1", "0", "2"]);
+ test_struct!(order_2, t: { #[tabled(order = 2)] f0: u8, f1: u8, f2: u8 } init: { f0: 0, f1: 1, f2: 2 } expected: ["f1", "f2", "f0"], ["1", "2", "0"]);
+ test_struct!(order_3, t: { f0: u8, #[tabled(order = 0)] f1: u8, f2: u8 } init: { f0: 0, f1: 1, f2: 2 } expected: ["f1", "f0", "f2"], ["1", "0", "2"]);
+ test_struct!(order_4, t: { f0: u8, #[tabled(order = 1)] f1: u8, f2: u8 } init: { f0: 0, f1: 1, f2: 2 } expected: ["f0", "f1", "f2"], ["0", "1", "2"]);
+ test_struct!(order_5, t: { f0: u8, #[tabled(order = 2)] f1: u8, f2: u8 } init: { f0: 0, f1: 1, f2: 2 } expected: ["f0", "f2", "f1"], ["0", "2", "1"]);
+ test_struct!(order_6, t: { f0: u8, f1: u8, #[tabled(order = 0)] f2: u8 } init: { f0: 0, f1: 1, f2: 2 } expected: ["f2", "f0", "f1"], ["2", "0", "1"]);
+ test_struct!(order_7, t: { f0: u8, f1: u8, #[tabled(order = 1)] f2: u8 } init: { f0: 0, f1: 1, f2: 2 } expected: ["f0", "f2", "f1"], ["0", "2", "1"]);
+ test_struct!(order_8, t: { f0: u8, f1: u8, #[tabled(order = 2)] f2: u8 } init: { f0: 0, f1: 1, f2: 2 } expected: ["f0", "f1", "f2"], ["0", "1", "2"]);
+ test_struct!(order_9, t: { #[tabled(order = 2)] f0: u8, f1: u8, #[tabled(order = 0)] f2: u8 } init: { f0: 0, f1: 1, f2: 2 } expected: ["f2", "f1", "f0"], ["2", "1", "0"]);
+ test_struct!(order_10, t: { #[tabled(order = 2)] f0: u8, #[tabled(order = 1)] f1: u8, f2: u8 } init: { f0: 0, f1: 1, f2: 2 } expected: ["f2", "f1", "f0"], ["2", "1", "0"]);
+ test_struct!(order_11, t: { #[tabled(order = 2)] f0: u8, #[tabled(order = 2)] f1: u8, #[tabled(order = 1)] f2: u8 } init: { f0: 0, f1: 1, f2: 2 } expected: ["f0", "f2", "f1"], ["0", "2", "1"]);
+ test_struct!(order_12, t: { #[tabled(order = 2)] f0: u8, #[tabled(order = 1)] f1: u8, #[tabled(order = 0)] f2: u8 } init: { f0: 0, f1: 1, f2: 2 } expected: ["f2", "f1", "f0"], ["2", "1", "0"]);
+
+ test_struct!(
+ rename_all,
+ t: #[tabled(rename_all = "UPPERCASE")] { f1: u8, f2: sstr }
+ init: { f1: 0, f2: "v2" }
+ expected: ["F1", "F2"], ["0", "v2"]
+ );
+ test_struct!(
+ rename_all_override_in_field_by_rename,
+ t: #[tabled(rename_all = "UPPERCASE")] { #[tabled(rename = "213213")] f1: u8, f2: sstr }
+ init: { f1: 0, f2: "v2" }
+ expected: ["213213", "F2"], ["0", "v2"]
+ );
+ test_struct!(
+ rename_all_override_in_field_by_rename_all,
+ t: #[tabled(rename_all = "UPPERCASE")] { #[tabled(rename_all = "lowercase")] f1: u8, f2: sstr }
+ init: { f1: 0, f2: "v2" }
+ expected: ["f1", "F2"], ["0", "v2"]
+ );
+ test_struct!(
+ rename_all_field,
+ t: { #[tabled(rename_all = "lowercase")] f1: u8, #[tabled(rename_all = "UPPERCASE")] f2: sstr }
+ init: { f1: 0, f2: "v2" }
+ expected: ["f1", "F2"], ["0", "v2"]
+ );
+ test_struct!(
+ rename_all_field_overridden_by_rename,
+ t: { #[tabled(rename_all = "lowercase", rename = "Hello")] f1: u8, #[tabled(rename_all = "UPPERCASE")] f2: sstr }
+ init: { f1: 0, f2: "v2" }
+ expected: ["Hello", "F2"], ["0", "v2"]
+ );
+
+ // #[test]
+ // fn order_compile_fail_when_order_is_bigger_then_count_fields() {
+ // #[derive(Tabled)]
+ // struct St {
+ // #[tabled(order = 3)]
+ // f0: u8,
+ // f1: u8,
+ // f2: u8,
+ // }
+ // }
+}
+
+test_tuple!(skipped_fields_not_implement_display_tuple, t: { #[tabled(skip)] () sstr }, init: { () "123" }, expected: ["1"], ["123"],);
+test_struct!(skipped_fields_not_implement_display_struct, t: { #[tabled(skip)] _unit: (), s: sstr } init: { _unit: (), s: "123" } expected: ["s"], ["123"],);
+test_struct!(
+ skipped_fields_not_implement_display_struct_in_inline,
+ t: { s: sstr, #[tabled(inline)] f: S1 }
+ pre: {
+ #[derive(Tabled)]
+ struct S1 {
+ #[tabled(skip)]
+ _unit: (),
+ s: sstr,
+ }
+ }
+ init: { s: "123", f: S1 { _unit: (), s: "..." } }
+ expected: ["s", "s"], ["123", "..."],
+);
+test_enum!(
+ skipped_fields_not_implement_display_enum,
+ t: {
+ #[tabled(inline("A::"))]
+ A {
+ name: sstr
+ }
+ #[tabled(inline("B::"))]
+ B {
+ issue: usize,
+ name: sstr,
+ #[tabled(skip)]
+ _gem: (),
+ }
+ #[tabled(inline("C::"))]
+ C(usize, #[tabled(skip)] (), sstr)
+ D
+ },
+ headers: ["A::name", "B::issue", "B::name", "C::0", "C::2", "D"],
+ tests:
+ A { name: "nrdxp" } => ["nrdxp", "", "", "", "", ""],
+ B { _gem: (), issue: 32, name: "nrdxp" } => ["", "32", "nrdxp", "", "", ""],
+ C(32, (), "nrdxp") => ["", "", "", "32", "nrdxp", ""],
+ D => ["", "", "", "", "", "+"],
+);
+
+test_struct!(
+ ignore_display_with_when_used_with_inline,
+ t: { f1: sstr, f2: sstr, #[tabled(display_with = "print", inline)] f3: usize }
+ init: { f1: "123", f2: "456", f3: 789 }
+ expected: ["f1", "f2", "usize"], ["123", "456", "789"],
+);
+test_struct!(
+ ignore_display_with_when_used_with_inline_2,
+ t: { f1: sstr, f2: sstr, #[tabled(display_with = "print", )] #[tabled(inline)] f3: usize }
+ init: { f1: "123", f2: "456", f3: 789 }
+ expected: ["f1", "f2", "usize"], ["123", "456", "789"],
+);
+test_struct!(
+ display_with_and_rename,
+ t: { f1: sstr, f2: sstr, #[tabled(display_with = "print", rename = "asd")] f3: usize }
+ pre: { #[allow(dead_code)] fn print<T>(_: T) -> String { String::new() } }
+ init: { f1: "123", f2: "456", f3: 789 }
+ expected: ["f1", "f2", "asd"], ["123", "456", ""],
+);
+test_struct!(
+ display_with_and_rename_2,
+ t: { f1: sstr, f2: sstr, #[tabled(display_with = "print")] #[tabled(rename = "asd")] f3: usize }
+ pre: { #[allow(dead_code)] fn print<T>(_: T) -> String { String::new() } }
+ init: { f1: "123", f2: "456", f3: 789 }
+ expected: ["f1", "f2", "asd"], ["123", "456", ""],
+);
+test_struct!(
+ display_with_and_rename_all,
+ t: { f1: sstr, f2: sstr, #[tabled(display_with = "print", rename_all = "UPPERCASE")] f3: usize }
+ pre: { #[allow(dead_code)] fn print<T>(_: T) -> String { String::new() } }
+ init: { f1: "123", f2: "456", f3: 789 }
+ expected: ["f1", "f2", "F3"], ["123", "456", ""],
+);
+
+#[test]
+fn rename_all_variants() {
+ macro_rules! test_case {
+ ( $name:ident, $case:expr ) => {
+ #[derive(Tabled)]
+ #[tabled(rename_all = $case)]
+ struct $name {
+ field: usize,
+ }
+ };
+ }
+
+ test_case!(S1, "UPPERCASE");
+ test_case!(S2, "lowercase");
+ test_case!(S3, "camelCase");
+ test_case!(S4, "PascalCase");
+ test_case!(S5, "snake_case");
+ test_case!(S6, "SCREAMING_SNAKE_CASE");
+ test_case!(S7, "kebab-case");
+ test_case!(S8, "verbatimcase");
+}
+
+// #[test]
+// fn wrong_rename_all_panic_when_used_as_not_first() {
+// #[derive(Tabled)]
+// #[tabled(rename_all = "UPPERCASE")]
+// #[tabled(rename_all = "some wrong case")]
+// struct Struct1 {
+// field: usize,
+// }
+
+// let st = Struct1 { field: 789 };
+
+// assert_eq!(Struct1::headers(), vec!["FIELD"],);
+// assert_eq!(st.fields(), vec!["789"]);
+
+// #[derive(Tabled)]
+// #[tabled(rename_all = "UPPERCASE", rename_all = "some wrong case")]
+// struct Struct2 {
+// field: usize,
+// }
+
+// let st = Struct2 { field: 789 };
+
+// assert_eq!(Struct1::headers(), vec!["FIELD"],);
+// assert_eq!(st.fields(), vec!["789"]);
+// }
+
+#[test]
+fn rename_all_gets_last_value() {
+ #[derive(Tabled)]
+ #[tabled(rename_all = "UPPERCASE")]
+ #[tabled(rename_all = "PascalCase")]
+ struct Struct1 {
+ field: usize,
+ }
+
+ let st = Struct1 { field: 789 };
+
+ assert_eq!(Struct1::headers(), vec!["Field"],);
+ assert_eq!(st.fields(), vec!["789"]);
+
+ #[derive(Tabled)]
+ #[tabled(rename_all = "UPPERCASE", rename_all = "PascalCase")]
+ struct Struct2 {
+ field: usize,
+ }
+
+ let st = Struct2 { field: 789 };
+
+ assert_eq!(Struct1::headers(), vec!["Field"],);
+ assert_eq!(st.fields(), vec!["789"]);
+}
+
+#[test]
+fn test_order_skip_usage() {
+ #[derive(Tabled, Default)]
+ pub struct Example {
+ #[tabled(skip)]
+ #[allow(dead_code)]
+ id: usize,
+ name: String,
+ #[tabled(order = 0)]
+ details: String,
+ }
+
+ #[derive(Tabled, Default)]
+ pub struct Example2 {
+ #[tabled(skip)]
+ #[allow(dead_code)]
+ id: usize,
+ name: String,
+ #[tabled(order = 1)]
+ details: String,
+ }
+
+ assert_eq!(Example::headers(), vec!["details", "name"],);
+ assert_eq!(Example::default().fields(), vec!["", ""]);
+}
+
+#[test]
+fn test_skip_enum_0() {
+ #[allow(dead_code)]
+ #[derive(Tabled)]
+ enum Letters {
+ Vowels {
+ character: char,
+ lang: u8,
+ },
+ Consonant(char),
+ #[tabled(skip)]
+ Digit,
+ }
+
+ assert_eq!(Letters::headers(), vec!["Vowels", "Consonant"]);
+ assert_eq!(Letters::Consonant('c').fields(), vec!["", "+"]);
+ assert_eq!(Letters::Digit.fields(), vec!["", ""]);
+}
+
+mod __ {
+ #[test]
+ fn dont_import_the_trait() {
+ #[derive(tabled::Tabled)]
+ struct __;
+ }
+}
diff --git a/vendor/tabled/tests/derive/mod.rs b/vendor/tabled/tests/derive/mod.rs
new file mode 100644
index 000000000..d0d4b359f
--- /dev/null
+++ b/vendor/tabled/tests/derive/mod.rs
@@ -0,0 +1 @@
+mod derive_test;
diff --git a/vendor/tabled/tests/macros/col_row_test.rs b/vendor/tabled/tests/macros/col_row_test.rs
new file mode 100644
index 000000000..e6d70edf0
--- /dev/null
+++ b/vendor/tabled/tests/macros/col_row_test.rs
@@ -0,0 +1,264 @@
+#![cfg(feature = "macros")]
+#![cfg(feature = "std")]
+
+use tabled::{
+ col, row,
+ settings::{format::Format, object::Segment, Alignment, Modify, Padding},
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+test_table!(
+ row_pair_test,
+ row!(
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]")))),
+ Matrix::new(4, 4)
+ .with(Modify::new(Segment::all()).with(Alignment::right()))
+ .with(Modify::new(Segment::all()).with(Padding::new(1, 1, 1, 1)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("({s})")))),
+ ),
+ "+--------------------------------------------------------+-------------------------------------------------------------+"
+ "| +-------+--------------+--------------+--------------+ | +-----+------------+------------+------------+------------+ |"
+ "| | [N] | [column 0] | [column 1] | [column 2] | | | | | | | | |"
+ "| +-------+--------------+--------------+--------------+ | | (N) | (column 0) | (column 1) | (column 2) | (column 3) | |"
+ "| | [0] | [0-0] | [0-1] | [0-2] | | | | | | | | |"
+ "| +-------+--------------+--------------+--------------+ | +-----+------------+------------+------------+------------+ |"
+ "| | [1] | [1-0] | [1-1] | [1-2] | | | | | | | | |"
+ "| +-------+--------------+--------------+--------------+ | | (0) | (0-0) | (0-1) | (0-2) | (0-3) | |"
+ "| | [2] | [2-0] | [2-1] | [2-2] | | | | | | | | |"
+ "| +-------+--------------+--------------+--------------+ | +-----+------------+------------+------------+------------+ |"
+ "| | | | | | | | |"
+ "| | | (1) | (1-0) | (1-1) | (1-2) | (1-3) | |"
+ "| | | | | | | | |"
+ "| | +-----+------------+------------+------------+------------+ |"
+ "| | | | | | | | |"
+ "| | | (2) | (2-0) | (2-1) | (2-2) | (2-3) | |"
+ "| | | | | | | | |"
+ "| | +-----+------------+------------+------------+------------+ |"
+ "| | | | | | | | |"
+ "| | | (3) | (3-0) | (3-1) | (3-2) | (3-3) | |"
+ "| | | | | | | | |"
+ "| | +-----+------------+------------+------------+------------+ |"
+ "+--------------------------------------------------------+-------------------------------------------------------------+"
+);
+
+test_table!(
+ col_pair_test,
+ col!(
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]")))),
+ Matrix::new(4, 4)
+ .with(Modify::new(Segment::all()).with(Alignment::right()))
+ .with(Modify::new(Segment::all()).with(Padding::new(1, 1, 1, 1)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("({s})")))),
+ ),
+ "+-------------------------------------------------------------+"
+ "| +-------+--------------+--------------+--------------+ |"
+ "| | [N] | [column 0] | [column 1] | [column 2] | |"
+ "| +-------+--------------+--------------+--------------+ |"
+ "| | [0] | [0-0] | [0-1] | [0-2] | |"
+ "| +-------+--------------+--------------+--------------+ |"
+ "| | [1] | [1-0] | [1-1] | [1-2] | |"
+ "| +-------+--------------+--------------+--------------+ |"
+ "| | [2] | [2-0] | [2-1] | [2-2] | |"
+ "| +-------+--------------+--------------+--------------+ |"
+ "+-------------------------------------------------------------+"
+ "| +-----+------------+------------+------------+------------+ |"
+ "| | | | | | | |"
+ "| | (N) | (column 0) | (column 1) | (column 2) | (column 3) | |"
+ "| | | | | | | |"
+ "| +-----+------------+------------+------------+------------+ |"
+ "| | | | | | | |"
+ "| | (0) | (0-0) | (0-1) | (0-2) | (0-3) | |"
+ "| | | | | | | |"
+ "| +-----+------------+------------+------------+------------+ |"
+ "| | | | | | | |"
+ "| | (1) | (1-0) | (1-1) | (1-2) | (1-3) | |"
+ "| | | | | | | |"
+ "| +-----+------------+------------+------------+------------+ |"
+ "| | | | | | | |"
+ "| | (2) | (2-0) | (2-1) | (2-2) | (2-3) | |"
+ "| | | | | | | |"
+ "| +-----+------------+------------+------------+------------+ |"
+ "| | | | | | | |"
+ "| | (3) | (3-0) | (3-1) | (3-2) | (3-3) | |"
+ "| | | | | | | |"
+ "| +-----+------------+------------+------------+------------+ |"
+ "+-------------------------------------------------------------+"
+);
+
+test_table!(
+ row_duplication_test,
+ row!(
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]"))));
+ 3
+ ),
+ "+--------------------------------------------------------+--------------------------------------------------------+--------------------------------------------------------+"
+ "| +-------+--------------+--------------+--------------+ | +-------+--------------+--------------+--------------+ | +-------+--------------+--------------+--------------+ |"
+ "| | [N] | [column 0] | [column 1] | [column 2] | | | [N] | [column 0] | [column 1] | [column 2] | | | [N] | [column 0] | [column 1] | [column 2] | |"
+ "| +-------+--------------+--------------+--------------+ | +-------+--------------+--------------+--------------+ | +-------+--------------+--------------+--------------+ |"
+ "| | [0] | [0-0] | [0-1] | [0-2] | | | [0] | [0-0] | [0-1] | [0-2] | | | [0] | [0-0] | [0-1] | [0-2] | |"
+ "| +-------+--------------+--------------+--------------+ | +-------+--------------+--------------+--------------+ | +-------+--------------+--------------+--------------+ |"
+ "| | [1] | [1-0] | [1-1] | [1-2] | | | [1] | [1-0] | [1-1] | [1-2] | | | [1] | [1-0] | [1-1] | [1-2] | |"
+ "| +-------+--------------+--------------+--------------+ | +-------+--------------+--------------+--------------+ | +-------+--------------+--------------+--------------+ |"
+ "| | [2] | [2-0] | [2-1] | [2-2] | | | [2] | [2-0] | [2-1] | [2-2] | | | [2] | [2-0] | [2-1] | [2-2] | |"
+ "| +-------+--------------+--------------+--------------+ | +-------+--------------+--------------+--------------+ | +-------+--------------+--------------+--------------+ |"
+ "+--------------------------------------------------------+--------------------------------------------------------+--------------------------------------------------------+"
+);
+
+test_table!(
+ col_and_rows_test,
+ col!(
+ row!(
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]")))),
+ Matrix::new(4, 4)
+ .with(Modify::new(Segment::all()).with(Alignment::right()))
+ .with(Modify::new(Segment::all()).with(Padding::new(1, 1, 1, 1)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("({s})")))),
+ ),
+ Matrix::new(3, 5)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(2, 2, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]")))),
+ ),
+ "+--------------------------------------------------------------------------------------------------------------------------+"
+ "| +--------------------------------------------------------+-------------------------------------------------------------+ |"
+ "| | +-------+--------------+--------------+--------------+ | +-----+------------+------------+------------+------------+ | |"
+ "| | | [N] | [column 0] | [column 1] | [column 2] | | | | | | | | | |"
+ "| | +-------+--------------+--------------+--------------+ | | (N) | (column 0) | (column 1) | (column 2) | (column 3) | | |"
+ "| | | [0] | [0-0] | [0-1] | [0-2] | | | | | | | | | |"
+ "| | +-------+--------------+--------------+--------------+ | +-----+------------+------------+------------+------------+ | |"
+ "| | | [1] | [1-0] | [1-1] | [1-2] | | | | | | | | | |"
+ "| | +-------+--------------+--------------+--------------+ | | (0) | (0-0) | (0-1) | (0-2) | (0-3) | | |"
+ "| | | [2] | [2-0] | [2-1] | [2-2] | | | | | | | | | |"
+ "| | +-------+--------------+--------------+--------------+ | +-----+------------+------------+------------+------------+ | |"
+ "| | | | | | | | | | |"
+ "| | | | (1) | (1-0) | (1-1) | (1-2) | (1-3) | | |"
+ "| | | | | | | | | | |"
+ "| | | +-----+------------+------------+------------+------------+ | |"
+ "| | | | | | | | | | |"
+ "| | | | (2) | (2-0) | (2-1) | (2-2) | (2-3) | | |"
+ "| | | | | | | | | | |"
+ "| | | +-----+------------+------------+------------+------------+ | |"
+ "| | | | | | | | | | |"
+ "| | | | (3) | (3-0) | (3-1) | (3-2) | (3-3) | | |"
+ "| | | | | | | | | | |"
+ "| | | +-----+------------+------------+------------+------------+ | |"
+ "| +--------------------------------------------------------+-------------------------------------------------------------+ |"
+ "+--------------------------------------------------------------------------------------------------------------------------+"
+ "| +-------+--------------+--------------+--------------+--------------+--------------+ |"
+ "| | [N] | [column 0] | [column 1] | [column 2] | [column 3] | [column 4] | |"
+ "| +-------+--------------+--------------+--------------+--------------+--------------+ |"
+ "| | [0] | [0-0] | [0-1] | [0-2] | [0-3] | [0-4] | |"
+ "| +-------+--------------+--------------+--------------+--------------+--------------+ |"
+ "| | [1] | [1-0] | [1-1] | [1-2] | [1-3] | [1-4] | |"
+ "| +-------+--------------+--------------+--------------+--------------+--------------+ |"
+ "| | [2] | [2-0] | [2-1] | [2-2] | [2-3] | [2-4] | |"
+ "| +-------+--------------+--------------+--------------+--------------+--------------+ |"
+ "+--------------------------------------------------------------------------------------------------------------------------+"
+);
+
+test_table!(
+ row_and_col_test,
+ row!(
+ col!(
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]")))),
+ Matrix::new(4, 4)
+ .with(Modify::new(Segment::all()).with(Alignment::right()))
+ .with(Modify::new(Segment::all()).with(Padding::new(1, 1, 1, 1)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("({s})")))),
+ ),
+ Matrix::new(3, 5)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(2, 2, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]")))),
+ ),
+ "+-----------------------------------------------------------------+--------------------------------------------------------------------------------------+"
+ "| +-------------------------------------------------------------+ | +-------+--------------+--------------+--------------+--------------+--------------+ |"
+ "| | +-------+--------------+--------------+--------------+ | | | [N] | [column 0] | [column 1] | [column 2] | [column 3] | [column 4] | |"
+ "| | | [N] | [column 0] | [column 1] | [column 2] | | | +-------+--------------+--------------+--------------+--------------+--------------+ |"
+ "| | +-------+--------------+--------------+--------------+ | | | [0] | [0-0] | [0-1] | [0-2] | [0-3] | [0-4] | |"
+ "| | | [0] | [0-0] | [0-1] | [0-2] | | | +-------+--------------+--------------+--------------+--------------+--------------+ |"
+ "| | +-------+--------------+--------------+--------------+ | | | [1] | [1-0] | [1-1] | [1-2] | [1-3] | [1-4] | |"
+ "| | | [1] | [1-0] | [1-1] | [1-2] | | | +-------+--------------+--------------+--------------+--------------+--------------+ |"
+ "| | +-------+--------------+--------------+--------------+ | | | [2] | [2-0] | [2-1] | [2-2] | [2-3] | [2-4] | |"
+ "| | | [2] | [2-0] | [2-1] | [2-2] | | | +-------+--------------+--------------+--------------+--------------+--------------+ |"
+ "| | +-------+--------------+--------------+--------------+ | | |"
+ "| +-------------------------------------------------------------+ | |"
+ "| | +-----+------------+------------+------------+------------+ | | |"
+ "| | | | | | | | | | |"
+ "| | | (N) | (column 0) | (column 1) | (column 2) | (column 3) | | | |"
+ "| | | | | | | | | | |"
+ "| | +-----+------------+------------+------------+------------+ | | |"
+ "| | | | | | | | | | |"
+ "| | | (0) | (0-0) | (0-1) | (0-2) | (0-3) | | | |"
+ "| | | | | | | | | | |"
+ "| | +-----+------------+------------+------------+------------+ | | |"
+ "| | | | | | | | | | |"
+ "| | | (1) | (1-0) | (1-1) | (1-2) | (1-3) | | | |"
+ "| | | | | | | | | | |"
+ "| | +-----+------------+------------+------------+------------+ | | |"
+ "| | | | | | | | | | |"
+ "| | | (2) | (2-0) | (2-1) | (2-2) | (2-3) | | | |"
+ "| | | | | | | | | | |"
+ "| | +-----+------------+------------+------------+------------+ | | |"
+ "| | | | | | | | | | |"
+ "| | | (3) | (3-0) | (3-1) | (3-2) | (3-3) | | | |"
+ "| | | | | | | | | | |"
+ "| | +-----+------------+------------+------------+------------+ | | |"
+ "| +-------------------------------------------------------------+ | |"
+ "+-----------------------------------------------------------------+--------------------------------------------------------------------------------------+"
+);
+
+test_table!(
+ row_str_test,
+ row!("hello", "world"),
+ "+-------+-------+"
+ "| hello | world |"
+ "+-------+-------+"
+);
+
+test_table!(
+ row_str_duplication_test,
+ row!("duplicate me"; 5),
+ "+--------------+--------------+--------------+--------------+--------------+"
+ "| duplicate me | duplicate me | duplicate me | duplicate me | duplicate me |"
+ "+--------------+--------------+--------------+--------------+--------------+"
+);
+
+test_table!(
+ row_display_mixed_test,
+ row!("str", "string".to_string(), 55, false),
+ "+-----+--------+----+-------+"
+ "| str | string | 55 | false |"
+ "+-----+--------+----+-------+"
+);
+
+test_table!(
+ col_display_mixed_test,
+ col!("str", "string".to_string(), 55, false),
+ "+--------+"
+ "| str |"
+ "+--------+"
+ "| string |"
+ "+--------+"
+ "| 55 |"
+ "+--------+"
+ "| false |"
+ "+--------+"
+);
diff --git a/vendor/tabled/tests/macros/mod.rs b/vendor/tabled/tests/macros/mod.rs
new file mode 100644
index 000000000..be52241e9
--- /dev/null
+++ b/vendor/tabled/tests/macros/mod.rs
@@ -0,0 +1 @@
+mod col_row_test;
diff --git a/vendor/tabled/tests/main.rs b/vendor/tabled/tests/main.rs
new file mode 100644
index 000000000..35004b906
--- /dev/null
+++ b/vendor/tabled/tests/main.rs
@@ -0,0 +1,7 @@
+mod core;
+mod derive;
+mod macros;
+mod settings;
+
+#[cfg(feature = "std")]
+mod matrix;
diff --git a/vendor/tabled/tests/matrix/matrix.rs b/vendor/tabled/tests/matrix/matrix.rs
new file mode 100644
index 000000000..48ba55f9b
--- /dev/null
+++ b/vendor/tabled/tests/matrix/matrix.rs
@@ -0,0 +1,161 @@
+use std::{
+ fmt::{self, Display},
+ iter::FromIterator,
+ string::ToString,
+};
+
+use tabled::{
+ grid::config::ColoredConfig,
+ grid::dimension::CompleteDimensionVecRecords,
+ grid::records::vec_records::{CellInfo, VecRecords},
+ settings::{object::Segment, Alignment, Modify, TableOption},
+ Table, Tabled,
+};
+
+use super::matrix_list::MatrixList;
+
+/// A helper table factory.
+///
+/// It uses center alignment by default, because it's more complex and may spot more issues.
+#[derive(Debug, Clone)]
+pub struct Matrix {
+ data: Vec<Vec<String>>,
+ size: (usize, usize),
+}
+
+impl Matrix {
+ pub fn empty() -> Self {
+ Self {
+ data: vec![],
+ size: (0, 0),
+ }
+ }
+
+ pub fn with_no_frame(rows: usize, columns: usize) -> Self {
+ Self {
+ data: create_matrix(rows, columns),
+ size: (rows, columns),
+ }
+ }
+
+ pub fn new(rows: usize, columns: usize) -> Self {
+ Self::with_no_frame(rows, columns)
+ .with_header()
+ .with_index()
+ }
+
+ pub fn vec(rows: usize, columns: usize) -> Vec<Vec<String>> {
+ Self::new(rows, columns).to_vec()
+ }
+
+ pub fn table(rows: usize, columns: usize) -> Table {
+ Self::new(rows, columns).to_table()
+ }
+
+ pub fn list<const ROWS: usize, const COLUMNS: usize>() -> Vec<MatrixList<COLUMNS, true>> {
+ create_list::<ROWS, COLUMNS>()
+ }
+
+ pub fn iter<I, T>(iter: I) -> Table
+ where
+ I: IntoIterator<Item = T>,
+ T: Tabled,
+ {
+ let mut table = tabled::Table::new(iter);
+ table.with(Modify::new(Segment::all()).with(Alignment::center()));
+ table
+ }
+
+ pub fn with_index(mut self) -> Self {
+ set_index(&mut self.data);
+ self
+ }
+
+ pub fn with_header(mut self) -> Self {
+ set_header(&mut self.data, self.size.1);
+ self
+ }
+
+ pub fn insert<V: ToString>(mut self, pos: tabled::grid::config::Position, value: V) -> Self {
+ self.data[pos.0][pos.1] = value.to_string();
+ self
+ }
+
+ pub fn to_table(&self) -> Table {
+ let mut table = tabled::Table::from_iter(self.data.clone());
+ table.with(Modify::new(Segment::all()).with(Alignment::center()));
+ table
+ }
+
+ pub fn to_vec(&self) -> Vec<Vec<String>> {
+ self.data.clone()
+ }
+
+ pub fn with<O>(self, opt: O) -> Table
+ where
+ O: TableOption<
+ VecRecords<CellInfo<String>>,
+ CompleteDimensionVecRecords<'static>,
+ ColoredConfig,
+ >,
+ {
+ let mut table = self.to_table();
+ table.with(opt);
+ table
+ }
+}
+
+impl Display for Matrix {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.clone().to_table().fmt(f)
+ }
+}
+
+fn create_matrix(rows: usize, columns: usize) -> Vec<Vec<String>> {
+ let mut arr = Vec::with_capacity(rows);
+ for row in 0..rows {
+ let mut data = Vec::with_capacity(columns);
+ for column in 0..columns {
+ let text = format!("{row}-{column}");
+ data.push(text);
+ }
+
+ arr.push(data);
+ }
+
+ arr
+}
+
+fn set_header(data: &mut Vec<Vec<String>>, columns: usize) {
+ data.insert(
+ 0,
+ (0..columns)
+ .map(|n| format!("column {n}"))
+ .collect::<Vec<_>>(),
+ );
+}
+
+fn set_index(data: &mut [Vec<String>]) {
+ if data.is_empty() {
+ return;
+ }
+
+ data[0].insert(0, "N".to_owned());
+
+ for (n, row) in data.iter_mut().skip(1).enumerate() {
+ row.insert(0, n.to_string());
+ }
+}
+
+fn create_list<const ROWS: usize, const COLUMNS: usize>() -> Vec<MatrixList<COLUMNS, true>> {
+ let mut arr = Vec::with_capacity(ROWS);
+ for row in 0..ROWS {
+ let data = (0..COLUMNS)
+ .map(|column| format!("{row}-{column}"))
+ .collect::<Vec<_>>();
+ let list = MatrixList::with_index(row, data);
+ arr.push(list);
+ }
+
+ arr
+}
diff --git a/vendor/tabled/tests/matrix/matrix_list.rs b/vendor/tabled/tests/matrix/matrix_list.rs
new file mode 100644
index 000000000..9f58400ca
--- /dev/null
+++ b/vendor/tabled/tests/matrix/matrix_list.rs
@@ -0,0 +1,58 @@
+use std::{
+ borrow::Cow,
+ iter::once,
+ ops::{Index, IndexMut},
+};
+
+use tabled::Tabled;
+
+#[derive(Debug)]
+pub struct MatrixList<const N: usize, const INDEX: bool> {
+ data: Vec<String>,
+}
+
+impl<const N: usize> MatrixList<N, false> {
+ #[allow(dead_code)]
+ pub fn new(data: Vec<String>) -> Self {
+ Self { data }
+ }
+}
+
+impl<const N: usize> MatrixList<N, true> {
+ pub fn with_index(index: usize, mut data: Vec<String>) -> Self {
+ assert_eq!(data.len(), N);
+ data.insert(0, index.to_string());
+ Self { data }
+ }
+}
+
+impl<const N: usize, const INDEX: bool> Index<usize> for MatrixList<N, INDEX> {
+ type Output = String;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self.data[index]
+ }
+}
+
+impl<const N: usize, const INDEX: bool> IndexMut<usize> for MatrixList<N, INDEX> {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+ &mut self.data[index]
+ }
+}
+
+impl<const N: usize, const INDEX: bool> Tabled for MatrixList<N, INDEX> {
+ const LENGTH: usize = N + 1;
+
+ fn fields(&self) -> Vec<Cow<'_, str>> {
+ self.data.iter().cloned().map(Cow::Owned).collect()
+ }
+
+ fn headers() -> Vec<Cow<'static, str>> {
+ let header = (0..N).map(|n| format!("column {n}"));
+
+ match INDEX {
+ true => once("N".to_owned()).chain(header).map(Cow::Owned).collect(),
+ false => header.map(Cow::Owned).collect(),
+ }
+ }
+}
diff --git a/vendor/tabled/tests/matrix/mod.rs b/vendor/tabled/tests/matrix/mod.rs
new file mode 100644
index 000000000..d2d42cfc5
--- /dev/null
+++ b/vendor/tabled/tests/matrix/mod.rs
@@ -0,0 +1,6 @@
+#[allow(clippy::module_inception)]
+mod matrix;
+mod matrix_list;
+
+pub use matrix::Matrix;
+pub use matrix_list::MatrixList;
diff --git a/vendor/tabled/tests/settings/alignment_test.rs b/vendor/tabled/tests/settings/alignment_test.rs
new file mode 100644
index 000000000..0a8145ae2
--- /dev/null
+++ b/vendor/tabled/tests/settings/alignment_test.rs
@@ -0,0 +1,138 @@
+#![cfg(feature = "std")]
+
+use tabled::settings::{
+ locator::ByColumnName,
+ object::{Columns, Rows, Segment},
+ Alignment, Modify, Padding, Style,
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+test_table!(
+ full_alignment,
+ Matrix::new(3, 3).with(Style::psql()).with(Modify::new(Segment::all()).with(Alignment::left())),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ head_and_data_alignment,
+ Matrix::new(3, 3)
+ .with(Modify::new(Rows::first()).with(Alignment::left()))
+ .with(Modify::new(Rows::new(1..)).with(Alignment::right())),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ full_alignment_multiline,
+ Matrix::new(3, 3).insert((3, 2), "https://\nwww\n.\nredhat\n.com\n/en")
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left())),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | https:// | 2-2 "
+ " | | www | "
+ " | | . | "
+ " | | redhat | "
+ " | | .com | "
+ " | | /en | "
+);
+
+test_table!(
+ vertical_alignment_test,
+ Matrix::new(3, 3)
+ .insert((2, 2), "E\nnde\navou\nros")
+ .insert((3, 2), "Red\nHat")
+ .insert((3, 3), "https://\nwww\n.\nredhat\n.com\n/en")
+ .with(Style::psql())
+ .with(Modify::new(Columns::new(1..)).with(Alignment::bottom())),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | | E | "
+ " | | nde | "
+ " | | avou | "
+ " | 1-0 | ros | 1-2 "
+ " 2 | | | https:// "
+ " | | | www "
+ " | | | . "
+ " | | | redhat "
+ " | | Red | .com "
+ " | 2-0 | Hat | /en "
+);
+
+test_table!(
+ alignment_doesnt_change_padding,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 0, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Alignment::left())),
+ " N| column 0| column 1| column 2"
+ "----+-----------+-----------+-----------"
+ " 0| 0-0 | 0-1 | 0-2 "
+ " 1| 1-0 | 1-1 | 1-2 "
+ " 2| 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ alignment_global,
+ Matrix::new(3, 3).with(Style::psql()).with(Alignment::right()),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ padding_by_column_name,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(ByColumnName::new("column 0")).with(Padding::new(3, 3, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Alignment::center())),
+ " N | column 0 | column 1 | column 2 "
+ "---+--------------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ padding_by_column_name_not_first_row,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(ByColumnName::new("0-2")).with(Padding::new(3, 3, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Alignment::center())),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ padding_by_column_name_not_existing,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(ByColumnName::new("column 01123123")).with(Padding::new(3, 3, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Alignment::center())),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
diff --git a/vendor/tabled/tests/settings/color_test.rs b/vendor/tabled/tests/settings/color_test.rs
new file mode 100644
index 000000000..b81c63ae3
--- /dev/null
+++ b/vendor/tabled/tests/settings/color_test.rs
@@ -0,0 +1,34 @@
+#![cfg(feature = "std")]
+
+use tabled::settings::{Color, Modify};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+test_table!(
+ color_global,
+ Matrix::new(3, 3).with(Color::FG_MAGENTA),
+ "+---+----------+----------+----------+"
+ "| \u{1b}[35mN\u{1b}[39m | \u{1b}[35mcolumn 0\u{1b}[39m | \u{1b}[35mcolumn 1\u{1b}[39m | \u{1b}[35mcolumn 2\u{1b}[39m |"
+ "+---+----------+----------+----------+"
+ "| \u{1b}[35m0\u{1b}[39m | \u{1b}[35m0-0\u{1b}[39m | \u{1b}[35m0-1\u{1b}[39m | \u{1b}[35m0-2\u{1b}[39m |"
+ "+---+----------+----------+----------+"
+ "| \u{1b}[35m1\u{1b}[39m | \u{1b}[35m1-0\u{1b}[39m | \u{1b}[35m1-1\u{1b}[39m | \u{1b}[35m1-2\u{1b}[39m |"
+ "+---+----------+----------+----------+"
+ "| \u{1b}[35m2\u{1b}[39m | \u{1b}[35m2-0\u{1b}[39m | \u{1b}[35m2-1\u{1b}[39m | \u{1b}[35m2-2\u{1b}[39m |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ color_cell,
+ Matrix::new(3, 3).with(Modify::new((0, 0)).with(Color::BG_BRIGHT_BLACK)),
+ "+---+----------+----------+----------+"
+ "| \u{1b}[100mN\u{1b}[49m | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
diff --git a/vendor/tabled/tests/settings/colorization.rs b/vendor/tabled/tests/settings/colorization.rs
new file mode 100644
index 000000000..49911c494
--- /dev/null
+++ b/vendor/tabled/tests/settings/colorization.rs
@@ -0,0 +1,64 @@
+#![cfg(feature = "std")]
+
+use tabled::settings::{
+ object::{Cell, Object},
+ themes::Colorization,
+ Color,
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+test_table!(
+ chess_2x3,
+ Matrix::new(2, 3).with(Colorization::chess(color1(), color2())),
+ "+---+----------+----------+----------+\n|\u{1b}[41m \u{1b}[49m\u{1b}[41mN\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41mcolumn 0\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106mcolumn 1\u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41mcolumn 2\u{1b}[49m\u{1b}[41m \u{1b}[49m|\n+---+----------+----------+----------+\n|\u{1b}[106m \u{1b}[49m\u{1b}[106m0\u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m0-0\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m0-1\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m0-2\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\n+---+----------+----------+----------+\n|\u{1b}[41m \u{1b}[49m\u{1b}[41m1\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m1-0\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m1-1\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m1-2\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m|\n+---+----------+----------+----------+"
+);
+
+test_table!(
+ chess_3x3,
+ Matrix::new(3, 3).with(Colorization::chess(color1(), color2())),
+ "+---+----------+----------+----------+\n|\u{1b}[41m \u{1b}[49m\u{1b}[41mN\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106mcolumn 0\u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41mcolumn 1\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106mcolumn 2\u{1b}[49m\u{1b}[106m \u{1b}[49m|\n+---+----------+----------+----------+\n|\u{1b}[106m \u{1b}[49m\u{1b}[106m0\u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m0-0\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m0-1\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m0-2\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m|\n+---+----------+----------+----------+\n|\u{1b}[41m \u{1b}[49m\u{1b}[41m1\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m1-0\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m1-1\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m1-2\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\n+---+----------+----------+----------+\n|\u{1b}[106m \u{1b}[49m\u{1b}[106m2\u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m2-0\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m2-1\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m2-2\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m|\n+---+----------+----------+----------+"
+);
+
+test_table!(
+ rows,
+ Matrix::new(2, 3).with(Colorization::rows([color1(), color2(), color3()])),
+ "+---+----------+----------+----------+\n|\u{1b}[41m \u{1b}[49m\u{1b}[41mN\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41mcolumn 0\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41mcolumn 1\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41mcolumn 2\u{1b}[49m\u{1b}[41m \u{1b}[49m|\n+---+----------+----------+----------+\n|\u{1b}[106m \u{1b}[49m\u{1b}[106m0\u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m0-0\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m0-1\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m0-2\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\n+---+----------+----------+----------+\n|\u{1b}[1m \u{1b}[22m\u{1b}[1m1\u{1b}[22m\u{1b}[1m \u{1b}[22m|\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m1-0\u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m|\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m1-1\u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m|\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m1-2\u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m|\n+---+----------+----------+----------+"
+);
+
+test_table!(
+ columns,
+ Matrix::new(2, 3).with(Colorization::columns([color1(), color2(), color3()])),
+ "+---+----------+----------+----------+\n|\u{1b}[41m \u{1b}[49m\u{1b}[41mN\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106mcolumn 0\u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[1m \u{1b}[22m\u{1b}[1mcolumn 1\u{1b}[22m\u{1b}[1m \u{1b}[22m|\u{1b}[41m \u{1b}[49m\u{1b}[41mcolumn 2\u{1b}[49m\u{1b}[41m \u{1b}[49m|\n+---+----------+----------+----------+\n|\u{1b}[41m \u{1b}[49m\u{1b}[41m0\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m0-0\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m0-1\u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m|\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m0-2\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m|\n+---+----------+----------+----------+\n|\u{1b}[41m \u{1b}[49m\u{1b}[41m1\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m1-0\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m1-1\u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m|\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m1-2\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m|\n+---+----------+----------+----------+"
+);
+
+test_table!(
+ by_row,
+ Matrix::new(2, 3).with(Colorization::by_row([color1(), color2(), color3()])),
+ "+---+----------+----------+----------+\n|\u{1b}[41m \u{1b}[49m\u{1b}[41mN\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106mcolumn 0\u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[1m \u{1b}[22m\u{1b}[1mcolumn 1\u{1b}[22m\u{1b}[1m \u{1b}[22m|\u{1b}[41m \u{1b}[49m\u{1b}[41mcolumn 2\u{1b}[49m\u{1b}[41m \u{1b}[49m|\n+---+----------+----------+----------+\n|\u{1b}[106m \u{1b}[49m\u{1b}[106m0\u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m0-0\u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m|\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m0-1\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m0-2\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\n+---+----------+----------+----------+\n|\u{1b}[1m \u{1b}[22m\u{1b}[1m1\u{1b}[22m\u{1b}[1m \u{1b}[22m|\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m1-0\u{1b}[49m\u{1b}[41m \u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m1-1\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m1-2\u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m|\n+---+----------+----------+----------+"
+);
+
+test_table!(
+ by_column,
+ Matrix::new(2, 3).with(Colorization::by_column([color1(), color2(), color3()])),
+ "+---+----------+----------+----------+\n|\u{1b}[41m \u{1b}[49m\u{1b}[41mN\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41mcolumn 0\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41mcolumn 1\u{1b}[49m\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\u{1b}[41mcolumn 2\u{1b}[49m\u{1b}[41m \u{1b}[49m|\n+---+----------+----------+----------+\n|\u{1b}[106m \u{1b}[49m\u{1b}[106m0\u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m0-0\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m0-1\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m0-2\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m|\n+---+----------+----------+----------+\n|\u{1b}[1m \u{1b}[22m\u{1b}[1m1\u{1b}[22m\u{1b}[1m \u{1b}[22m|\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m1-0\u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m|\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m1-1\u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m|\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m1-2\u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m|\n+---+----------+----------+----------+"
+);
+
+test_table!(
+ exact,
+ Matrix::new(2, 3).with(Colorization::exact([color1(), color2(), color3()], Cell::new(0, 0).and(Cell::new(1, 1)).and(Cell::new(2, 2)))),
+ "+---+----------+----------+----------+\n|\u{1b}[41m \u{1b}[49m\u{1b}[41mN\u{1b}[49m\u{1b}[41m \u{1b}[49m| column 0 | column 1 | column 2 |\n+---+----------+----------+----------+\n| 0 |\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m0-0\u{1b}[49m\u{1b}[106m \u{1b}[49m\u{1b}[106m \u{1b}[49m| 0-1 | 0-2 |\n+---+----------+----------+----------+\n| 1 | 1-0 |\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m1-1\u{1b}[22m\u{1b}[1m \u{1b}[22m\u{1b}[1m \u{1b}[22m| 1-2 |\n+---+----------+----------+----------+"
+);
+
+fn color1() -> Color {
+ Color::BG_RED
+}
+
+fn color2() -> Color {
+ Color::BG_BRIGHT_CYAN
+}
+
+fn color3() -> Color {
+ Color::BOLD
+}
diff --git a/vendor/tabled/tests/settings/column_names_test.rs b/vendor/tabled/tests/settings/column_names_test.rs
new file mode 100644
index 000000000..c2b23f1e3
--- /dev/null
+++ b/vendor/tabled/tests/settings/column_names_test.rs
@@ -0,0 +1,270 @@
+#![cfg(feature = "std")]
+
+use tabled::{
+ grid::config::AlignmentHorizontal,
+ settings::{themes::ColumnNames, Color},
+ Table,
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+test_table!(
+ new,
+ Matrix::new(3, 3).with(ColumnNames::new(["1", "2", "3", "4"])),
+ "+1--+2---------+3---------+4---------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ new_more_names_then_columns,
+ Matrix::new(3, 3).with(ColumnNames::new(["1", "2", "3", "4", "5", "6", "7"])),
+ "+1--+2---------+3---------+4---------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ new_less_names_then_columns,
+ Matrix::new(3, 3).with(ColumnNames::new(["1", "2"])),
+ "+1--+2---------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ new_empty,
+ Matrix::new(3, 3).with(ColumnNames::new([""; 0])),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ default,
+ Matrix::new(3, 3).with(ColumnNames::default()),
+ "+N--+column 0+column 1+column 2+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+--------+--------+--------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+--------+--------+--------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+--------+--------+--------+"
+);
+
+test_table!(
+ alignment_left,
+ Table::new([("Hello", "World"), ("and", "looooong\nword")])
+ .with(ColumnNames::default().set_alignment(AlignmentHorizontal::Left)),
+ "+&str---+&str------+"
+ "| Hello | World |"
+ "+-------+----------+"
+ "| and | looooong |"
+ "| | word |"
+ "+-------+----------+"
+);
+
+test_table!(
+ alignment_right,
+ Table::new([("Hello", "World"), ("and", "looooong\nword")])
+ .with(ColumnNames::default().set_alignment(AlignmentHorizontal::Right)),
+ "+---&str+------&str+"
+ "| Hello | World |"
+ "+-------+----------+"
+ "| and | looooong |"
+ "| | word |"
+ "+-------+----------+"
+);
+
+test_table!(
+ alignment_center,
+ Table::new([("Hello", "World"), ("and", "looooong\nword")])
+ .with(ColumnNames::default().set_alignment(AlignmentHorizontal::Center)),
+ "+-&str--+---&str---+"
+ "| Hello | World |"
+ "+-------+----------+"
+ "| and | looooong |"
+ "| | word |"
+ "+-------+----------+"
+);
+
+test_table!(
+ alignment_left_offset,
+ Table::new([("Hello", "World"), ("and", "looooong\nword")])
+ .with(ColumnNames::default().set_alignment(AlignmentHorizontal::Left).set_offset(1)),
+ "+-&str--+-&str-----+"
+ "| Hello | World |"
+ "+-------+----------+"
+ "| and | looooong |"
+ "| | word |"
+ "+-------+----------+"
+);
+
+test_table!(
+ alignment_left_offset_big,
+ Table::new([("Hello", "World"), ("and", "looooong\nword")])
+ .with(ColumnNames::default().set_alignment(AlignmentHorizontal::Left).set_offset(30)),
+ "+------------------------------&str+------------------------------&str+"
+ "| Hello | World |"
+ "+----------------------------------+----------------------------------+"
+ "| and | looooong |"
+ "| | word |"
+ "+----------------------------------+----------------------------------+"
+);
+
+test_table!(
+ alignment_left_offset_0,
+ Table::new([("Hello", "World"), ("and", "looooong\nword")])
+ .with(ColumnNames::default().set_alignment(AlignmentHorizontal::Left).set_offset(0)),
+ "+&str---+&str------+"
+ "| Hello | World |"
+ "+-------+----------+"
+ "| and | looooong |"
+ "| | word |"
+ "+-------+----------+"
+);
+
+test_table!(
+ alignment_right_offset,
+ Table::new([("Hello", "World"), ("and", "looooong\nword")])
+ .with(ColumnNames::default().set_alignment(AlignmentHorizontal::Right).set_offset(1)),
+ "+--&str-+-----&str-+"
+ "| Hello | World |"
+ "+-------+----------+"
+ "| and | looooong |"
+ "| | word |"
+ "+-------+----------+"
+);
+
+test_table!(
+ alignment_right_offset_big,
+ Table::new([("Hello", "World"), ("and", "looooong\nword")])
+ .with(ColumnNames::default().set_alignment(AlignmentHorizontal::Right).set_offset(30)),
+ "+&str------------------------------+&str------------------------------+"
+ "| Hello | World |"
+ "+----------------------------------+----------------------------------+"
+ "| and | looooong |"
+ "| | word |"
+ "+----------------------------------+----------------------------------+"
+);
+
+test_table!(
+ alignment_right_offset_0,
+ Table::new([("Hello", "World"), ("and", "looooong\nword")])
+ .with(ColumnNames::default().set_alignment(AlignmentHorizontal::Right).set_offset(0)),
+ "+---&str+------&str+"
+ "| Hello | World |"
+ "+-------+----------+"
+ "| and | looooong |"
+ "| | word |"
+ "+-------+----------+"
+);
+
+test_table!(
+ set_line,
+ Matrix::new(3, 3).with(ColumnNames::default().set_line(1)),
+ "+---+--------+--------+--------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+N--+column 0+column 1+column 2+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+--------+--------+--------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+--------+--------+--------+"
+);
+
+test_table!(
+ set_line_max_out,
+ Matrix::new(3, 3).with(ColumnNames::default().set_line(100)),
+ "+---+--------+--------+--------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+--------+--------+--------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+--------+--------+--------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+--------+--------+--------+"
+);
+
+test_table!(
+ set_line_0,
+ Matrix::new(3, 3).with(ColumnNames::default().set_line(0)),
+ "+N--+column 0+column 1+column 2+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+--------+--------+--------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+--------+--------+--------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+--------+--------+--------+"
+);
+
+test_table!(
+ set_colors_some_some,
+ Table::new([("Hello", "World"), ("and", "looooong\nword")])
+ .with(ColumnNames::default().set_colors([Some(Color::BG_BLACK), Some(Color::BG_BLUE)])),
+ "+\u{1b}[40m&\u{1b}[49m\u{1b}[40ms\u{1b}[49m\u{1b}[40mt\u{1b}[49m\u{1b}[40mr\u{1b}[49m---+\u{1b}[44m&\u{1b}[49m\u{1b}[44ms\u{1b}[49m\u{1b}[44mt\u{1b}[49m\u{1b}[44mr\u{1b}[49m------+"
+ "| Hello | World |"
+ "+-------+----------+"
+ "| and | looooong |"
+ "| | word |"
+ "+-------+----------+"
+);
+
+test_table!(
+ set_colors_none_some,
+ Table::new([("Hello", "World"), ("and", "looooong\nword")])
+ .with(ColumnNames::default().set_colors([None, Some(Color::BG_BLUE)])),
+ "+&str---+\u{1b}[44m&\u{1b}[49m\u{1b}[44ms\u{1b}[49m\u{1b}[44mt\u{1b}[49m\u{1b}[44mr\u{1b}[49m------+"
+ "| Hello | World |"
+ "+-------+----------+"
+ "| and | looooong |"
+ "| | word |"
+ "+-------+----------+"
+);
+
+test_table!(
+ set_colors_none_none,
+ Table::new([("Hello", "World"), ("and", "looooong\nword")])
+ .with(ColumnNames::default().set_colors([None, None])),
+ "+&str---+&str------+"
+ "| Hello | World |"
+ "+-------+----------+"
+ "| and | looooong |"
+ "| | word |"
+ "+-------+----------+"
+);
+
+test_table!(
+ set_colors_empty,
+ Table::new([("Hello", "World"), ("and", "looooong\nword")])
+ .with(ColumnNames::default().set_colors([None; 0])),
+ "+&str---+&str------+"
+ "| Hello | World |"
+ "+-------+----------+"
+ "| and | looooong |"
+ "| | word |"
+ "+-------+----------+"
+);
diff --git a/vendor/tabled/tests/settings/concat_test.rs b/vendor/tabled/tests/settings/concat_test.rs
new file mode 100644
index 000000000..b988dcad9
--- /dev/null
+++ b/vendor/tabled/tests/settings/concat_test.rs
@@ -0,0 +1,140 @@
+#![cfg(feature = "std")]
+
+use tabled::settings::{Concat, Style};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+test_table!(
+ join_vertical_0,
+ Matrix::new(2, 3).insert((1, 0), "123").with(Style::psql())
+ .with(Concat::vertical(Matrix::new(2, 3).to_table()))
+ .to_string(),
+ " N | column 0 | column 1 | column 2 "
+ "-----+----------+----------+----------"
+ " 123 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " N | column 0 | column 1 | column 2 "
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+);
+
+test_table!(
+ join_vertical_1,
+ Matrix::new(2, 3)
+ .with(Concat::vertical(Matrix::new(2, 3).insert((1, 0), "123").with(Style::psql()))),
+ "+-----+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+-----+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+-----+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+-----+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+-----+----------+----------+----------+"
+ "| 123 | 0-0 | 0-1 | 0-2 |"
+ "+-----+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+-----+----------+----------+----------+"
+);
+
+test_table!(
+ join_horizontal_0,
+ {
+ let mut table1 = Matrix::table(2, 3);
+ table1.with(Style::ascii());
+ let mut table2 = Matrix::table(2, 3);
+ table2.with(Style::psql());
+ table2.with(Concat::horizontal(table1)).to_string()
+ },
+ " N | column 0 | column 1 | column 2 | N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------+---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 | 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 | 1 | 1-0 | 1-1 | 1-2 "
+);
+
+test_table!(
+ join_horizontal_1,
+ {
+ let mut table1 = Matrix::table(2, 3);
+ table1.with(Style::ascii());
+ let mut table2 = Matrix::table(2, 3);
+ table2.with(Style::psql());
+ table1.with(Concat::horizontal(table2)).to_string()
+ },
+ "+---+----------+----------+----------+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 | N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+---+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 | 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 | 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+---+----------+----------+----------+"
+);
+
+test_table!(
+ join_vertical_different_size,
+ {
+ let mut table1 = Matrix::table(2, 2);
+ table1.with(Style::psql());
+ let mut table2 = Matrix::table(2, 3);
+ table2.with(Style::psql());
+ table1.with(Concat::vertical(table2)).to_string()
+ },
+ " N | column 0 | column 1 | "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | "
+ " 1 | 1-0 | 1-1 | "
+ " N | column 0 | column 1 | column 2 "
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+);
+
+test_table!(
+ join_horizontal_different_size,
+ {
+ let mut table1 = Matrix::table(2, 3);
+ table1.with(Style::psql());
+ let mut table2 = Matrix::table(3, 3);
+ table2.with(Style::psql());
+ table1.with(Concat::horizontal(table2)).to_string()
+ },
+ " N | column 0 | column 1 | column 2 | N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------+---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 | 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 | 1 | 1-0 | 1-1 | 1-2 "
+ " | | | | 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ join_horizontal_with_not_default_empty_string,
+ {
+ let mut table1 = Matrix::table(2, 3);
+ table1.with(Style::psql());
+ let mut table2 = Matrix::table(3, 3);
+ table2.with(Style::psql());
+ table1.with(Concat::horizontal(table2).default_cell("NaN")).to_string()
+ },
+ " N | column 0 | column 1 | column 2 | N | column 0 | column 1 | column 2 "
+ "-----+----------+----------+----------+---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 | 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 | 1 | 1-0 | 1-1 | 1-2 "
+ " NaN | NaN | NaN | NaN | 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ join_vertical_with_not_default_empty_string,
+ {
+ let mut table1 = Matrix::table(2, 2);
+ table1.with(Style::psql());
+ let mut table2 = Matrix::table(2, 3);
+ table2.with(Style::psql());
+ table1.with(Concat::vertical(table2).default_cell("NaN")).to_string()
+ },
+ " N | column 0 | column 1 | NaN "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | NaN "
+ " 1 | 1-0 | 1-1 | NaN "
+ " N | column 0 | column 1 | column 2 "
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+);
diff --git a/vendor/tabled/tests/settings/disable_test.rs b/vendor/tabled/tests/settings/disable_test.rs
new file mode 100644
index 000000000..cf50acf84
--- /dev/null
+++ b/vendor/tabled/tests/settings/disable_test.rs
@@ -0,0 +1,83 @@
+#![cfg(feature = "std")]
+
+use tabled::settings::{
+ locator::ByColumnName,
+ object::{Columns, Rows, Segment},
+ style::{HorizontalLine, Style},
+ Alignment, Disable, Modify,
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+test_table!(
+ disable_rows,
+ Matrix::new(3, 3).with(Disable::row(Rows::new(1..=2))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ disable_header,
+ Matrix::new(3, 3).with(Style::psql()).with(Disable::row(Rows::first())),
+ " 0 | 0-0 | 0-1 | 0-2 "
+ "---+-----+-----+-----"
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ disable_all_table_via_rows,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Disable::row(Columns::new(..))),
+ ""
+);
+
+test_table!(
+ disable_header_with_new_styling,
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Disable::row(Rows::new(..1)))
+ .with(Style::modern().remove_horizontal().horizontals([HorizontalLine::new(1, Style::modern().get_horizontal())])),
+ "┌───┬─────┬─────┬─────┐"
+ "│ 0 │ 0-0 │ 0-1 │ 0-2 │"
+ "├───┼─────┼─────┼─────┤"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+ "└───┴─────┴─────┴─────┘"
+);
+
+test_table!(
+ disable_columns,
+ Matrix::new(3, 3).with(Style::psql()).with(Disable::column(Columns::first())),
+ " column 0 | column 1 | column 2 "
+ "----------+----------+----------"
+ " 0-0 | 0-1 | 0-2 "
+ " 1-0 | 1-1 | 1-2 "
+ " 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ disable_column_by_name,
+ Matrix::new(3, 3).with(Style::psql())
+ .with(Disable::column(ByColumnName::new("column 1")))
+ .with(Disable::column(ByColumnName::new("column 3"))),
+ " N | column 0 | column 2 "
+ "---+----------+----------"
+ " 0 | 0-0 | 0-2 "
+ " 1 | 1-0 | 1-2 "
+ " 2 | 2-0 | 2-2 "
+);
+
+test_table!(
+ disable_all_table_via_columns,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Disable::column(Columns::new(..))),
+ ""
+);
diff --git a/vendor/tabled/tests/settings/duplicate_test.rs b/vendor/tabled/tests/settings/duplicate_test.rs
new file mode 100644
index 000000000..6d15fbfac
--- /dev/null
+++ b/vendor/tabled/tests/settings/duplicate_test.rs
@@ -0,0 +1,189 @@
+#![cfg(feature = "std")]
+
+use tabled::settings::{
+ object::{Cell, Columns, Rows, Segment},
+ Dup,
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+test_table!(
+ dup_cell_to_cell,
+ Matrix::new(3, 3).with(Dup::new(Cell::new(0, 0), Cell::new(0, 1))),
+ "+----------+----------+----------+----------+"
+ "| column 0 | column 0 | column 1 | column 2 |"
+ "+----------+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+----------+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+----------+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+----------+----------+----------+----------+"
+);
+
+test_table!(
+ dup_cell_to_column,
+ Matrix::new(3, 3).with(Dup::new(Columns::single(1), Cell::new(0, 0))),
+ "+---+---+----------+----------+"
+ "| N | N | column 1 | column 2 |"
+ "+---+---+----------+----------+"
+ "| 0 | N | 0-1 | 0-2 |"
+ "+---+---+----------+----------+"
+ "| 1 | N | 1-1 | 1-2 |"
+ "+---+---+----------+----------+"
+ "| 2 | N | 2-1 | 2-2 |"
+ "+---+---+----------+----------+"
+);
+
+test_table!(
+ dup_row_to_row_single,
+ Matrix::new(3, 3).with(Dup::new(Rows::single(1), Rows::single(0))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ dup_row_to_row_single_to_many,
+ Matrix::new(3, 3).with(Dup::new(Rows::new(1..3), Rows::single(0))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ dup_row_to_row_single_to_all,
+ Matrix::new(3, 3).with(Dup::new(Rows::new(1..), Rows::single(0))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ dup_row_to_column_single,
+ Matrix::new(3, 3).with(Dup::new(Columns::single(1), Rows::single(0))),
+ "+---+----------+----------+----------+"
+ "| N | N | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | column 0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | column 1 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | column 2 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ dup_column_to_row_single,
+ Matrix::new(3, 3).with(Dup::new(Columns::single(1), Columns::single(0))),
+ "+---+---+----------+----------+"
+ "| N | N | column 1 | column 2 |"
+ "+---+---+----------+----------+"
+ "| 0 | 0 | 0-1 | 0-2 |"
+ "+---+---+----------+----------+"
+ "| 1 | 1 | 1-1 | 1-2 |"
+ "+---+---+----------+----------+"
+ "| 2 | 2 | 2-1 | 2-2 |"
+ "+---+---+----------+----------+"
+);
+
+test_table!(
+ dup_row_to_column_single_repeat,
+ Matrix::new(4, 3).with(Dup::new(Columns::single(1), Rows::single(0))),
+ "+---+----------+----------+----------+"
+ "| N | N | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | column 0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | column 1 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | column 2 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+ "| 3 | N | 3-1 | 3-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ dup_column_to_row_single_stop,
+ Matrix::new(4, 3).with(Dup::new(Rows::single(1), Columns::single(0))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| N | 0 | 1 | 2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+ "| 3 | 3-0 | 3-1 | 3-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ dup_row_to_global,
+ Matrix::new(4, 3).with(Dup::new(Segment::all(), Rows::single(0))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ dup_column_to_global,
+ Matrix::new(4, 3).with(Dup::new(Segment::all(), Columns::single(0))),
+ "+---+---+---+---+"
+ "| N | 0 | 1 | 2 |"
+ "+---+---+---+---+"
+ "| 3 | N | 0 | 1 |"
+ "+---+---+---+---+"
+ "| 2 | 3 | N | 0 |"
+ "+---+---+---+---+"
+ "| 1 | 2 | 3 | N |"
+ "+---+---+---+---+"
+ "| 0 | 1 | 2 | 3 |"
+ "+---+---+---+---+"
+);
+
+test_table!(
+ dup_empty_table,
+ Matrix::empty().with(Dup::new(Segment::all(), Columns::single(0))),
+ ""
+);
+
+test_table!(
+ dup_invalid_target,
+ Matrix::new(4, 3).with(Dup::new(Segment::all(), Columns::single(99))),
+ Matrix::new(4, 3),
+);
+
+test_table!(
+ dup_invalid_source,
+ Matrix::new(4, 3).with(Dup::new(Rows::single(99), Columns::first())),
+ Matrix::new(4, 3),
+);
diff --git a/vendor/tabled/tests/settings/extract_test.rs b/vendor/tabled/tests/settings/extract_test.rs
new file mode 100644
index 000000000..ce4697d7a
--- /dev/null
+++ b/vendor/tabled/tests/settings/extract_test.rs
@@ -0,0 +1,242 @@
+#![cfg(feature = "std")]
+
+use tabled::{
+ builder::Builder,
+ settings::{
+ object::{Rows, Segment},
+ Alignment, Disable, Extract, Format, Modify, Padding,
+ },
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+test_table!(
+ extract_segment_full_test,
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]"))))
+ .with(Extract::segment(.., ..)),
+ "+-------+--------------+--------------+--------------+"
+ "| [N] | [column 0] | [column 1] | [column 2] |"
+ "+-------+--------------+--------------+--------------+"
+ "| [0] | [0-0] | [0-1] | [0-2] |"
+ "+-------+--------------+--------------+--------------+"
+ "| [1] | [1-0] | [1-1] | [1-2] |"
+ "+-------+--------------+--------------+--------------+"
+ "| [2] | [2-0] | [2-1] | [2-2] |"
+ "+-------+--------------+--------------+--------------+"
+);
+
+test_table!(
+ extract_segment_skip_top_row_test,
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]"))))
+ .with(Extract::segment(1.., ..)),
+ "+-------+---------+---------+---------+"
+ "| [0] | [0-0] | [0-1] | [0-2] |"
+ "+-------+---------+---------+---------+"
+ "| [1] | [1-0] | [1-1] | [1-2] |"
+ "+-------+---------+---------+---------+"
+ "| [2] | [2-0] | [2-1] | [2-2] |"
+ "+-------+---------+---------+---------+"
+);
+
+test_table!(
+ extract_segment_skip_column_test,
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]"))))
+ .with(Extract::segment(.., 1..)),
+ "+--------------+--------------+--------------+"
+ "| [column 0] | [column 1] | [column 2] |"
+ "+--------------+--------------+--------------+"
+ "| [0-0] | [0-1] | [0-2] |"
+ "+--------------+--------------+--------------+"
+ "| [1-0] | [1-1] | [1-2] |"
+ "+--------------+--------------+--------------+"
+ "| [2-0] | [2-1] | [2-2] |"
+ "+--------------+--------------+--------------+"
+);
+
+test_table!(
+ extract_segment_bottom_right_square_test,
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]"))))
+ .with(Extract::segment(2.., 2..)),
+ "+---------+---------+"
+ "| [1-1] | [1-2] |"
+ "+---------+---------+"
+ "| [2-1] | [2-2] |"
+ "+---------+---------+"
+);
+
+test_table!(
+ extract_segment_middle_section_test,
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]"))))
+ .with(Extract::segment(1..3, 1..)),
+ "+---------+---------+---------+"
+ "| [0-0] | [0-1] | [0-2] |"
+ "+---------+---------+---------+"
+ "| [1-0] | [1-1] | [1-2] |"
+ "+---------+---------+---------+"
+);
+
+test_table!(
+ extract_segment_empty_test,
+ Matrix::new(3, 3).with(Extract::segment(1..1, 1..1)),
+ ""
+);
+
+test_table!(
+ extract_rows_full_test,
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]"))))
+ .with(Extract::rows(..)),
+ "+-------+--------------+--------------+--------------+"
+ "| [N] | [column 0] | [column 1] | [column 2] |"
+ "+-------+--------------+--------------+--------------+"
+ "| [0] | [0-0] | [0-1] | [0-2] |"
+ "+-------+--------------+--------------+--------------+"
+ "| [1] | [1-0] | [1-1] | [1-2] |"
+ "+-------+--------------+--------------+--------------+"
+ "| [2] | [2-0] | [2-1] | [2-2] |"
+ "+-------+--------------+--------------+--------------+"
+);
+
+test_table!(
+ extract_rows_empty_test,
+ Matrix::new(3, 3).with(Extract::rows(0..0)),
+ ""
+);
+
+test_table!(
+ extract_rows_partial_view_test,
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]"))))
+ .with(Extract::rows(0..=2)),
+ "+-------+--------------+--------------+--------------+"
+ "| [N] | [column 0] | [column 1] | [column 2] |"
+ "+-------+--------------+--------------+--------------+"
+ "| [0] | [0-0] | [0-1] | [0-2] |"
+ "+-------+--------------+--------------+--------------+"
+ "| [1] | [1-0] | [1-1] | [1-2] |"
+ "+-------+--------------+--------------+--------------+"
+);
+
+test_table!(
+ extract_columns_full_test,
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]"))))
+ .with(Extract::columns(..)),
+ "+-------+--------------+--------------+--------------+"
+ "| [N] | [column 0] | [column 1] | [column 2] |"
+ "+-------+--------------+--------------+--------------+"
+ "| [0] | [0-0] | [0-1] | [0-2] |"
+ "+-------+--------------+--------------+--------------+"
+ "| [1] | [1-0] | [1-1] | [1-2] |"
+ "+-------+--------------+--------------+--------------+"
+ "| [2] | [2-0] | [2-1] | [2-2] |"
+ "+-------+--------------+--------------+--------------+"
+);
+
+test_table!(
+ extract_columns_empty_test,
+ Matrix::new(3, 3).with(Extract::columns(0..0)),
+ ""
+);
+
+test_table!(
+ extract_columns_partial_view_test,
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]"))))
+ .with(Extract::columns(0..2)),
+ "+-------+--------------+"
+ "| [N] | [column 0] |"
+ "+-------+--------------+"
+ "| [0] | [0-0] |"
+ "+-------+--------------+"
+ "| [1] | [1-0] |"
+ "+-------+--------------+"
+ "| [2] | [2-0] |"
+ "+-------+--------------+"
+);
+
+test_table!(
+ extract_inside_test,
+ Matrix::new(3, 3).with(Disable::row(Rows::first())).with(Extract::segment(1..2, 1..2)),
+ "+-----+"
+ "| 1-0 |"
+ "+-----+"
+);
+
+test_table!(
+ extract_left_test,
+ Matrix::new(3, 3).with(Disable::row(Rows::first())).with(Extract::segment(.., ..1)),
+ "+---+"
+ "| 0 |"
+ "+---+"
+ "| 1 |"
+ "+---+"
+ "| 2 |"
+ "+---+"
+);
+
+test_table!(
+ extract_right_test,
+ Matrix::new(3, 3).with(Disable::row(Rows::first())).with(Extract::segment(.., 2..)),
+ "+-----+-----+"
+ "| 0-1 | 0-2 |"
+ "+-----+-----+"
+ "| 1-1 | 1-2 |"
+ "+-----+-----+"
+ "| 2-1 | 2-2 |"
+ "+-----+-----+"
+);
+
+test_table!(
+ extract_top_test,
+ Matrix::new(3, 3).with(Disable::row(Rows::first())).with(Extract::segment(..1, ..)),
+ "+---+-----+-----+-----+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+-----+-----+-----+"
+);
+
+test_table!(
+ extract_bottom_test,
+ Matrix::new(3, 3).with(Disable::row(Rows::first())).with(Extract::segment(2.., ..)),
+ "+---+-----+-----+-----+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+-----+-----+-----+"
+);
+
+test_table!(
+ extract_all_test,
+ Matrix::new(3, 3)
+ .with(Disable::row(Rows::first()))
+ .with(Extract::segment(3.., 3..)),
+ ""
+);
+
+test_table!(
+ extract_empty_test,
+ Builder::default().build().with(Extract::segment(.., ..)),
+ ""
+);
diff --git a/vendor/tabled/tests/settings/format_test.rs b/vendor/tabled/tests/settings/format_test.rs
new file mode 100644
index 000000000..82585ca62
--- /dev/null
+++ b/vendor/tabled/tests/settings/format_test.rs
@@ -0,0 +1,269 @@
+#![cfg(feature = "std")]
+
+use tabled::settings::{
+ object::{Cell, Columns, Object, Rows, Segment},
+ Alignment, Format, Modify, Padding, Style,
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+#[cfg(feature = "color")]
+use owo_colors::OwoColorize;
+
+test_table!(
+ formatting_full_test,
+ Matrix::new(3, 3).with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]")))),
+ "+-----+------------+------------+------------+"
+ "| [N] | [column 0] | [column 1] | [column 2] |"
+ "+-----+------------+------------+------------+"
+ "| [0] | [0-0] | [0-1] | [0-2] |"
+ "+-----+------------+------------+------------+"
+ "| [1] | [1-0] | [1-1] | [1-2] |"
+ "+-----+------------+------------+------------+"
+ "| [2] | [2-0] | [2-1] | [2-2] |"
+ "+-----+------------+------------+------------+"
+);
+
+test_table!(
+ formatting_head_test,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Rows::first()).with(Format::content(|s| format!(":{s}")))),
+ "| :N | :column 0 | :column 1 | :column 2 |"
+ "|----|-----------|-----------|-----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ formatting_row_test,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Rows::new(1..)).with(Format::content(|s| format!("<{s}>")))),
+ " N | column 0 | column 1 | column 2 "
+ "-----+----------+----------+----------"
+ " <0> | <0-0> | <0-1> | <0-2> "
+ " <1> | <1-0> | <1-1> | <1-2> "
+ " <2> | <2-0> | <2-1> | <2-2> "
+);
+
+test_table!(
+ formatting_column_test,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Columns::single(0)).with(Format::content(|s| format!("(x) {s}")))),
+ " (x) N | column 0 | column 1 | column 2 "
+ "-------+----------+----------+----------"
+ " (x) 0 | 0-0 | 0-1 | 0-2 "
+ " (x) 1 | 1-0 | 1-1 | 1-2 "
+ " (x) 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ formatting_multiline_test,
+ Matrix::new(3, 3)
+ .insert((2, 2), "E\nnde\navou\nros")
+ .insert((3, 2), "Red\nHat")
+ .insert((3, 3), "https://\nwww\n.\nredhat\n.com\n/en")
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("(x) {s}")).multiline())),
+ " (x) N | (x) column 0 | (x) column 1 | (x) column 2 "
+ "-------+--------------+--------------+--------------"
+ " (x) 0 | (x) 0-0 | (x) 0-1 | (x) 0-2 "
+ " (x) 1 | (x) 1-0 | (x) E | (x) 1-2 "
+ " | | (x) nde | "
+ " | | (x) avou | "
+ " | | (x) ros | "
+ " (x) 2 | (x) 2-0 | (x) Red | (x) https:// "
+ " | | (x) Hat | (x) www "
+ " | | | (x) . "
+ " | | | (x) redhat "
+ " | | | (x) .com "
+ " | | | (x) /en "
+);
+
+test_table!(
+ formatting_cell_test,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Cell::new(0, 0)).with(Format::content(|s| format!("(x) {s}"))))
+ .with(Modify::new(Cell::new(0, 1)).with(Format::content(|s| format!("(x) {s}"))))
+ .with(Modify::new(Cell::new(0, 2)).with(Format::content(|s| format!("(x) {s}")))),
+ " (x) N | (x) column 0 | (x) column 1 | column 2 "
+ "-------+--------------+--------------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ formatting_combination_and_test,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(
+ Modify::new(Columns::single(0).and(Rows::single(0)))
+ .with(Format::content(|s| format!("(x) {s}"))),
+ ),
+ " (x) N | (x) column 0 | (x) column 1 | (x) column 2 "
+ "-------+--------------+--------------+--------------"
+ " (x) 0 | 0-0 | 0-1 | 0-2 "
+ " (x) 1 | 1-0 | 1-1 | 1-2 "
+ " (x) 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ formatting_combination_not_test,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(
+ Modify::new(Columns::single(0).and(Rows::single(0)).not(Cell::new(0, 0)))
+ .with(Format::content(|s| format!("(x) {s}"))),
+ ),
+ " N | (x) column 0 | (x) column 1 | (x) column 2 "
+ "-------+--------------+--------------+--------------"
+ " (x) 0 | 0-0 | 0-1 | 0-2 "
+ " (x) 1 | 1-0 | 1-1 | 1-2 "
+ " (x) 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ formatting_combination_inverse_test,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Columns::single(0).inverse()).with(Format::content(|s| format!("(x) {s}")))),
+ " N | (x) column 0 | (x) column 1 | (x) column 2 "
+ "---+--------------+--------------+--------------"
+ " 0 | (x) 0-0 | (x) 0-1 | (x) 0-2 "
+ " 1 | (x) 1-0 | (x) 1-1 | (x) 1-2 "
+ " 2 | (x) 2-0 | (x) 2-1 | (x) 2-2 "
+);
+
+test_table!(
+ formatting_combination_intersect_test,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(
+ Modify::new(Columns::new(1..3).intersect(Rows::new(1..3)))
+ .with(Format::content(|s| format!("(x) {s}"))),
+ ),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | (x) 0-0 | (x) 0-1 | 0-2 "
+ " 1 | (x) 1-0 | (x) 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ formatting_using_lambda_test,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Rows::first()).with(Format::content(|s| format!(":{s}")))),
+ "| :N | :column 0 | :column 1 | :column 2 |"
+ "|----|-----------|-----------|-----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ formatting_using_function_test,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Rows::first()).with(Format::content(str::to_uppercase))),
+ "| N | COLUMN 0 | COLUMN 1 | COLUMN 2 |"
+ "|---|----------|----------|----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ format_with_index,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Rows::first()).with(Format::positioned(|a, (b, c)| match (b, c) {
+ (0, 0) => "(0, 0)".to_string(),
+ (0, 1) => "(0, 1)".to_string(),
+ (0, 2) => "(0, 2)".to_string(),
+ _ => a.to_string(),
+ }))),
+ "| (0, 0) | (0, 1) | (0, 2) | column 2 |"
+ "|--------|--------|--------|----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ format_doesnt_change_padding,
+ Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 1, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Format::content(|s| format!("[{s}]")))),
+ "+-------+--------------+--------------+--------------+"
+ "| [N] | [column 0] | [column 1] | [column 2] |"
+ "+-------+--------------+--------------+--------------+"
+ "| [0] | [0-0] | [0-1] | [0-2] |"
+ "+-------+--------------+--------------+--------------+"
+ "| [1] | [1-0] | [1-1] | [1-2] |"
+ "+-------+--------------+--------------+--------------+"
+ "| [2] | [2-0] | [2-1] | [2-2] |"
+ "+-------+--------------+--------------+--------------+"
+);
+
+test_table!(
+ formatting_content_str_test,
+ Matrix::new(3, 3).with(Modify::new(Segment::all()).with(Format::content(|_| String::from("Hello World")))),
+ "+-------------+-------------+-------------+-------------+"
+ "| Hello World | Hello World | Hello World | Hello World |"
+ "+-------------+-------------+-------------+-------------+"
+ "| Hello World | Hello World | Hello World | Hello World |"
+ "+-------------+-------------+-------------+-------------+"
+ "| Hello World | Hello World | Hello World | Hello World |"
+ "+-------------+-------------+-------------+-------------+"
+ "| Hello World | Hello World | Hello World | Hello World |"
+ "+-------------+-------------+-------------+-------------+"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ color_test,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(
+ Modify::new(Columns::new(..1).and(Columns::new(2..)))
+ .with(Format::content(|s| s.red().to_string())),
+ )
+ .with(Modify::new(Columns::new(1..2)).with(Format::content(|s| s.blue().to_string()))),
+ " \u{1b}[31mN\u{1b}[39m | \u{1b}[34mcolumn 0\u{1b}[39m | \u{1b}[31mcolumn 1\u{1b}[39m | \u{1b}[31mcolumn 2\u{1b}[39m "
+ "---+----------+----------+----------"
+ " \u{1b}[31m0\u{1b}[39m | \u{1b}[34m0-0\u{1b}[39m | \u{1b}[31m0-1\u{1b}[39m | \u{1b}[31m0-2\u{1b}[39m "
+ " \u{1b}[31m1\u{1b}[39m | \u{1b}[34m1-0\u{1b}[39m | \u{1b}[31m1-1\u{1b}[39m | \u{1b}[31m1-2\u{1b}[39m "
+ " \u{1b}[31m2\u{1b}[39m | \u{1b}[34m2-0\u{1b}[39m | \u{1b}[31m2-1\u{1b}[39m | \u{1b}[31m2-2\u{1b}[39m "
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ color_multiline_test,
+ Matrix::new(3, 3)
+ .insert((2, 2), "E\nnde\navou\nros")
+ .insert((3, 2), "Red\nHat")
+ .insert((3, 3), "https://\nwww\n.\nredhat\n.com\n/en")
+ .with(Style::psql())
+ .with(Modify::new(Columns::new(..1)).with(Format::content(|s| s.red().to_string()).multiline()))
+ .with(Modify::new(Columns::new(1..2)).with(Format::content(|s| s.blue().to_string()).multiline()))
+ .with(Modify::new(Columns::new(2..)).with(Format::content(|s| s.green().to_string()).multiline())),
+ " \u{1b}[31mN\u{1b}[39m | \u{1b}[34mcolumn 0\u{1b}[39m | \u{1b}[32mcolumn 1\u{1b}[39m | \u{1b}[32mcolumn 2\u{1b}[39m "
+ "---+----------+----------+----------\n \u{1b}[31m0\u{1b}[39m | \u{1b}[34m0-0\u{1b}[39m | \u{1b}[32m0-1\u{1b}[39m | \u{1b}[32m0-2\u{1b}[39m "
+ " \u{1b}[31m1\u{1b}[39m | \u{1b}[34m1-0\u{1b}[39m | \u{1b}[32mE\u{1b}[39m | \u{1b}[32m1-2\u{1b}[39m "
+ " | | \u{1b}[32mnde\u{1b}[39m | "
+ " | | \u{1b}[32mavou\u{1b}[39m | "
+ " | | \u{1b}[32mros\u{1b}[39m | "
+ " \u{1b}[31m2\u{1b}[39m | \u{1b}[34m2-0\u{1b}[39m | \u{1b}[32mRed\u{1b}[39m | \u{1b}[32mhttps://\u{1b}[39m "
+ " | | \u{1b}[32mHat\u{1b}[39m | \u{1b}[32mwww\u{1b}[39m \n | | | \u{1b}[32m.\u{1b}[39m "
+ " | | | \u{1b}[32mredhat\u{1b}[39m "
+ " | | | \u{1b}[32m.com\u{1b}[39m "
+ " | | | \u{1b}[32m/en\u{1b}[39m "
+);
diff --git a/vendor/tabled/tests/settings/formatting_test.rs b/vendor/tabled/tests/settings/formatting_test.rs
new file mode 100644
index 000000000..70f925a8b
--- /dev/null
+++ b/vendor/tabled/tests/settings/formatting_test.rs
@@ -0,0 +1,68 @@
+#![cfg(feature = "std")]
+
+use tabled::settings::{formatting::Justification, object::Columns, Color, Modify};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+test_table!(
+ justification,
+ Matrix::new(3, 3).with(Justification::new('#')),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | ##0-0### | ##0-1### | ##0-2### |"
+ "+---+----------+----------+----------+"
+ "| 1 | ##1-0### | ##1-1### | ##1-2### |"
+ "+---+----------+----------+----------+"
+ "| 2 | ##2-0### | ##2-1### | ##2-2### |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ justification_color,
+ Matrix::new(3, 3).with(Justification::new('#').color(Color::BG_RED)),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | \u{1b}[41m##\u{1b}[49m0-0\u{1b}[41m###\u{1b}[49m | \u{1b}[41m##\u{1b}[49m0-1\u{1b}[41m###\u{1b}[49m | \u{1b}[41m##\u{1b}[49m0-2\u{1b}[41m###\u{1b}[49m |"
+ "+---+----------+----------+----------+"
+ "| 1 | \u{1b}[41m##\u{1b}[49m1-0\u{1b}[41m###\u{1b}[49m | \u{1b}[41m##\u{1b}[49m1-1\u{1b}[41m###\u{1b}[49m | \u{1b}[41m##\u{1b}[49m1-2\u{1b}[41m###\u{1b}[49m |"
+ "+---+----------+----------+----------+"
+ "| 2 | \u{1b}[41m##\u{1b}[49m2-0\u{1b}[41m###\u{1b}[49m | \u{1b}[41m##\u{1b}[49m2-1\u{1b}[41m###\u{1b}[49m | \u{1b}[41m##\u{1b}[49m2-2\u{1b}[41m###\u{1b}[49m |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ justification_columns,
+ Matrix::new(3, 3)
+ .with(Modify::new(Columns::single(1)).with(Justification::new('#')))
+ .with(Modify::new(Columns::single(2)).with(Justification::new('@')))
+ .with(Modify::new(Columns::single(3)).with(Justification::new('$'))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | ##0-0### | @@0-1@@@ | $$0-2$$$ |"
+ "+---+----------+----------+----------+"
+ "| 1 | ##1-0### | @@1-1@@@ | $$1-2$$$ |"
+ "+---+----------+----------+----------+"
+ "| 2 | ##2-0### | @@2-1@@@ | $$2-2$$$ |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ justification_color_columns,
+ Matrix::new(3, 3)
+ .with(Modify::new(Columns::single(1)).with(Justification::new('#').color(Color::BG_BLUE)))
+ .with(Modify::new(Columns::single(2)).with(Justification::new('@').color(Color::BG_RED)))
+ .with(Modify::new(Columns::single(3)).with(Justification::new('$').color(Color::BG_WHITE))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | \u{1b}[44m##\u{1b}[49m0-0\u{1b}[44m###\u{1b}[49m | \u{1b}[41m@@\u{1b}[49m0-1\u{1b}[41m@@@\u{1b}[49m | \u{1b}[47m$$\u{1b}[49m0-2\u{1b}[47m$$$\u{1b}[49m |"
+ "+---+----------+----------+----------+"
+ "| 1 | \u{1b}[44m##\u{1b}[49m1-0\u{1b}[44m###\u{1b}[49m | \u{1b}[41m@@\u{1b}[49m1-1\u{1b}[41m@@@\u{1b}[49m | \u{1b}[47m$$\u{1b}[49m1-2\u{1b}[47m$$$\u{1b}[49m |"
+ "+---+----------+----------+----------+"
+ "| 2 | \u{1b}[44m##\u{1b}[49m2-0\u{1b}[44m###\u{1b}[49m | \u{1b}[41m@@\u{1b}[49m2-1\u{1b}[41m@@@\u{1b}[49m | \u{1b}[47m$$\u{1b}[49m2-2\u{1b}[47m$$$\u{1b}[49m |"
+ "+---+----------+----------+----------+"
+);
diff --git a/vendor/tabled/tests/settings/height_test.rs b/vendor/tabled/tests/settings/height_test.rs
new file mode 100644
index 000000000..48d6a6364
--- /dev/null
+++ b/vendor/tabled/tests/settings/height_test.rs
@@ -0,0 +1,263 @@
+#![cfg(feature = "std")]
+
+use tabled::settings::{
+ object::{Columns, Segment},
+ Alignment, Format, Height, Modify, Style,
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+#[cfg(feature = "color")]
+use owo_colors::OwoColorize;
+
+test_table!(
+ cell_height_increase,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(
+ Modify::new(Columns::first())
+ .with(Height::increase(3))
+ )
+ .with(Modify::new(Segment::all()).with(
+ Alignment::center_vertical()
+ )),
+ "| N | | | |"
+ "| | column 0 | column 1 | column 2 |"
+ "| | | | |"
+ "|---|----------|----------|----------|"
+ "| 0 | | | |"
+ "| | 0-0 | 0-1 | 0-2 |"
+ "| | | | |"
+ "| 1 | | | |"
+ "| | 1-0 | 1-1 | 1-2 |"
+ "| | | | |"
+ "| 2 | | | |"
+ "| | 2-0 | 2-1 | 2-2 |"
+ "| | | | |"
+);
+
+test_table!(
+ table_height_increase,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Columns::first()).with(Alignment::center_vertical()))
+ .with(Height::increase(10)),
+ "| | column 0 | column 1 | column 2 |"
+ "| N | | | |"
+ "| | | | |"
+ "|---|----------|----------|----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| | | | |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| | | | |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "| | | | |"
+);
+
+test_table!(
+ cell_height_increase_zero,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(
+ Modify::new(Columns::first())
+ .with(Height::increase(0))
+ )
+ .with(Modify::new(Segment::all()).with(
+ Alignment::center_vertical()
+ )),
+ "| N | column 0 | column 1 | column 2 |"
+ "|---|----------|----------|----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ table_height_increase_zero,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Columns::first()).with(Alignment::center_vertical()))
+ .with(Height::increase(0)),
+ "| N | column 0 | column 1 | column 2 |"
+ "|---|----------|----------|----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ cell_height_limit,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Columns::first()).with(Format::content(|s| format!("xxxx\n{s}xxxx\nxxxx\n"))))
+ .with(
+ Modify::new(Columns::first())
+ .with(Height::limit(1))
+ )
+ .with(Modify::new(Segment::all()).with(
+ Alignment::center_vertical()
+ )),
+ "| xxxx | column 0 | column 1 | column 2 |"
+ "|------|----------|----------|----------|"
+ "| xxxx | 0-0 | 0-1 | 0-2 |"
+ "| xxxx | 1-0 | 1-1 | 1-2 |"
+ "| xxxx | 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ table_height_limit,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Columns::first()).with(Format::content(|s| format!("xxxx\n{s}xxxx\nxxxx\n"))))
+ .with(Modify::new(Columns::first()).with(Alignment::center_vertical()))
+ .with(Height::limit(10)),
+ "| xxxx | column 0 | column 1 | column 2 |"
+ "| Nxxxx | | | |"
+ "|-------|----------|----------|----------|"
+ "| xxxx | 0-0 | 0-1 | 0-2 |"
+ "| 0xxxx | | | |"
+ "| xxxx | 1-0 | 1-1 | 1-2 |"
+ "| 1xxxx | | | |"
+ "| xxxx | 2-0 | 2-1 | 2-2 |"
+ "| 2xxxx | | | |"
+ "| xxxx | | | |"
+);
+
+test_table!(
+ table_height_limit_style_change_after,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Columns::first()).with(Format::content(|s| format!("xxxx\n{s}xxxx\nxxxx\n"))))
+ .with(Modify::new(Columns::first()).with(Alignment::center_vertical()))
+ .with(Height::limit(7)),
+ "| xxxx | column 0 | column 1 | column 2 |"
+ "|-------|----------|----------|----------|"
+ "| xxxx | 0-0 | 0-1 | 0-2 |"
+ "| xxxx | 1-0 | 1-1 | 1-2 |"
+ "| 1xxxx | | | |"
+ "| xxxx | 2-0 | 2-1 | 2-2 |"
+ "| 2xxxx | | | |"
+);
+
+test_table!(
+ cell_height_limit_zero,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Columns::first()).with(Format::content(|s| format!("xxxx\n{s}xxxx\nxxxx\n"))))
+ .with(
+ Modify::new(Columns::first())
+ .with(Height::limit(0))
+ )
+ .with(Modify::new(Segment::all()).with(
+ Alignment::center_vertical()
+ )),
+ "| | column 0 | column 1 | column 2 |"
+ "|--|----------|----------|----------|"
+ "| | 0-0 | 0-1 | 0-2 |"
+ "| | 1-0 | 1-1 | 1-2 |"
+ "| | 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ table_height_limit_zero,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(
+ Modify::new(Columns::new(..))
+ .with(Format::content(|s| format!("xxxx\n{s}xxxx\nxxxx\n")))
+ )
+ .with(Height::limit(0)),
+ "|--|--|--|--|"
+);
+
+test_table!(
+ table_height_limit_zero_1,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Height::limit(0))
+ .with(
+ Modify::new(Columns::new(..)).with(Format::content(|s| format!("xxxx\n{s}xxxx\nxxxx\n")))
+ ),
+ "| xxxx | xxxx | xxxx | xxxx |"
+ "| xxxx | xxxx | xxxx | xxxx |"
+ "| xxxx | xxxx | xxxx | xxxx |"
+ "| | | | |"
+ "|------|------|------|------|"
+ "| xxxx | xxxx | xxxx | xxxx |"
+ "| xxxx | xxxx | xxxx | xxxx |"
+ "| xxxx | xxxx | xxxx | xxxx |"
+ "| | | | |"
+ "| xxxx | xxxx | xxxx | xxxx |"
+ "| xxxx | xxxx | xxxx | xxxx |"
+ "| xxxx | xxxx | xxxx | xxxx |"
+ "| | | | |"
+ "| xxxx | xxxx | xxxx | xxxx |"
+ "| xxxx | xxxx | xxxx | xxxx |"
+ "| xxxx | xxxx | xxxx | xxxx |"
+ "| | | | |"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ cell_height_limit_colored,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Columns::first()).with(Format::content(|s| format!("xxxx\n{s}xxxx\nxxxx\n").red().to_string())))
+ .with(
+ Modify::new(Columns::first())
+ .with(Height::limit(1))
+ )
+ .with(Modify::new(Segment::all()).with(
+ Alignment::center_vertical()
+ )),
+ "| \u{1b}[31mxxxx\u{1b}[39m | column 0 | column 1 | column 2 |"
+ "|------|----------|----------|----------|"
+ "| \u{1b}[31mxxxx\u{1b}[39m | 0-0 | 0-1 | 0-2 |"
+ "| \u{1b}[31mxxxx\u{1b}[39m | 1-0 | 1-1 | 1-2 |"
+ "| \u{1b}[31mxxxx\u{1b}[39m | 2-0 | 2-1 | 2-2 |"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ table_height_limit_colored,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Columns::first()).with(Format::content(|s| format!("xxxx\n{s}xxxx\nxxxx\n").blue().on_green().to_string())))
+ .with(Modify::new(Columns::first()).with(Alignment::center_vertical()))
+ .with(Height::limit(10)),
+ "| \u{1b}[34;42mxxxx\u{1b}[39m\u{1b}[49m | column 0 | column 1 | column 2 |"
+ "| \u{1b}[34m\u{1b}[42mNxxxx\u{1b}[39m\u{1b}[49m | | | |"
+ "|-------|----------|----------|----------|"
+ "| \u{1b}[34;42mxxxx\u{1b}[39m\u{1b}[49m | 0-0 | 0-1 | 0-2 |"
+ "| \u{1b}[34m\u{1b}[42m0xxxx\u{1b}[39m\u{1b}[49m | | | |"
+ "| \u{1b}[34;42mxxxx\u{1b}[39m\u{1b}[49m | 1-0 | 1-1 | 1-2 |"
+ "| \u{1b}[34m\u{1b}[42m1xxxx\u{1b}[39m\u{1b}[49m | | | |"
+ "| \u{1b}[34;42mxxxx\u{1b}[39m\u{1b}[49m | 2-0 | 2-1 | 2-2 |"
+ "| \u{1b}[34m\u{1b}[42m2xxxx\u{1b}[39m\u{1b}[49m | | | |"
+ "| \u{1b}[34m\u{1b}[42mxxxx\u{1b}[39m\u{1b}[49m | | | |"
+);
+
+#[cfg(feature = "macros")]
+test_table!(
+ cell_height_1x1,
+ tabled::row![tabled::col!["SGML"].with(Height::increase(4))],
+ "+----------+"
+ "| +------+ |"
+ "| | SGML | |"
+ "| | | |"
+ "| +------+ |"
+ "+----------+"
+);
+
+#[cfg(feature = "macros")]
+test_table!(
+ cell_height_1x1_no_top_border,
+ tabled::row![tabled::col!["SGML"].with(Style::ascii().remove_top()).with(Height::increase(4))],
+ "+----------+"
+ "| | SGML | |"
+ "| | | |"
+ "| | | |"
+ "| +------+ |"
+ "+----------+"
+);
diff --git a/vendor/tabled/tests/settings/highlingt_test.rs b/vendor/tabled/tests/settings/highlingt_test.rs
new file mode 100644
index 000000000..cb5ce75f3
--- /dev/null
+++ b/vendor/tabled/tests/settings/highlingt_test.rs
@@ -0,0 +1,419 @@
+#![cfg(feature = "std")]
+
+use tabled::{
+ builder::Builder,
+ settings::{
+ highlight::Highlight,
+ object::{Cell, Columns, Frame, Object, Rows, Segment},
+ style::{Border, Style},
+ },
+};
+
+use crate::matrix::Matrix;
+use testing_table::{static_table, test_table};
+
+test_table!(
+ highlingt_object_exceeds_boundaries,
+ Matrix::new(3, 3).with(Style::modern()).with(Highlight::new(Cell::new(1000, 0), Border::filled('+'))),
+ "┌───┬──────────┬──────────┬──────────┐"
+ "│ N │ column 0 │ column 1 │ column 2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 0 │ 0-0 │ 0-1 │ 0-2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+ "└───┴──────────┴──────────┴──────────┘"
+);
+
+test_table!(
+ highlingt_empty_table,
+ Builder::default()
+ .build()
+ .with(Highlight::new(Segment::all(), Border::filled('+'))),
+ ""
+);
+
+test_table!(
+ highlingt_cell,
+ Matrix::new(3, 3)
+ .with(Style::modern())
+ .with(Highlight::new(Cell::new(0, 0), Border::filled('+')))
+ .with(Highlight::new(Cell::new(1, 1), Border::filled('*'))),
+ "+++++──────────┬──────────┬──────────┐"
+ "+ N + column 0 │ column 1 │ column 2 │"
+ "++++************──────────┼──────────┤"
+ "│ 0 * 0-0 * 0-1 │ 0-2 │"
+ "├───************──────────┼──────────┤"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+ "└───┴──────────┴──────────┴──────────┘"
+);
+
+test_table!(
+ highlingt_row,
+ Matrix::new(3, 3)
+ .with(Style::modern())
+ .with(Highlight::new(Rows::single(0), Border::filled('+')))
+ .with(Highlight::new(Rows::single(3), Border::filled('*'))),
+ "++++++++++++++++++++++++++++++++++++++"
+ "+ N │ column 0 │ column 1 │ column 2 +"
+ "++++++++++++++++++++++++++++++++++++++"
+ "│ 0 │ 0-0 │ 0-1 │ 0-2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "**************************************"
+ "* 2 │ 2-0 │ 2-1 │ 2-2 *"
+ "**************************************"
+);
+
+test_table!(
+ highlingt_column,
+ Matrix::new(3, 3)
+ .with(Style::modern())
+ .with(Highlight::new(Columns::single(0), Border::filled('+')))
+ .with(Highlight::new(Columns::single(2), Border::filled('*'))),
+ "+++++──────────************──────────┐"
+ "+ N + column 0 * column 1 * column 2 │"
+ "+───+──────────*──────────*──────────┤"
+ "+ 0 + 0-0 * 0-1 * 0-2 │"
+ "+───+──────────*──────────*──────────┤"
+ "+ 1 + 1-0 * 1-1 * 1-2 │"
+ "+───+──────────*──────────*──────────┤"
+ "+ 2 + 2-0 * 2-1 * 2-2 │"
+ "+++++──────────************──────────┘"
+);
+
+test_table!(
+ highlingt_row_range,
+ Matrix::new(3, 3)
+ .with(Style::modern())
+ .with(Highlight::new(Rows::new(1..3), Border::filled('+'))),
+ "┌───┬──────────┬──────────┬──────────┐"
+ "│ N │ column 0 │ column 1 │ column 2 │"
+ "++++++++++++++++++++++++++++++++++++++"
+ "+ 0 │ 0-0 │ 0-1 │ 0-2 +"
+ "+───┼──────────┼──────────┼──────────+"
+ "+ 1 │ 1-0 │ 1-1 │ 1-2 +"
+ "++++++++++++++++++++++++++++++++++++++"
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+ "└───┴──────────┴──────────┴──────────┘"
+);
+
+test_table!(
+ highlingt_column_range,
+ Matrix::new(3, 3)
+ .with(Style::modern())
+ .with(Highlight::new(Columns::new(..2), Border::filled('+'))),
+ "++++++++++++++++──────────┬──────────┐"
+ "+ N │ column 0 + column 1 │ column 2 │"
+ "+───┼──────────+──────────┼──────────┤"
+ "+ 0 │ 0-0 + 0-1 │ 0-2 │"
+ "+───┼──────────+──────────┼──────────┤"
+ "+ 1 │ 1-0 + 1-1 │ 1-2 │"
+ "+───┼──────────+──────────┼──────────┤"
+ "+ 2 │ 2-0 + 2-1 │ 2-2 │"
+ "++++++++++++++++──────────┴──────────┘"
+);
+
+test_table!(
+ highlingt_frame,
+ Matrix::new(3, 3)
+ .with(Style::modern())
+ .with(Highlight::new(
+ Frame,
+ Border::filled('+')
+ .corner_top_left('*')
+ .corner_top_right('#')
+ .corner_bottom_left('@')
+ .corner_bottom_right('.'),
+ )),
+ "*++++++++++++++++++++++++++++++++++++#"
+ "+ N │ column 0 │ column 1 │ column 2 +"
+ "+───┼──────────┼──────────┼──────────+"
+ "+ 0 │ 0-0 │ 0-1 │ 0-2 +"
+ "+───┼──────────┼──────────┼──────────+"
+ "+ 1 │ 1-0 │ 1-1 │ 1-2 +"
+ "+───┼──────────┼──────────┼──────────+"
+ "+ 2 │ 2-0 │ 2-1 │ 2-2 +"
+ "@++++++++++++++++++++++++++++++++++++."
+);
+
+test_table!(
+ highlingt_full,
+ Matrix::new(3, 3)
+ .with(Style::modern())
+ .with(Highlight::new(
+ Segment::all(),
+ Border::filled('+')
+ .corner_top_left('*')
+ .corner_top_right('#')
+ .corner_bottom_left('@')
+ .corner_bottom_right('.'),
+ )),
+ "*++++++++++++++++++++++++++++++++++++#"
+ "+ N │ column 0 │ column 1 │ column 2 +"
+ "+───┼──────────┼──────────┼──────────+"
+ "+ 0 │ 0-0 │ 0-1 │ 0-2 +"
+ "+───┼──────────┼──────────┼──────────+"
+ "+ 1 │ 1-0 │ 1-1 │ 1-2 +"
+ "+───┼──────────┼──────────┼──────────+"
+ "+ 2 │ 2-0 │ 2-1 │ 2-2 +"
+ "@++++++++++++++++++++++++++++++++++++."
+);
+
+test_table!(
+ highlingt_single_column,
+ Matrix::table(3, 0)
+ .with(Style::modern())
+ .with(Highlight::new(Cell::new(0, 0), Border::default().left('*').top('x')))
+ .with(Highlight::new(Rows::new(1..3), Border::default().left('n'))),
+ "┌xxx┐"
+ "* N │"
+ "├───┤"
+ "n 0 │"
+ "n───┤"
+ "n 1 │"
+ "├───┤"
+ "│ 2 │"
+ "└───┘"
+);
+
+test_table!(
+ highlingt_several_times,
+ Matrix::new(3, 3)
+ .with(Style::modern())
+ .with(Highlight::new(Frame, Border::filled('*')))
+ .with(Highlight::new(Cell::new(1, 1), Border::filled('#')))
+ .with(Highlight::new(Columns::single(3), Border::filled('x'))),
+ "**************************xxxxxxxxxxxx"
+ "* N │ column 0 │ column 1 x column 2 x"
+ "*───############──────────x──────────x"
+ "* 0 # 0-0 # 0-1 x 0-2 x"
+ "*───############──────────x──────────x"
+ "* 1 │ 1-0 │ 1-1 x 1-2 x"
+ "*───┼──────────┼──────────x──────────x"
+ "* 2 │ 2-0 │ 2-1 x 2-2 x"
+ "**************************xxxxxxxxxxxx"
+);
+
+// @todo
+//
+// #[test]
+// fn highlingt_empty_border() {
+// let data = create_vector::<3, 3>();
+// let table = Table::new(&data)
+// .with(Style::modern())
+// .with(Highlight::new(Frame, Border::empty()))
+// .to_string();
+
+// let expected = static_table!(
+// " N │ column 0 │ column 1 │ column 2 "
+// "─── ──────────"
+// " 0 0-0 │ 0-1 0-2 "
+// "─── ──────────┼────────── ──────────"
+// " 1 1-0 │ 1-1 1-2 "
+// "─── ──────────"
+// " 2 │ 2-0 │ 2-1 │ 2-2 "
+// );
+
+// assert_eq!(table, expected);
+// }
+
+#[test]
+fn highlingt_complex_figures() {
+ macro_rules! test_highlight {
+ ($object:expr, $expected:expr,) => {
+ let border = Border::filled('+')
+ .corner_top_left('*')
+ .corner_top_right('#')
+ .corner_bottom_left('@')
+ .corner_bottom_right('.');
+
+ let table = Matrix::new(3, 3)
+ .with(Style::modern())
+ .with(Highlight::new($object, border))
+ .to_string();
+
+ assert_eq!(table, $expected);
+ };
+ }
+
+ test_highlight!(
+ Segment::all().not(Segment::new(2.., 1..3)),
+ static_table!(
+ "*++++++++++++++++++++++++++++++++++++#"
+ "+ N │ column 0 │ column 1 │ column 2 +"
+ "+───┼──────────┼──────────┼──────────+"
+ "+ 0 │ 0-0 │ 0-1 │ 0-2 +"
+ "+───*+++++++++++++++++++++#──────────+"
+ "+ 1 + 1-0 │ 1-1 + 1-2 +"
+ "+───+──────────┼──────────+──────────+"
+ "+ 2 + 2-0 │ 2-1 + 2-2 +"
+ "@+++.──────────┴──────────@++++++++++."
+ ),
+ );
+
+ test_highlight!(
+ Segment::all()
+ .not(Segment::new(0..1, 1..3))
+ .not(Columns::single(0)),
+ static_table!(
+ "┌───┬──────────┬──────────*++++++++++#"
+ "│ N │ column 0 │ column 1 + column 2 +"
+ "├───*+++++++++++++++++++++.──────────+"
+ "│ 0 + 0-0 │ 0-1 │ 0-2 +"
+ "├───+──────────┼──────────┼──────────+"
+ "│ 1 + 1-0 │ 1-1 │ 1-2 +"
+ "├───+──────────┼──────────┼──────────+"
+ "│ 2 + 2-0 │ 2-1 │ 2-2 +"
+ "└───@++++++++++++++++++++++++++++++++."
+ ),
+ );
+
+ test_highlight!(
+ Segment::all().not(Segment::new(0..1, 1..3)),
+ static_table!(
+ "*+++#──────────┬──────────*++++++++++#"
+ "+ N + column 0 │ column 1 + column 2 +"
+ "+───@+++++++++++++++++++++.──────────+"
+ "+ 0 │ 0-0 │ 0-1 │ 0-2 +"
+ "+───┼──────────┼──────────┼──────────+"
+ "+ 1 │ 1-0 │ 1-1 │ 1-2 +"
+ "+───┼──────────┼──────────┼──────────+"
+ "+ 2 │ 2-0 │ 2-1 │ 2-2 +"
+ "@++++++++++++++++++++++++++++++++++++."
+ ),
+ );
+
+ test_highlight!(
+ Segment::all().not(Segment::new(1..2, 1..3)),
+ static_table!(
+ "*++++++++++++++++++++++++++++++++++++#"
+ "+ N │ column 0 │ column 1 │ column 2 +"
+ "+───*+++++++++++++++++++++#──────────+"
+ "+ 0 + 0-0 │ 0-1 + 0-2 +"
+ "+───@+++++++++++++++++++++.──────────+"
+ "+ 1 │ 1-0 │ 1-1 │ 1-2 +"
+ "+───┼──────────┼──────────┼──────────+"
+ "+ 2 │ 2-0 │ 2-1 │ 2-2 +"
+ "@++++++++++++++++++++++++++++++++++++."
+ ),
+ );
+
+ test_highlight!(
+ Cell::new(0, 0)
+ .and(Cell::new(3, 3))
+ .and(Cell::new(0, 3))
+ .and(Cell::new(3, 0)),
+ static_table!(
+ "*+++#──────────┬──────────*++++++++++#"
+ "+ N + column 0 │ column 1 + column 2 +"
+ "@+++.──────────┼──────────@++++++++++."
+ "│ 0 │ 0-0 │ 0-1 │ 0-2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "*+++#──────────┼──────────*++++++++++#"
+ "+ 2 + 2-0 │ 2-1 + 2-2 +"
+ "@+++.──────────┴──────────@++++++++++."
+ ),
+ );
+
+ test_highlight!(
+ Rows::single(0).and(Rows::single(3)),
+ static_table!(
+ "*++++++++++++++++++++++++++++++++++++#"
+ "+ N │ column 0 │ column 1 │ column 2 +"
+ "@++++++++++++++++++++++++++++++++++++."
+ "│ 0 │ 0-0 │ 0-1 │ 0-2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "*++++++++++++++++++++++++++++++++++++#"
+ "+ 2 │ 2-0 │ 2-1 │ 2-2 +"
+ "@++++++++++++++++++++++++++++++++++++."
+ ),
+ );
+
+ test_highlight!(
+ Columns::single(0).and(Columns::single(3)),
+ static_table!(
+ "*+++#──────────┬──────────*++++++++++#"
+ "+ N + column 0 │ column 1 + column 2 +"
+ "+───+──────────┼──────────+──────────+"
+ "+ 0 + 0-0 │ 0-1 + 0-2 +"
+ "+───+──────────┼──────────+──────────+"
+ "+ 1 + 1-0 │ 1-1 + 1-2 +"
+ "+───+──────────┼──────────+──────────+"
+ "+ 2 + 2-0 │ 2-1 + 2-2 +"
+ "@+++.──────────┴──────────@++++++++++."
+ ),
+ );
+
+ test_highlight!(
+ Segment::all().not(Cell::new(3, 1).and(Cell::new(3, 2))),
+ static_table!(
+ "*++++++++++++++++++++++++++++++++++++#"
+ "+ N │ column 0 │ column 1 │ column 2 +"
+ "+───┼──────────┼──────────┼──────────+"
+ "+ 0 │ 0-0 │ 0-1 │ 0-2 +"
+ "+───┼──────────┼──────────┼──────────+"
+ "+ 1 │ 1-0 │ 1-1 │ 1-2 +"
+ "+───*+++++++++++++++++++++#──────────+"
+ "+ 2 + 2-0 │ 2-1 + 2-2 +"
+ "@+++.──────────┴──────────@++++++++++."
+ ),
+ );
+
+ test_highlight!(
+ Rows::single(0)
+ .and(Cell::new(1, 1).and(Cell::new(1, 2)))
+ .and(Cell::new(2, 3)),
+ static_table!(
+ "*++++++++++++++++++++++++++++++++++++#"
+ "+ N │ column 0 │ column 1 │ column 2 +"
+ "@+++#──────────┼──────────*++++++++++."
+ "│ 0 + 0-0 │ 0-1 + 0-2 │"
+ "├───@+++++++++++++++++++++*++++++++++#"
+ "│ 1 │ 1-0 │ 1-1 + 1-2 +"
+ "├───┼──────────┼──────────@++++++++++."
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+ "└───┴──────────┴──────────┴──────────┘"
+ ),
+ );
+
+ test_highlight!(
+ Segment::all()
+ .not(Segment::new(2.., 0..3))
+ .not(Cell::new(1, 0)),
+ static_table!(
+ "*++++++++++++++++++++++++++++++++++++#"
+ "+ N │ column 0 │ column 1 │ column 2 +"
+ "@+++#──────────┼──────────┼──────────+"
+ "│ 0 + 0-0 │ 0-1 │ 0-2 +"
+ "├───@+++++++++++++++++++++#──────────+"
+ "│ 1 │ 1-0 │ 1-1 + 1-2 +"
+ "├───┼──────────┼──────────+──────────+"
+ "│ 2 │ 2-0 │ 2-1 + 2-2 +"
+ "└───┴──────────┴──────────@++++++++++."
+ ),
+ );
+
+ test_highlight!(
+ Segment::all()
+ .not(Segment::new(..1, 1..))
+ .not(Segment::new(1..2, 2..))
+ .not(Cell::new(2, 3)),
+ static_table!(
+ "*+++#──────────┬──────────┬──────────┐"
+ "+ N + column 0 │ column 1 │ column 2 │"
+ "+───@++++++++++#──────────┼──────────┤"
+ "+ 0 │ 0-0 + 0-1 │ 0-2 │"
+ "+───┼──────────@++++++++++#──────────┤"
+ "+ 1 │ 1-0 │ 1-1 + 1-2 │"
+ "+───┼──────────┼──────────@++++++++++#"
+ "+ 2 │ 2-0 │ 2-1 │ 2-2 +"
+ "@++++++++++++++++++++++++++++++++++++."
+ ),
+ );
+}
diff --git a/vendor/tabled/tests/settings/margin_test.rs b/vendor/tabled/tests/settings/margin_test.rs
new file mode 100644
index 000000000..513287350
--- /dev/null
+++ b/vendor/tabled/tests/settings/margin_test.rs
@@ -0,0 +1,195 @@
+#![cfg(feature = "std")]
+
+use tabled::settings::{object::Cell, Border, Highlight, Margin, Modify, Span, Style, Width};
+
+use crate::matrix::Matrix;
+use testing_table::{is_lines_equal, static_table, test_table};
+
+#[cfg(feature = "color")]
+use ::{owo_colors::OwoColorize, std::convert::TryFrom, tabled::settings::Color};
+
+test_table!(
+ margin_with_table_based_on_grid_borders,
+ Matrix::new(3, 3)
+ .with(Style::extended())
+ .with(Highlight::new(Cell::new(0, 0), Border::filled('+')))
+ .with(Highlight::new(Cell::new(1, 1), Border::filled('*')))
+ .with(Margin::new(1, 2, 1, 2).fill('>', '<', 'V', '^')),
+ "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV"
+ ">+++++══════════╦══════════╦══════════╗<<"
+ ">+ N + column 0 ║ column 1 ║ column 2 ║<<"
+ ">++++************══════════╬══════════╣<<"
+ ">║ 0 * 0-0 * 0-1 ║ 0-2 ║<<"
+ ">╠═══************══════════╬══════════╣<<"
+ ">║ 1 ║ 1-0 ║ 1-1 ║ 1-2 ║<<"
+ ">╠═══╬══════════╬══════════╬══════════╣<<"
+ ">║ 2 ║ 2-0 ║ 2-1 ║ 2-2 ║<<"
+ ">╚═══╩══════════╩══════════╩══════════╝<<"
+ "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
+ "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
+);
+
+test_table!(
+ margin_without_table_based_on_grid_borders,
+ Matrix::new(3, 3)
+ .insert((3, 2), "https://\nwww\n.\nredhat\n.com\n/en")
+ .with(Style::psql())
+ .with(Modify::new(Cell::new(3, 2)).with(Span::column(2)))
+ .with(Margin::new(1, 1, 1, 1).fill('>', '<', 'V', '^')),
+ "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV"
+ "> N | column 0 | column 1 | column 2 <"
+ ">---+----------+----------+----------<"
+ "> 0 | 0-0 | 0-1 | 0-2 <"
+ "> 1 | 1-0 | 1-1 | 1-2 <"
+ "> 2 | 2-0 | https:// <"
+ "> | | www <"
+ "> | | . <"
+ "> | | redhat <"
+ "> | | .com <"
+ "> | | /en <"
+ "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
+);
+
+test_table!(
+ table_with_empty_margin,
+ Matrix::new(3, 3)
+ .insert((3, 2), "https://\nwww\n.\nredhat\n.com\n/en")
+ .with(Style::psql())
+ .with(Modify::new(Cell::new(3, 2)).with(Span::column(2)))
+ .with(Margin::new(0, 0, 0, 0).fill('>', '<', 'V', '^')),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | https:// "
+ " | | www "
+ " | | . "
+ " | | redhat "
+ " | | .com "
+ " | | /en "
+);
+
+#[test]
+fn table_with_margin_and_min_width() {
+ let table = Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Cell::new(1, 1)).with(Span::column(2)))
+ .with(Margin::new(1, 1, 1, 1).fill('>', '<', 'V', '^'))
+ .with(Width::truncate(20))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "VVVVVVVVVVVVVVVVVVVV"
+ "> | co | co | col <"
+ ">--+----+----+-----<"
+ "> | 0-0 | 0-2 <"
+ "> | 1- | 1- | 1-2 <"
+ "> | 2- | 2- | 2-2 <"
+ "^^^^^^^^^^^^^^^^^^^^"
+ )
+ );
+ assert!(is_lines_equal(&table, 20));
+}
+
+#[test]
+fn table_with_margin_and_max_width() {
+ let table = Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Cell::new(1, 1)).with(Span::column(2)))
+ .with(Margin::new(1, 1, 1, 1).fill('>', '<', 'V', '^'))
+ .with(Width::increase(50))
+ .to_string();
+
+ assert_eq!(papergrid::util::string::string_width_multiline(&table), 50);
+ assert_eq!(
+ table,
+ static_table!(
+ "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV"
+ "> N | column 0 | column 1 | column 2 <"
+ ">------+-------------+-------------+-------------<"
+ "> 0 | 0-0 | 0-2 <"
+ "> 1 | 1-0 | 1-1 | 1-2 <"
+ "> 2 | 2-0 | 2-1 | 2-2 <"
+ "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
+ )
+ );
+}
+
+#[test]
+#[ignore = "It's not yet clear what to do with such spans"]
+fn table_0_spanned_with_width() {
+ let table = Matrix::table(0, 0)
+ .with(Modify::new(Cell::new(0, 0)).with(Span::column(0)))
+ .with(Width::increase(50))
+ .to_string();
+
+ assert_eq!(table, "++\n|\n++\n");
+
+ let table = Matrix::table(0, 0)
+ .with(Modify::new(Cell::new(0, 0)).with(Span::column(0)))
+ .with(Width::truncate(50))
+ .to_string();
+
+ assert_eq!(table, "++\n|\n++\n");
+}
+
+#[test]
+fn margin_color_test_not_colored_feature() {
+ use tabled::settings::Color;
+
+ let table = Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Margin::new(2, 2, 2, 2).fill('>', '<', 'V', '^').colorize(
+ Color::BG_GREEN,
+ Color::BG_YELLOW,
+ Color::BG_RED,
+ Color::BG_BLUE,
+ ))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "\u{1b}[41mVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV\u{1b}[49m"
+ "\u{1b}[41mVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV\u{1b}[49m"
+ "\u{1b}[42m>>\u{1b}[49m N | column 0 | column 1 | column 2 \u{1b}[43m<<\u{1b}[49m"
+ "\u{1b}[42m>>\u{1b}[49m---+----------+----------+----------\u{1b}[43m<<\u{1b}[49m"
+ "\u{1b}[42m>>\u{1b}[49m 0 | 0-0 | 0-1 | 0-2 \u{1b}[43m<<\u{1b}[49m"
+ "\u{1b}[42m>>\u{1b}[49m 1 | 1-0 | 1-1 | 1-2 \u{1b}[43m<<\u{1b}[49m"
+ "\u{1b}[42m>>\u{1b}[49m 2 | 2-0 | 2-1 | 2-2 \u{1b}[43m<<\u{1b}[49m"
+ "\u{1b}[44m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u{1b}[49m"
+ "\u{1b}[44m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u{1b}[49m"
+ )
+ );
+}
+
+#[cfg(feature = "color")]
+#[test]
+fn margin_color_test() {
+ let table = Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Margin::new(2, 2, 2, 2).fill('>', '<', 'V', '^').colorize(
+ Color::try_from(" ".red().bold().to_string()).unwrap(),
+ Color::try_from(" ".green().to_string()).unwrap(),
+ Color::try_from(" ".on_blue().red().bold().to_string()).unwrap(),
+ Color::try_from(" ".on_yellow().blue().to_string()).unwrap(),
+ ))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "\u{1b}[1m\u{1b}[31m\u{1b}[44mVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV\u{1b}[22m\u{1b}[39m\u{1b}[49m"
+ "\u{1b}[1m\u{1b}[31m\u{1b}[44mVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV\u{1b}[22m\u{1b}[39m\u{1b}[49m"
+ "\u{1b}[1m\u{1b}[31m>>\u{1b}[22m\u{1b}[39m N | column 0 | column 1 | column 2 \u{1b}[32m<<\u{1b}[39m"
+ "\u{1b}[1m\u{1b}[31m>>\u{1b}[22m\u{1b}[39m---+----------+----------+----------\u{1b}[32m<<\u{1b}[39m"
+ "\u{1b}[1m\u{1b}[31m>>\u{1b}[22m\u{1b}[39m 0 | 0-0 | 0-1 | 0-2 \u{1b}[32m<<\u{1b}[39m"
+ "\u{1b}[1m\u{1b}[31m>>\u{1b}[22m\u{1b}[39m 1 | 1-0 | 1-1 | 1-2 \u{1b}[32m<<\u{1b}[39m"
+ "\u{1b}[1m\u{1b}[31m>>\u{1b}[22m\u{1b}[39m 2 | 2-0 | 2-1 | 2-2 \u{1b}[32m<<\u{1b}[39m"
+ "\u{1b}[34m\u{1b}[43m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u{1b}[39m\u{1b}[49m"
+ "\u{1b}[34m\u{1b}[43m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u{1b}[39m\u{1b}[49m"
+ )
+ );
+}
diff --git a/vendor/tabled/tests/settings/merge_test.rs b/vendor/tabled/tests/settings/merge_test.rs
new file mode 100644
index 000000000..cbc119aa9
--- /dev/null
+++ b/vendor/tabled/tests/settings/merge_test.rs
@@ -0,0 +1,196 @@
+#![cfg(feature = "std")]
+
+use tabled::{settings::merge::Merge, Table};
+
+use testing_table::test_table;
+
+test_table!(
+ merge_horizontal,
+ Table::new([[0, 1, 1], [1, 1, 2], [1, 1, 1]]).with(Merge::horizontal()),
+ "+---+---+---+"
+ "| 0 | 1 | 2 |"
+ "+---+---+---+"
+ "| 0 | 1 |"
+ "+---+---+---+"
+ "| 1 | 2 |"
+ "+---+---+---+"
+ "| 1 |"
+ "+---+---+---+"
+);
+
+test_table!(
+ merge_horizontal_with_no_duplicates,
+ Table::new([[0, 1, 2], [0, 1, 2], [0, 1, 2]]).with(Merge::horizontal()),
+ "+---+---+---+"
+ "| 0 | 1 | 2 |"
+ "+---+---+---+"
+ "| 0 | 1 | 2 |"
+ "+---+---+---+"
+ "| 0 | 1 | 2 |"
+ "+---+---+---+"
+ "| 0 | 1 | 2 |"
+ "+---+---+---+"
+);
+
+test_table!(
+ merge_horizontal_empty,
+ Table::new([[0usize; 0]]).with(Merge::horizontal()),
+ ""
+);
+
+test_table!(
+ merge_vertical_0,
+ Table::new([[0, 3, 5], [0, 3, 3], [0, 2, 3]]).with(Merge::vertical()),
+ "+---+---+---+"
+ "| 0 | 1 | 2 |"
+ "+ +---+---+"
+ "| | 3 | 5 |"
+ "+ + +---+"
+ "+ +---+ 3 +"
+ "| | 2 | |"
+ "+---+---+---+"
+);
+
+test_table!(
+ merge_vertical_1,
+ Table::new([[0, 3, 2], [0, 3, 3], [0, 2, 3]]).with(Merge::vertical()),
+ "+---+---+---+"
+ "| 0 | 1 | 2 |"
+ "+ +---+ +"
+ "+ + 3 +---+"
+ "+ +---+ 3 +"
+ "| | 2 | |"
+ "+---+---+---+"
+);
+
+test_table!(
+ merge_vertical_with_no_duplicates,
+ Table::new([[5; 3], [15; 3], [115; 3]]).with(Merge::vertical()),
+ "+-----+-----+-----+"
+ "| 0 | 1 | 2 |"
+ "+-----+-----+-----+"
+ "| 5 | 5 | 5 |"
+ "+-----+-----+-----+"
+ "| 15 | 15 | 15 |"
+ "+-----+-----+-----+"
+ "| 115 | 115 | 115 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ merge_vertical_empty,
+ Table::new([[0usize; 0]]).with(Merge::vertical()),
+ ""
+);
+
+test_table!(
+ merge_horizontal_and_vertical_0,
+ Table::new([[3, 3, 5], [3, 7, 8], [9, 10, 11]]).with(Merge::horizontal()).with(Merge::vertical()),
+ "+---+----+----+"
+ "| 0 | 1 | 2 |"
+ "+---+----+----+"
+ "| 3 | 5 |"
+ "+---+----+----+"
+ "| 3 | 7 | 8 |"
+ "+---+----+----+"
+ "| 9 | 10 | 11 |"
+ "+---+----+----+"
+);
+
+test_table!(
+ merge_horizontal_and_vertical_1,
+ Table::new([[0, 1, 1], [1, 1, 2], [1, 1, 1]]).with(Merge::horizontal()).with(Merge::vertical()),
+ "+---+---+---+"
+ "| 0 | 1 | 2 |"
+ "+ +---+---+"
+ "| | 1 |"
+ "+---+---+---+"
+ "| 1 | 2 |"
+ "+---+---+---+"
+ "| 1 |"
+ "+---+---+---+"
+);
+
+test_table!(
+ merge_horizontal_and_vertical_2,
+ Table::new([[3, 4, 5], [3, 3, 8], [3, 10, 11]]).with(Merge::horizontal()).with(Merge::vertical()),
+ "+---+----+----+"
+ "| 0 | 1 | 2 |"
+ "+---+----+----+"
+ "| 3 | 4 | 5 |"
+ "+---+----+----+"
+ "| 3 | 8 |"
+ "+---+----+----+"
+ "| 3 | 10 | 11 |"
+ "+---+----+----+"
+);
+
+test_table!(
+ merge_horizontal_and_vertical_3,
+ Table::new([[3, 4, 5], [3, 3, 8], [3, 10, 11]]).with(Merge::vertical()).with(Merge::horizontal()),
+ "+---+----+----+"
+ "| 0 | 1 | 2 |"
+ "+---+----+----+"
+ "| 3 | 4 | 5 |"
+ "+ +----+----+"
+ "| | 3 | 8 |"
+ "+ +----+----+"
+ "| | 10 | 11 |"
+ "+---+----+----+"
+);
+
+test_table!(
+ merge_horizontal_and_vertical_4,
+ Table::new([[3, 4, 5], [4, 4, 8], [3, 4, 11]]).with(Merge::vertical()).with(Merge::horizontal()),
+ "+---+---+----+"
+ "| 0 | 1 | 2 |"
+ "+---+---+----+"
+ "| 3 | 4 | 5 |"
+ "+---+ +----+"
+ "| 4 | | 8 |"
+ "+---+ +----+"
+ "| 3 | | 11 |"
+ "+---+---+----+"
+);
+
+test_table!(
+ merge_horizontal_and_vertical_5,
+ Table::new([[3, 4, 4], [4, 4, 8], [3, 4, 11]]).with(Merge::vertical()).with(Merge::horizontal()),
+ "+---+---+----+"
+ "| 0 | 1 | 2 |"
+ "+---+---+----+"
+ "| 3 | 4 | 4 |"
+ "+---+ +----+"
+ "| 4 | | 8 |"
+ "+---+ +----+"
+ "| 3 | | 11 |"
+ "+---+---+----+"
+);
+
+test_table!(
+ merge_horizontal_and_vertical_6,
+ Table::new([[4, 4, 4], [5, 4, 8], [3, 4, 11]]).with(Merge::vertical()).with(Merge::horizontal()),
+ "+---+---+----+"
+ "| 0 | 1 | 2 |"
+ "+---+---+----+"
+ "| 4 | 4 | 4 |"
+ "+---+ +----+"
+ "| 5 | | 8 |"
+ "+---+ +----+"
+ "| 3 | | 11 |"
+ "+---+---+----+"
+);
+
+test_table!(
+ merge_horizontal_and_vertical_7,
+ Table::new([[0, 0, 0], [0, 0, 1], [2, 0, 0]]).with(Merge::horizontal()).with(Merge::vertical()),
+ "+---+---+---+"
+ "| 0 | 1 | 2 |"
+ "+---+---+---+"
+ "| 0 |"
+ "+---+---+---+"
+ "| 0 | 1 |"
+ "+---+---+---+"
+ "| 2 | 0 |"
+ "+---+---+---+"
+);
diff --git a/vendor/tabled/tests/settings/mod.rs b/vendor/tabled/tests/settings/mod.rs
new file mode 100644
index 000000000..da76a5e14
--- /dev/null
+++ b/vendor/tabled/tests/settings/mod.rs
@@ -0,0 +1,23 @@
+mod alignment_test;
+mod color_test;
+mod colorization;
+mod column_names_test;
+mod concat_test;
+mod disable_test;
+mod duplicate_test;
+mod extract_test;
+mod format_test;
+mod formatting_test;
+mod height_test;
+mod highlingt_test;
+mod margin_test;
+mod merge_test;
+mod padding_test;
+mod panel_test;
+mod render_settings;
+mod rotate_test;
+mod shadow_test;
+mod span_test;
+mod split_test;
+mod style_test;
+mod width_test;
diff --git a/vendor/tabled/tests/settings/padding_test.rs b/vendor/tabled/tests/settings/padding_test.rs
new file mode 100644
index 000000000..9a3edb4aa
--- /dev/null
+++ b/vendor/tabled/tests/settings/padding_test.rs
@@ -0,0 +1,138 @@
+#![cfg(feature = "std")]
+
+use tabled::settings::{
+ object::{Rows, Segment},
+ Alignment, Modify, Padding, Style,
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+#[cfg(feature = "color")]
+use ::{owo_colors::OwoColorize, std::convert::TryFrom, tabled::settings::Color};
+
+test_table!(
+ padding,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Rows::new(1..)).with(Padding::new(1, 1, 0, 2))),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " | | | "
+ " | | | "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " | | | "
+ " | | | "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ " | | | "
+ " | | | "
+);
+
+test_table!(
+ padding_with_set_characters,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Padding::new(1, 2, 1, 1).fill('>', '<', 'V', '^'))),
+ "VVVV|VVVVVVVVVVV|VVVVVVVVVVV|VVVVVVVVVVV"
+ ">N<<|>column 0<<|>column 1<<|>column 2<<"
+ "^^^^|^^^^^^^^^^^|^^^^^^^^^^^|^^^^^^^^^^^"
+ "----+-----------+-----------+-----------"
+ "VVVV|VVVVVVVVVVV|VVVVVVVVVVV|VVVVVVVVVVV"
+ ">0<<|> 0-0 <<|> 0-1 <<|> 0-2 <<"
+ "^^^^|^^^^^^^^^^^|^^^^^^^^^^^|^^^^^^^^^^^"
+ "VVVV|VVVVVVVVVVV|VVVVVVVVVVV|VVVVVVVVVVV"
+ ">1<<|> 1-0 <<|> 1-1 <<|> 1-2 <<"
+ "^^^^|^^^^^^^^^^^|^^^^^^^^^^^|^^^^^^^^^^^"
+ "VVVV|VVVVVVVVVVV|VVVVVVVVVVV|VVVVVVVVVVV"
+ ">2<<|> 2-0 <<|> 2-1 <<|> 2-2 <<"
+ "^^^^|^^^^^^^^^^^|^^^^^^^^^^^|^^^^^^^^^^^"
+);
+
+test_table!(
+ padding_with_set_characters_and_zero_ident,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Padding::zero().fill('>', '<', '^', 'V'))),
+ "N|column 0|column 1|column 2"
+ "-+--------+--------+--------"
+ "0| 0-0 | 0-1 | 0-2 "
+ "1| 1-0 | 1-1 | 1-2 "
+ "2| 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ padding_multiline,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Rows::new(1..)).with(Padding::new(1, 1, 1, 1))),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " | | | "
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " | | | "
+ " | | | "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " | | | "
+ " | | | "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ " | | | "
+);
+
+test_table!(
+ padding_multiline_with_vertical_alignment,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::center()).with(Alignment::center_vertical()))
+ .with(Modify::new(Rows::new(1..)).with(Padding::new(1, 1, 1, 1))),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " | | | "
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " | | | "
+ " | | | "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " | | | "
+ " | | | "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ " | | | "
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ padding_color,
+ {
+ let padding = Padding::new(2, 2, 2, 2).colorize(
+ Color::try_from(' '.on_yellow().to_string()).unwrap(),
+ Color::try_from(' '.on_blue().to_string()).unwrap(),
+ Color::try_from(' '.on_red().to_string()).unwrap(),
+ Color::try_from(' '.on_green().to_string()).unwrap(),
+ );
+
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Rows::new(1..)).with(padding))
+ },
+ " N | column 0 | column 1 | column 2 \n-----+----------+----------+----------\n\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\n\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\n\u{1b}[43m \u{1b}[49m0\u{1b}[44m \u{1b}[49m|\u{1b}[43m \u{1b}[49m 0-0 \u{1b}[44m \u{1b}[49m|\u{1b}[43m \u{1b}[49m 0-1 \u{1b}[44m \u{1b}[49m|\u{1b}[43m \u{1b}[49m 0-2 \u{1b}[44m \u{1b}[49m\n\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m\n\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m\n\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\n\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\n\u{1b}[43m \u{1b}[49m1\u{1b}[44m \u{1b}[49m|\u{1b}[43m \u{1b}[49m 1-0 \u{1b}[44m \u{1b}[49m|\u{1b}[43m \u{1b}[49m 1-1 \u{1b}[44m \u{1b}[49m|\u{1b}[43m \u{1b}[49m 1-2 \u{1b}[44m \u{1b}[49m\n\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m\n\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m\n\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\n\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m|\u{1b}[41m \u{1b}[49m\n\u{1b}[43m \u{1b}[49m2\u{1b}[44m \u{1b}[49m|\u{1b}[43m \u{1b}[49m 2-0 \u{1b}[44m \u{1b}[49m|\u{1b}[43m \u{1b}[49m 2-1 \u{1b}[44m \u{1b}[49m|\u{1b}[43m \u{1b}[49m 2-2 \u{1b}[44m \u{1b}[49m\n\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m\n\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m|\u{1b}[42m \u{1b}[49m"
+);
+
+test_table!(
+ padding_table,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Padding::new(1, 1, 0, 2)),
+ " N | column 0 | column 1 | column 2 "
+ " | | | "
+ " | | | "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " | | | "
+ " | | | "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " | | | "
+ " | | | "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ " | | | "
+ " | | | "
+);
diff --git a/vendor/tabled/tests/settings/panel_test.rs b/vendor/tabled/tests/settings/panel_test.rs
new file mode 100644
index 000000000..613fe1d0e
--- /dev/null
+++ b/vendor/tabled/tests/settings/panel_test.rs
@@ -0,0 +1,337 @@
+#![cfg(feature = "std")]
+
+use tabled::settings::{
+ object::{Cell, Object, Rows, Segment},
+ style::{BorderSpanCorrection, HorizontalLine},
+ Alignment, Border, Highlight, Modify, Panel, Span, Style, Width,
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+test_table!(
+ panel_has_no_style_by_default,
+ Matrix::new(3, 3).with(Style::psql()).with(Panel::horizontal(0,"Linux Distributions")),
+ " Linux Distributions "
+ "---+----------+----------+----------"
+ " N | column 0 | column 1 | column 2 "
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ highlight_panel_0,
+ Matrix::new(3, 3)
+ .with(Panel::horizontal(0,"Linux Distributions"))
+ .with(Style::psql())
+ .with(Highlight::new(Cell::new(0, 0), Border::filled('#'))),
+ "##### "
+ "# Linux Distributions "
+ "#####----------+----------+----------"
+ " N | column 0 | column 1 | column 2 "
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ highlight_panel_1,
+ Matrix::new(3, 3)
+ .with(Panel::horizontal(0,"Linux Distributions"))
+ .with(Style::psql())
+ .with(Highlight::new(Cell::new(0, 0), Border::filled('#')))
+ .with(Highlight::new(Cell::new(0, 1), Border::filled('#')))
+ .with(Highlight::new(Cell::new(0, 2), Border::filled('#')))
+ .with(Highlight::new(Cell::new(0, 3), Border::filled('#'))),
+ "######################################"
+ "# Linux Distributions #"
+ "######################################"
+ " N | column 0 | column 1 | column 2 "
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ top_panel,
+ Matrix::new(3, 3)
+ .with(Panel::horizontal(0,"Linux Distributions"))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::psql()),
+ " Linux Distributions "
+ "---+----------+----------+----------"
+ " N | column 0 | column 1 | column 2 "
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ bottom_panel,
+ Matrix::new(3, 3)
+ .with(Panel::horizontal(4,"Linux Distributions"))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::psql()),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ " Linux Distributions "
+);
+
+test_table!(
+ inner_panel,
+ Matrix::new(3, 3)
+ .with(Panel::horizontal(2,"Linux Distributions"))
+ .with(Modify::new(Rows::new(2..)).with(Alignment::center()))
+ .with(Style::psql()),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " Linux Distributions "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ header,
+ Matrix::new(3, 3)
+ .with(Panel::header("Linux Distributions"))
+ .with(Style::psql())
+ .with(Modify::new(Rows::new(0..1)).with(Alignment::center())),
+ " Linux Distributions "
+ "---+----------+----------+----------"
+ " N | column 0 | column 1 | column 2 "
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ footer,
+ Matrix::new(3, 3)
+ .with(Panel::header("Linux Distributions"))
+ .with(Panel::footer("The end"))
+ .with(Style::psql())
+ .with(Modify::new(Rows::first().and(Rows::last())).with(Alignment::center())),
+ " Linux Distributions "
+ "---+----------+----------+----------"
+ " N | column 0 | column 1 | column 2 "
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ " The end "
+);
+
+test_table!(
+ panel_style_uses_most_left_and_right_cell_styles,
+ Matrix::iter([(0, 1)]).with(Panel::horizontal(0,"Numbers")).with(Style::modern()),
+ "┌─────┬─────┐"
+ "│ Numbers │"
+ "├─────┼─────┤"
+ "│ i32 │ i32 │"
+ "├─────┼─────┤"
+ "│ 0 │ 1 │"
+ "└─────┴─────┘"
+);
+
+test_table!(
+ panel_style_change,
+ Matrix::iter([(0, 1)])
+ .with(Panel::horizontal(0,"Numbers"))
+ .with(Style::modern().intersection_top('─').horizontals([HorizontalLine::new(1, Style::modern().get_horizontal()).intersection(Some('┬'))]))
+ .with(Modify::new(Cell::new(0, 0)).with(Alignment::center())),
+ "┌───────────┐"
+ "│ Numbers │"
+ "├─────┬─────┤"
+ "│ i32 │ i32 │"
+ "├─────┼─────┤"
+ "│ 0 │ 1 │"
+ "└─────┴─────┘"
+);
+
+test_table!(
+ panel_style_uses_most_left_and_right_cell_styles_correct,
+ Matrix::iter([(0, 1)])
+ .with(Panel::horizontal(0,"Numbers"))
+ .with(Style::modern())
+ .with(BorderSpanCorrection),
+ "┌───────────┐"
+ "│ Numbers │"
+ "├─────┬─────┤"
+ "│ i32 │ i32 │"
+ "├─────┼─────┤"
+ "│ 0 │ 1 │"
+ "└─────┴─────┘"
+);
+
+test_table!(
+ panel_style_change_correct,
+ Matrix::iter([(0, 1)])
+ .with(Panel::horizontal(0,"Numbers"))
+ .with(Style::modern().intersection_top('─').horizontals([HorizontalLine::new(1, Style::modern().get_horizontal()).intersection(Some('┬'))]))
+ .with(BorderSpanCorrection)
+ .with(Modify::new(Cell::new(0, 0)).with(Alignment::center())),
+ "┌───────────┐"
+ "│ Numbers │"
+ "├───────────┤" // it's different because we use a top_intersection char by default when making style for `Panel`s.
+ "│ i32 │ i32 │"
+ "├─────┼─────┤"
+ "│ 0 │ 1 │"
+ "└─────┴─────┘"
+);
+
+test_table!(
+ panel_in_single_column,
+ #[allow(clippy::needless_borrow)]
+ Matrix::iter(&[(0)]).with(Panel::horizontal(0,"Numbers")).with(Style::modern()),
+ "┌─────────┐"
+ "│ Numbers │"
+ "├─────────┤"
+ "│ i32 │"
+ "├─────────┤"
+ "│ 0 │"
+ "└─────────┘"
+);
+
+test_table!(
+ panel_vertical_0,
+ Matrix::new(3, 3).with(Style::psql()).with(Panel::vertical(0,"Linux Distributions")),
+ " Linux Distributions | N | column 0 | column 1 | column 2 "
+ " +---+----------+----------+----------"
+ " | 0 | 0-0 | 0-1 | 0-2 "
+ " | 1 | 1-0 | 1-1 | 1-2 "
+ " | 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ panel_vertical_1,
+ Matrix::new(3, 3).with(Style::psql()).with(Panel::vertical(1,"Linux Distributions")),
+ " N | Linux Distributions | column 0 | column 1 | column 2 "
+ "---+ +----------+----------+----------"
+ " 0 | | 0-0 | 0-1 | 0-2 "
+ " 1 | | 1-0 | 1-1 | 1-2 "
+ " 2 | | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ panel_vertical_2,
+ Matrix::new(3, 3).with(Style::psql()).with(Panel::vertical(4,"Linux Distributions")),
+ " N | column 0 | column 1 | column 2 | Linux Distributions "
+ "---+----------+----------+----------+ "
+ " 0 | 0-0 | 0-1 | 0-2 | "
+ " 1 | 1-0 | 1-1 | 1-2 | "
+ " 2 | 2-0 | 2-1 | 2-2 | "
+);
+
+test_table!(
+ panel_vertical_0_wrap,
+ Matrix::new(3, 3).with(Style::psql()).with(Panel::vertical(0,"Linux Distributions")).with(Modify::new(Cell::new(0, 0)).with(Width::wrap(3))),
+ " Lin | N | column 0 | column 1 | column 2 "
+ " ux | | | | "
+ " Dis | | | | "
+ " tri +---+----------+----------+----------"
+ " but | 0 | 0-0 | 0-1 | 0-2 "
+ " ion | 1 | 1-0 | 1-1 | 1-2 "
+ " s | 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ panel_vertical_0_wrap_0,
+ Matrix::new(3, 3).with(Style::psql()).with(Panel::vertical(0,"Linux Distributions")).with(Modify::new(Cell::new(0, 0)).with(Width::wrap(0))),
+ " | N | column 0 | column 1 | column 2 "
+ " +---+----------+----------+----------"
+ " | 0 | 0-0 | 0-1 | 0-2 "
+ " | 1 | 1-0 | 1-1 | 1-2 "
+ " | 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ panel_vertical_0_wrap_100,
+ Matrix::new(3, 3).with(Style::psql()).with(Panel::vertical(0,"Linux Distributions")).with(Modify::new(Cell::new(0, 0)).with(Width::wrap(100))),
+ " Linux Distributions | N | column 0 | column 1 | column 2 "
+ " +---+----------+----------+----------"
+ " | 0 | 0-0 | 0-1 | 0-2 "
+ " | 1 | 1-0 | 1-1 | 1-2 "
+ " | 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ panel_horizontal_set_0,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Panel::horizontal(0,"Linux Distributions"))
+ .with(Panel::vertical(0,"asd")),
+ " asd | Linux Distributions "
+ " +---+----------+----------+----------"
+ " | N | column 0 | column 1 | column 2 "
+ " | 0 | 0-0 | 0-1 | 0-2 "
+ " | 1 | 1-0 | 1-1 | 1-2 "
+ " | 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ panel_horizontal_set_1,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Panel::horizontal(0,"Linux Distributions"))
+ .with(Panel::vertical(0,"asd"))
+ .with(Panel::vertical(5,"asd"))
+ ,
+ " asd | Linux Distributions | asd "
+ " +---+----------+----------+----------+ "
+ " | N | column 0 | column 1 | column 2 | "
+ " | 0 | 0-0 | 0-1 | 0-2 | "
+ " | 1 | 1-0 | 1-1 | 1-2 | "
+ " | 2 | 2-0 | 2-1 | 2-2 | "
+);
+
+test_table!(
+ ignore_col_span_intersect_with_other_span,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Panel::horizontal(0,"Linux Distributions"))
+ .with(Panel::vertical(0,"asd"))
+ .with(Panel::vertical(5,"zxc"))
+ .with(Modify::new((1, 3)).with(Span::column(3)).with("wwwww")),
+ " asd | Linux Distributions | zxc "
+ " +---+----------+-------+----------+ "
+ " | N | column 0 | wwwww | column 2 | "
+ " | 0 | 0-0 | 0-1 | 0-2 | "
+ " | 1 | 1-0 | 1-1 | 1-2 | "
+ " | 2 | 2-0 | 2-1 | 2-2 | "
+);
+
+test_table!(
+ panel_horizontal_x_2,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Panel::horizontal(0,"Linux Distributions"))
+ .with(Panel::vertical(0,"asd"))
+ .with(Panel::vertical(5,"zxc"))
+ .with(Modify::new((1, 3)).with(Span::column(2)).with("wwwww")),
+ " asd | Linux Distributions | zxc "
+ " +---+----------+-----+-----+ "
+ " | N | column 0 | wwwww | "
+ " | 0 | 0-0 | 0-1 | 0-2 | "
+ " | 1 | 1-0 | 1-1 | 1-2 | "
+ " | 2 | 2-0 | 2-1 | 2-2 | "
+);
+
+test_table!(
+ ignore_row_span_intersect_with_other_span,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Panel::horizontal(2,"Linux Distributions"))
+ .with(Panel::vertical(0,"asd"))
+ .with(Panel::vertical(5,"zxc"))
+ .with(Modify::new((0, 3)).with(Span::row(4)).with("xxxxx")),
+ " asd | N | column 0 | xxxxx | column 2 | zxc "
+ " +---+----------+-------+----------+ "
+ " | 0 | 0-0 | 0-1 | 0-2 | "
+ " | Linux Distributions | "
+ " | 1 | 1-0 | 1-1 | 1-2 | "
+ " | 2 | 2-0 | 2-1 | 2-2 | "
+);
diff --git a/vendor/tabled/tests/settings/render_settings.rs b/vendor/tabled/tests/settings/render_settings.rs
new file mode 100644
index 000000000..f25a59a67
--- /dev/null
+++ b/vendor/tabled/tests/settings/render_settings.rs
@@ -0,0 +1,292 @@
+#![cfg(feature = "std")]
+
+use tabled::settings::{
+ formatting::{AlignmentStrategy, TabSize, TrimStrategy},
+ object::Segment,
+ Alignment, Modify, Span, Style,
+};
+
+use crate::matrix::{Matrix, MatrixList};
+use testing_table::test_table;
+
+#[cfg(feature = "color")]
+use owo_colors::OwoColorize;
+
+test_table!(
+ alignment_per_line,
+ Matrix::iter(multiline_data1())
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::right()).with(AlignmentStrategy::PerLine)),
+ " N | column 0 | column 1 | column 2 "
+ "-----------+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " asd | 1-0 | 1-1 | 1-2 "
+ " 21213123 | | | "
+ " | | | "
+ " asdasd | | | "
+ " | | | "
+ " | | | "
+ " 2 | 2-0 | https:// | 2-2 "
+ " | | www | "
+ " | | . | "
+ " | | redhat | "
+ " | | .com | "
+ " | | /en | "
+);
+
+test_table!(
+ alignment_per_line_with_trim_0,
+ Matrix::iter(multiline_data1())
+ .with(Style::psql())
+ .with(Alignment::right())
+ .with(AlignmentStrategy::PerLine)
+ .with(TrimStrategy::Horizontal),
+ " N | column 0 | column 1 | column 2 "
+ "-----------+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " asd | 1-0 | 1-1 | 1-2 "
+ " 21213123 | | | "
+ " | | | "
+ " asdasd | | | "
+ " | | | "
+ " | | | "
+ " 2 | 2-0 | https:// | 2-2 "
+ " | | www | "
+ " | | . | "
+ " | | redhat | "
+ " | | .com | "
+ " | | /en | "
+);
+
+test_table!(
+ alignment_per_line_with_trim_1,
+ Matrix::iter(multiline_data2())
+ .with(Style::psql())
+ .with(Modify::new(Segment::all())
+ .with(Alignment::center_vertical())
+ .with(Alignment::left())
+ .with(AlignmentStrategy::PerLine)
+ .with(TrimStrategy::Both)),
+ " N | column 0 | column 1 | column 2 "
+ "-------------------+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " | | | "
+ " | | | "
+ " | | | "
+ " asd | 1-0 | 1-1 | 1-2 "
+ " 21213123 asdasd | | | "
+ " | | | "
+ " | | | "
+ " | | | "
+ " | | https:// | "
+ " | | www | "
+ " 2 | 2-0 | . | 2-2 "
+ " | | redhat | "
+ " | | .com | "
+ " | | /en | "
+);
+
+test_table!(
+ tab_isnot_handled_by_default_test,
+ Matrix::iter(tab_data1()).with(Style::psql()),
+ " N | column 0 | column 1 | column 2 "
+ "--------------+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 123\t123\tasdasd | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | htt\tps:// | 2-2 "
+ " | | www | "
+ " | | . | "
+ " | | red\that | "
+ " | | .c\tom | "
+ " | | /en | "
+);
+
+test_table!(
+ tab_size_test_0,
+ Matrix::iter(tab_data1()).with(Style::psql()).with(TabSize::new(4)),
+ " N | column 0 | column 1 | column 2 "
+ "----------------------+----------+--------------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 123 123 asdasd | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | htt ps:// | 2-2 "
+ " | | www | "
+ " | | . | "
+ " | | red hat | "
+ " | | .c om | "
+ " | | /en | "
+);
+
+test_table!(
+ tab_size_test_1,
+ Matrix::iter(tab_data1()).with(Style::psql()).with(Modify::new(Segment::all()).with(Alignment::right())).with(TabSize::new(2)),
+ " N | column 0 | column 1 | column 2 "
+ "------------------+----------+------------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 123 123 asdasd | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | htt ps:// | 2-2 "
+ " | | www | "
+ " | | . | "
+ " | | red hat | "
+ " | | .c om | "
+ " | | /en | "
+);
+
+test_table!(
+ tab_size_test_2,
+ Matrix::iter(tab_data1()).with(Style::psql()).with(Modify::new(Segment::all()).with(Alignment::right())).with(TabSize::new(0)),
+ " N | column 0 | column 1 | column 2 "
+ "--------------+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 123123asdasd | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | https:// | 2-2 "
+ " | | www | "
+ " | | . | "
+ " | | redhat | "
+ " | | .com | "
+ " | | /en | "
+);
+
+test_table!(
+ tab_size_span_test,
+ Matrix::iter(tab_data2())
+ .with(TabSize::new(4))
+ .with(Style::psql())
+ .with(Modify::new((0, 0)).with(Span::column(3)))
+ .with(Modify::new((1, 0)).with(Span::column(2)))
+ .with(Modify::new((2, 1)).with(Span::column(2))),
+ " N | column 2 "
+ "----------------------+-----+--------------+----------"
+ " H ello World | 0-1 | 0-2 "
+ " 123 123 asdasd | 1-0 | 1-2 "
+ " 2 | 2-0 | htt ps:// | 2-2 "
+ " | | www | "
+ " | | . | "
+ " | | red hat | "
+ " | | .c om | "
+ " | | /en | "
+);
+
+test_table!(
+ test_top_alignment_and_vertical_trim_1,
+ Matrix::iter(vec![" \n\n\n Hello World"])
+ .with(Style::modern())
+ .with(Modify::new(Segment::all()).with(Alignment::top()).with(TrimStrategy::Vertical)),
+ "┌─────────────────┐"
+ "│ &str │"
+ "├─────────────────┤"
+ "│ Hello World │"
+ "│ │"
+ "│ │"
+ "│ │"
+ "└─────────────────┘"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ trim_colored_string_test_2,
+ Matrix::iter(colored_data())
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::right()).with(TrimStrategy::None)),
+ " N | column 0 | column 1 | column 2 "
+ "-----------+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " \u{1b}[31masd\u{1b}[39m | 1-0 | 1-1 | 1-2 "
+ " \u{1b}[31m21213123\u{1b}[39m | | | "
+ " | | | "
+ " \u{1b}[31m asdasd\u{1b}[39m | | | "
+ " | | | "
+ " \u{1b}[31m\u{1b}[39m | | | "
+ " 2 | 2-0 | \u{1b}[44mhttps://\u{1b}[49m | 2-2 "
+ " | | \u{1b}[44mwww\u{1b}[49m | "
+ " | | \u{1b}[44m.\u{1b}[49m | "
+ " | | \u{1b}[44mredhat\u{1b}[49m | "
+ " | | \u{1b}[44m.com\u{1b}[49m | "
+ " | | \u{1b}[44m/en\u{1b}[49m | "
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ trim_colored_string_test_1,
+ Matrix::iter(colored_data())
+ .with(Style::psql())
+ .with(
+ Modify::new(Segment::all())
+ .with(Alignment::right())
+ .with(TrimStrategy::Horizontal)
+ .with(AlignmentStrategy::PerLine),
+ ),
+ " N | column 0 | column 1 | column 2 "
+ "-----------+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " \u{1b}[31masd\u{1b}[39m | 1-0 | 1-1 | 1-2 "
+ " \u{1b}[31m21213123\u{1b}[39m | | | "
+ " | | | "
+ " \u{1b}[31masdasd\u{1b}[39m | | | "
+ " | | | "
+ " \u{1b}[31m\u{1b}[39m | | | "
+ " 2 | 2-0 | \u{1b}[44mhttps://\u{1b}[49m | 2-2 "
+ " | | \u{1b}[44mwww\u{1b}[49m | "
+ " | | \u{1b}[44m.\u{1b}[49m | "
+ " | | \u{1b}[44mredhat\u{1b}[49m | "
+ " | | \u{1b}[44m.com\u{1b}[49m | "
+ " | | \u{1b}[44m/en\u{1b}[49m | "
+);
+#[cfg(feature = "color")]
+test_table!(
+ trim_colored_string_test_0,
+ Matrix::iter(colored_data())
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::right()).with(TrimStrategy::Horizontal)),
+ " N | column 0 | column 1 | column 2 "
+ "-----------+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " \u{1b}[31masd\u{1b}[39m | 1-0 | 1-1 | 1-2 "
+ " \u{1b}[31m21213123\u{1b}[39m | | | "
+ " | | | "
+ " \u{1b}[31masdasd\u{1b}[39m | | | "
+ " | | | "
+ " \u{1b}[31m\u{1b}[39m | | | "
+ " 2 | 2-0 | \u{1b}[44mhttps://\u{1b}[49m | 2-2 "
+ " | | \u{1b}[44mwww\u{1b}[49m | "
+ " | | \u{1b}[44m.\u{1b}[49m | "
+ " | | \u{1b}[44mredhat\u{1b}[49m | "
+ " | | \u{1b}[44m.com\u{1b}[49m | "
+ " | | \u{1b}[44m/en\u{1b}[49m | "
+);
+
+fn multiline_data1() -> Vec<MatrixList<3, true>> {
+ let mut data = Matrix::list::<3, 3>();
+ data[1][0] = String::from("asd\n21213123\n\n asdasd\n\n");
+ data[2][2] = String::from("https://\nwww\n.\nredhat\n.com\n/en");
+ data
+}
+
+fn multiline_data2() -> Vec<MatrixList<3, true>> {
+ let mut data = Matrix::list::<3, 3>();
+ data[1][0] = String::from("\n\n\nasd\n21213123 asdasd\n\n\n");
+ data[2][2] = String::from("https://\nwww\n.\nredhat\n.com\n/en");
+ data
+}
+
+fn tab_data1() -> Vec<MatrixList<3, true>> {
+ let mut data = Matrix::list::<3, 3>();
+ data[1][0] = String::from("123\t123\tasdasd");
+ data[2][2] = String::from("htt\tps://\nwww\n.\nred\that\n.c\tom\n/en");
+ data
+}
+
+fn tab_data2() -> Vec<MatrixList<3, true>> {
+ let mut data = Matrix::list::<3, 3>();
+ data[0][0] = String::from("\tH\t\tello\tWorld");
+ data[1][0] = String::from("123\t123\tasdasd");
+ data[2][2] = String::from("htt\tps://\nwww\n.\nred\that\n.c\tom\n/en");
+ data
+}
+
+#[cfg(feature = "color")]
+fn colored_data() -> Vec<MatrixList<3, true>> {
+ let mut data = Matrix::list::<3, 3>();
+ data[1][0] = "asd\n21213123\n\n asdasd\n\n".red().to_string();
+ data[2][2] = "https://\nwww\n.\nredhat\n.com\n/en".on_blue().to_string();
+ data
+}
diff --git a/vendor/tabled/tests/settings/rotate_test.rs b/vendor/tabled/tests/settings/rotate_test.rs
new file mode 100644
index 000000000..42a781818
--- /dev/null
+++ b/vendor/tabled/tests/settings/rotate_test.rs
@@ -0,0 +1,214 @@
+#![cfg(feature = "std")]
+
+// todo: add method for SPACING between cells.
+
+use tabled::settings::{
+ object::{Cell, Rows},
+ Border, Highlight, Rotate,
+};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+#[test]
+fn test_rotate() {
+ let table = || Matrix::iter([(123, 456, 789), (234, 567, 891)]);
+
+ assert_eq!(
+ table()
+ .with(Rotate::Left)
+ .with(Rotate::Left)
+ .with(Rotate::Left)
+ .with(Rotate::Left)
+ .to_string(),
+ table().to_string()
+ );
+ assert_eq!(
+ table()
+ .with(Rotate::Right)
+ .with(Rotate::Right)
+ .with(Rotate::Right)
+ .with(Rotate::Right)
+ .to_string(),
+ table().to_string()
+ );
+ assert_eq!(
+ table().with(Rotate::Right).with(Rotate::Left).to_string(),
+ table().to_string()
+ );
+ assert_eq!(
+ table().with(Rotate::Left).with(Rotate::Right).to_string(),
+ table().to_string()
+ );
+ assert_eq!(
+ table().with(Rotate::Bottom).with(Rotate::Top).to_string(),
+ table().to_string()
+ );
+ assert_eq!(
+ table()
+ .with(Rotate::Bottom)
+ .with(Rotate::Bottom)
+ .to_string(),
+ table().to_string()
+ );
+ assert_eq!(
+ table().with(Rotate::Top).with(Rotate::Top).to_string(),
+ table().to_string()
+ );
+}
+
+test_table!(
+ test_3x3_box_0,
+ Matrix::iter([(123, 456, 789), (234, 567, 891)]).with(Rotate::Left),
+ "+-----+-----+-----+"
+ "| i32 | 789 | 891 |"
+ "+-----+-----+-----+"
+ "| i32 | 456 | 567 |"
+ "+-----+-----+-----+"
+ "| i32 | 123 | 234 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ test_3x3_box_1,
+ Matrix::iter([(123, 456, 789), (234, 567, 891)]).with(Rotate::Left).with(Rotate::Right).with(Rotate::Right),
+ "+-----+-----+-----+"
+ "| 234 | 123 | i32 |"
+ "+-----+-----+-----+"
+ "| 567 | 456 | i32 |"
+ "+-----+-----+-----+"
+ "| 891 | 789 | i32 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ test_left_rotate,
+ Matrix::iter([(123, 456, 789), (234, 567, 891), (111, 222, 333)]).with(Rotate::Left),
+ "+-----+-----+-----+-----+"
+ "| i32 | 789 | 891 | 333 |"
+ "+-----+-----+-----+-----+"
+ "| i32 | 456 | 567 | 222 |"
+ "+-----+-----+-----+-----+"
+ "| i32 | 123 | 234 | 111 |"
+ "+-----+-----+-----+-----+"
+);
+
+test_table!(
+ test_right_rotate,
+ Matrix::iter([(123, 456, 789), (234, 567, 891), (111, 222, 333)]).with(Rotate::Right),
+ "+-----+-----+-----+-----+"
+ "| 111 | 234 | 123 | i32 |"
+ "+-----+-----+-----+-----+"
+ "| 222 | 567 | 456 | i32 |"
+ "+-----+-----+-----+-----+"
+ "| 333 | 891 | 789 | i32 |"
+ "+-----+-----+-----+-----+"
+);
+
+test_table!(
+ test_bottom_rotate,
+ Matrix::iter([(123, 456, 789), (234, 567, 891), (111, 222, 333)]).with(Rotate::Bottom),
+ "+-----+-----+-----+"
+ "| 111 | 222 | 333 |"
+ "+-----+-----+-----+"
+ "| 234 | 567 | 891 |"
+ "+-----+-----+-----+"
+ "| 123 | 456 | 789 |"
+ "+-----+-----+-----+"
+ "| i32 | i32 | i32 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ test_top_rotate,
+ Matrix::iter([(123, 456, 789), (234, 567, 891), (111, 222, 333)]).with(Rotate::Top),
+ "+-----+-----+-----+"
+ "| 111 | 222 | 333 |"
+ "+-----+-----+-----+"
+ "| 234 | 567 | 891 |"
+ "+-----+-----+-----+"
+ "| 123 | 456 | 789 |"
+ "+-----+-----+-----+"
+ "| i32 | i32 | i32 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ rotate_preserve_border_styles_test_0,
+ Matrix::iter([(123, 456, 789), (234, 567, 891), (111, 222, 333)])
+ .with(Highlight::new(Rows::single(0), Border::default().top('*')))
+ .with(Rotate::Left),
+ "+*****************+-----+"
+ "| i32 | 789 | 891 | 333 |"
+ "+-----+-----+-----+-----+"
+ "| i32 | 456 | 567 | 222 |"
+ "+-----+-----+-----+-----+"
+ "| i32 | 123 | 234 | 111 |"
+ "+-----+-----+-----+-----+"
+);
+
+// it's a correct behaviour because
+// when we sen bottom border of cell(0, 2) we also set top border of cell(1, 2)
+//
+// todo: determine if it's correct
+test_table!(
+ rotate_preserve_border_styles_test_1,
+ Matrix::iter([(123, 456, 789), (234, 567, 891), (111, 222, 333)])
+ .with(Highlight::new(Cell::new(0, 2), Border::default().bottom('*')))
+ .with(Rotate::Left),
+ "+-----+-----+-----+-----+"
+ "| i32 | 789 | 891 | 333 |"
+ "+-----+-----+*****+-----+"
+ "| i32 | 456 | 567 | 222 |"
+ "+-----+-----+-----+-----+"
+ "| i32 | 123 | 234 | 111 |"
+ "+-----+-----+-----+-----+"
+);
+
+test_table!(
+ test_left_rotate_1,
+ Matrix::iter([(0, 1, 2, 3, 4, 5), (0, 1, 2, 3, 4, 5)]).with(Rotate::Left),
+ "+-----+---+---+"
+ "| i32 | 5 | 5 |"
+ "+-----+---+---+"
+ "| i32 | 4 | 4 |"
+ "+-----+---+---+"
+ "| i32 | 3 | 3 |"
+ "+-----+---+---+"
+ "| i32 | 2 | 2 |"
+ "+-----+---+---+"
+ "| i32 | 1 | 1 |"
+ "+-----+---+---+"
+ "| i32 | 0 | 0 |"
+ "+-----+---+---+"
+);
+
+test_table!(
+ test_right_rotate_1,
+ Matrix::iter([(0, 1, 2, 3, 4, 5), (0, 1, 2, 3, 4, 5)]).with(Rotate::Right),
+ "+---+---+-----+"
+ "| 0 | 0 | i32 |"
+ "+---+---+-----+"
+ "| 1 | 1 | i32 |"
+ "+---+---+-----+"
+ "| 2 | 2 | i32 |"
+ "+---+---+-----+"
+ "| 3 | 3 | i32 |"
+ "+---+---+-----+"
+ "| 4 | 4 | i32 |"
+ "+---+---+-----+"
+ "| 5 | 5 | i32 |"
+ "+---+---+-----+"
+);
+
+test_table!(
+ test_bottom_rotate_1,
+ Matrix::iter([(0, 1, 2, 3, 4, 5), (0, 1, 2, 3, 4, 5)]).with(Rotate::Bottom),
+ "+-----+-----+-----+-----+-----+-----+"
+ "| 0 | 1 | 2 | 3 | 4 | 5 |"
+ "+-----+-----+-----+-----+-----+-----+"
+ "| 0 | 1 | 2 | 3 | 4 | 5 |"
+ "+-----+-----+-----+-----+-----+-----+"
+ "| i32 | i32 | i32 | i32 | i32 | i32 |"
+ "+-----+-----+-----+-----+-----+-----+"
+);
diff --git a/vendor/tabled/tests/settings/shadow_test.rs b/vendor/tabled/tests/settings/shadow_test.rs
new file mode 100644
index 000000000..2d169698c
--- /dev/null
+++ b/vendor/tabled/tests/settings/shadow_test.rs
@@ -0,0 +1,105 @@
+#![cfg(feature = "std")]
+
+use tabled::settings::{Shadow, Style};
+
+use crate::matrix::Matrix;
+use testing_table::test_table;
+
+#[cfg(feature = "color")]
+use ::{owo_colors::OwoColorize, std::convert::TryFrom, tabled::settings::Color};
+
+test_table!(
+ test_shadow_bottom_right_0,
+ Matrix::iter([(123, 456, 789), (234, 567, 891)]).with(Style::psql()).with(Shadow::new(1)),
+ " i32 | i32 | i32 "
+ "-----+-----+-----▒"
+ " 123 | 456 | 789 ▒"
+ " 234 | 567 | 891 ▒"
+ " ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒"
+);
+
+test_table!(
+ test_shadow_bottom_left_0,
+ Matrix::iter([(123, 456, 789), (234, 567, 891)]).with(Style::psql()).with(Shadow::new(1).set_left()),
+ " i32 | i32 | i32 "
+ "▒-----+-----+-----"
+ "▒ 123 | 456 | 789 "
+ "▒ 234 | 567 | 891 "
+ "▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ "
+);
+
+test_table!(
+ test_shadow_top_right_0,
+ Matrix::iter([(123, 456, 789), (234, 567, 891)]).with(Style::psql()).with(Shadow::new(1).set_top()),
+ " ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒"
+ " i32 | i32 | i32 ▒"
+ "-----+-----+-----▒"
+ " 123 | 456 | 789 ▒"
+ " 234 | 567 | 891 "
+);
+
+test_table!(
+ test_shadow_top_left_0,
+ Matrix::iter([(123, 456, 789), (234, 567, 891)]).with(Style::psql()).with(Shadow::new(1).set_top().set_left()),
+ "▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ "
+ "▒ i32 | i32 | i32 "
+ "▒-----+-----+-----"
+ "▒ 123 | 456 | 789 "
+ " 234 | 567 | 891 "
+);
+
+test_table!(
+ test_shadow_set_fill,
+ Matrix::iter([(123, 456, 789), (234, 567, 891)]).with(Shadow::new(1).set_fill('▓')),
+ "+-----+-----+-----+ "
+ "| i32 | i32 | i32 |▓"
+ "+-----+-----+-----+▓"
+ "| 123 | 456 | 789 |▓"
+ "+-----+-----+-----+▓"
+ "| 234 | 567 | 891 |▓"
+ "+-----+-----+-----+▓"
+ " ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓"
+);
+
+test_table!(
+ test_shadow_size_1,
+ Matrix::iter([(123, 456, 789), (234, 567, 891)]).with(Shadow::new(2).set_fill('▓')),
+ "+-----+-----+-----+ "
+ "| i32 | i32 | i32 |▓▓"
+ "+-----+-----+-----+▓▓"
+ "| 123 | 456 | 789 |▓▓"
+ "+-----+-----+-----+▓▓"
+ "| 234 | 567 | 891 |▓▓"
+ "+-----+-----+-----+▓▓"
+ " ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓"
+ " ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓"
+);
+
+test_table!(
+ test_shadow_set_offset_0,
+ Matrix::iter([(123, 456, 789), (234, 567, 891)]).with(Shadow::new(2).set_offset(3)),
+ "+-----+-----+-----+ "
+ "| i32 | i32 | i32 | "
+ "+-----+-----+-----+ "
+ "| 123 | 456 | 789 |▒▒"
+ "+-----+-----+-----+▒▒"
+ "| 234 | 567 | 891 |▒▒"
+ "+-----+-----+-----+▒▒"
+ " ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒"
+ " ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ test_shadow_set_color_0,
+ Matrix::iter([(123, 456, 789), (234, 567, 891)]).with(Shadow::new(2).set_offset(3).set_color(Color::try_from(' '.red().to_string()).unwrap())),
+ "+-----+-----+-----+ "
+ "| i32 | i32 | i32 | "
+ "+-----+-----+-----+ "
+ "| 123 | 456 | 789 |\u{1b}[31m▒▒\u{1b}[39m"
+ "+-----+-----+-----+\u{1b}[31m▒▒\u{1b}[39m"
+ "| 234 | 567 | 891 |\u{1b}[31m▒▒\u{1b}[39m"
+ "+-----+-----+-----+\u{1b}[31m▒▒\u{1b}[39m"
+ " \u{1b}[31m▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒\u{1b}[39m"
+ " \u{1b}[31m▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒\u{1b}[39m"
+);
diff --git a/vendor/tabled/tests/settings/span_test.rs b/vendor/tabled/tests/settings/span_test.rs
new file mode 100644
index 000000000..ed61d8619
--- /dev/null
+++ b/vendor/tabled/tests/settings/span_test.rs
@@ -0,0 +1,1234 @@
+#![cfg(feature = "std")]
+#![allow(clippy::redundant_clone)]
+
+use std::iter::FromIterator;
+
+use tabled::{
+ builder::Builder,
+ grid::config::Position,
+ settings::{
+ object::{Columns, Segment},
+ style::{Border, BorderSpanCorrection, Style},
+ Alignment, Highlight, Modify, Padding, Panel, Span,
+ },
+ Table,
+};
+
+use crate::matrix::Matrix;
+use testing_table::{static_table, test_table};
+
+test_table!(
+ span_column_test_0,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Columns::single(0)).with(Span::column(2))),
+ " N | column 1 | column 2 "
+ "-+-+----------+----------"
+ " 0 | 0-1 | 0-2 "
+ " 1 | 1-1 | 1-2 "
+ " 2 | 2-1 | 2-2 "
+);
+
+test_table!(
+ span_column_test_1,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Columns::new(1..2)).with(Span::column(2))),
+ " N | column 0 | column 2 "
+ "---+-----+----+----------"
+ " 0 | 0-0 | 0-2 "
+ " 1 | 1-0 | 1-2 "
+ " 2 | 2-0 | 2-2 "
+);
+
+test_table!(
+ span_column_test_2,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Columns::single(0)).with(Span::column(4))),
+ " N "
+ "+++"
+ " 0 "
+ " 1 "
+ " 2 "
+);
+
+test_table!(
+ cell_span_test_0,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((0, 0)).with(Span::column(2))),
+ " N | column 1 | column 2 "
+ "---+-----+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ cell_span_test_1,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((1, 0)).with(Span::column(2))),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ cell_span_test_2,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new((2, 0)).with(Span::column(2))),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ cell_span_test_3,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((3, 0)).with(Span::column(2))),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-1 | 2-2 "
+);
+
+test_table!(
+ cell_span_test_4,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((0, 1)).with(Span::column(2))),
+ " N | column 0 | column 2 "
+ "---+-----+-----+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ cell_span_test_5,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((1, 1)).with(Span::column(2))),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ cell_span_test_6,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((2, 1)).with(Span::column(2))),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ cell_span_test_7,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((3, 1)).with(Span::column(2))),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-2 "
+);
+
+test_table!(
+ cell_span_test_8,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new((0, 2)).with(Span::column(2))),
+ " N | column 0 | column 1 "
+ "---+----------+-----+-----"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ cell_span_test_9,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new((1, 2)).with(Span::column(2))),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ cell_span_test_10,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new((2, 2)).with(Span::column(2))),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ cell_span_test_11,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new((3, 2)).with(Span::column(2))),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 "
+);
+
+test_table!(
+ span_multiline,
+ Matrix::new(3, 3)
+ .insert((3, 2), "https://\nwww\n.\nredhat\n.com\n/en")
+ .with(Style::psql())
+ .with(Modify::new((3, 2)).with(Span::column(2))),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | https:// "
+ " | | www "
+ " | | . "
+ " | | redhat "
+ " | | .com "
+ " | | /en "
+);
+
+test_table!(
+ indent_works_in_spaned_columns,
+ Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Padding::new(3, 0, 0, 0)))
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((1, 1)).with(Span::column(3)))
+ .with(Modify::new((3, 1)).with(Span::column(3))),
+ " N| column 0| column 1| column 2"
+ "----+-----------+-----------+-----------"
+ " 0| 0-0 "
+ " 1| 1-0 | 1-1 | 1-2 "
+ " 2| 2-0 "
+);
+
+test_table!(
+ spaned_columns_with_collision,
+ Matrix::iter([["just 1 column"; 5]; 5])
+ .with(Style::modern())
+ .with(
+ Modify::new((0, 0))
+ .with(Span::column(5))
+ .with("span all 5 columns"),
+ )
+ .with(
+ Modify::new((1, 0))
+ .with(Span::column(4))
+ .with("span 4 columns"),
+ )
+ .with(
+ Modify::new((2, 0))
+ .with(Span::column(3))
+ .with("span 3 columns"),
+ )
+ .with(
+ Modify::new((2, 3))
+ .with(Span::column(2))
+ .with("span 2 columns"),
+ )
+ .with(
+ Modify::new((3, 0))
+ .with(Span::column(2))
+ .with("span 3 columns"),
+ )
+ .with(
+ Modify::new((3, 2))
+ .with(Span::column(3))
+ .with("span 3 columns"),
+ )
+ .with(
+ Modify::new((4, 1))
+ .with(Span::column(4))
+ .with("span 4 columns"),
+ ),
+ "┌───────────────┬───────────────┬───────────────┬───────────────┬───────────────┐"
+ "│ span all 5 columns │"
+ "├───────────────┼───────────────┼───────────────┼───────────────┼───────────────┤"
+ "│ span 4 columns │ just 1 column │"
+ "├───────────────┼───────────────┼───────────────┼───────────────┼───────────────┤"
+ "│ span 3 columns │ span 2 columns │"
+ "├───────────────┼───────────────┼───────────────┼───────────────┼───────────────┤"
+ "│ span 3 columns │ span 3 columns │"
+ "├───────────────┼───────────────┼───────────────┼───────────────┼───────────────┤"
+ "│ just 1 column │ span 4 columns │"
+ "├───────────────┼───────────────┼───────────────┼───────────────┼───────────────┤"
+ "│ just 1 column │ just 1 column │ just 1 column │ just 1 column │ just 1 column │"
+ "└───────────────┴───────────────┴───────────────┴───────────────┴───────────────┘"
+);
+
+test_table!(
+ span_with_panel_test_0,
+ Matrix::iter([[1, 2, 3]])
+ .with(Panel::horizontal(0,"Tabled Releases"))
+ .with(Modify::new((1, 0)).with(Span::column(2)))
+ .with(Style::ascii()),
+ "+-----+-----+-----+"
+ "| Tabled Releases |"
+ "+-----+-----+-----+"
+ "| 0 | 2 |"
+ "+-----+-----+-----+"
+ "| 1 | 2 | 3 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ span_with_panel_test_1,
+ Matrix::iter([[1, 2, 3], [4, 5, 6]])
+ .with(Panel::horizontal(0,"Tabled Releases"))
+ .with(Modify::new((2, 0)).with(Span::column(2)))
+ .with(Style::ascii()),
+ "+-----+-----+-----+"
+ "| Tabled Releases |"
+ "+-----+-----+-----+"
+ "| 0 | 1 | 2 |"
+ "+-----+-----+-----+"
+ "| 1 | 3 |"
+ "+-----+-----+-----+"
+ "| 4 | 5 | 6 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ span_with_panel_test_2,
+ Matrix::iter([[1, 2, 3], [4, 5, 6]])
+ .with(Panel::horizontal(0,"Tabled Releases"))
+ .with(Modify::new((1, 0)).with(Span::column(2)))
+ .with(Modify::new((2, 0)).with(Span::column(2)))
+ .with(Style::ascii()),
+ "+-----+-----+-----+"
+ "| Tabled Releases |"
+ "+-----+-----+-----+"
+ "| 0 | 2 |"
+ "+-----+-----+-----+"
+ "| 1 | 3 |"
+ "+-----+-----+-----+"
+ "| 4 | 5 | 6 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ span_with_panel_with_correction_test_0,
+ Matrix::iter([[1, 2, 3]])
+ .with(Panel::horizontal(0,"Tabled Releases"))
+ .with(Modify::new((1, 0)).with(Span::column(2)))
+ .with(Style::ascii())
+ .with(BorderSpanCorrection),
+ "+-----------------+"
+ "| Tabled Releases |"
+ "+-----------+-----+"
+ "| 0 | 2 |"
+ "+-----+-----+-----+"
+ "| 1 | 2 | 3 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ span_with_panel_with_correction_test_1,
+ Matrix::iter([[1, 2, 3], [4, 5, 6]])
+ .with(Panel::horizontal(0,"Tabled Releases"))
+ .with(Modify::new((2, 0)).with(Span::column(2)))
+ .with(Style::ascii())
+ .with(BorderSpanCorrection),
+ "+-----------------+"
+ "| Tabled Releases |"
+ "+-----+-----+-----+"
+ "| 0 | 1 | 2 |"
+ "+-----+-----+-----+"
+ "| 1 | 3 |"
+ "+-----+-----+-----+"
+ "| 4 | 5 | 6 |"
+ "+-----+-----+-----+"
+);
+
+test_table!(
+ span_with_panel_with_correction_test_2,
+ Matrix::iter([[1, 2, 3], [4, 5, 6]])
+ .with(Panel::horizontal(0,"Tabled Releases"))
+ .with(Modify::new((1, 0)).with(Span::column(2)))
+ .with(Modify::new((2, 0)).with(Span::column(2)))
+ .with(Style::ascii())
+ .with(BorderSpanCorrection),
+ "+-----------------+"
+ "| Tabled Releases |"
+ "+-----------+-----+"
+ "| 0 | 2 |"
+ "+-----------+-----+"
+ "| 1 | 3 |"
+ "+-----+-----+-----+"
+ "| 4 | 5 | 6 |"
+ "+-----+-----+-----+"
+);
+
+#[test]
+#[should_panic]
+#[ignore = "span zero not yet decided"]
+fn span_column_exceeds_boundaries_test() {
+ // todo: determine if it's the right behaiviour
+
+ Matrix::new(3, 3)
+ .with(Modify::new(Columns::single(0)).with(Span::column(100)))
+ .to_string();
+}
+
+#[test]
+#[ignore = "span zero not yet decided"]
+fn span_cell_exceeds_boundaries_test() {
+ // these tests shows that exiding boundaries causes invalid behaiviour
+ //
+ // todo: determine if it's the right behaiviour
+
+ let table = Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((0, 0)).with(Span::column(20)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N "
+ "---+-----+-----+-----"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((1, 1)).with(Span::column(20)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((1, 0)).with(Span::column(20)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+}
+
+#[test]
+#[ignore = "span zero not yet decided"]
+fn span_zero_test() {
+ let table = Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new((0, 0)).with(Span::column(0)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " column 0 | column 1 | column 2 "
+ "----+-----+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new((0, 1)).with(Span::column(0)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 1 | column 2 "
+ "---+-----+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new((0, 2)).with(Span::column(0)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 2 "
+ "---+-----+-----+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new((0, 3)).with(Span::column(0)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 "
+ "---+----------+-----+-----"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new((0, 4)).with(Span::column(0)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .with(Style::psql())
+ .with(Modify::new((0, 0)).with(Span::column(0)))
+ .with(Modify::new((1, 1)).with(Span::column(0)))
+ .with(Modify::new((2, 2)).with(Span::column(0)))
+ .with(Modify::new((3, 2)).with(Span::column(0)))
+ .with(Modify::new((3, 1)).with(Span::column(0)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " column 0 | column 1 | column 2 "
+ "------+-------+------+----------"
+ " 0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-2 "
+ " 2 | 2-2 "
+ )
+ );
+}
+
+#[test]
+#[ignore = "span zero not yet decided"]
+fn span_all_table_to_zero_test() {
+ let table = Matrix::table(2, 2)
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Span::column(0)))
+ .to_string();
+
+ // todo: determine whether it's correct
+ assert_eq!(table, static_table!("\n++\n\n\n"));
+}
+
+mod row {
+ use tabled::settings::object::Rows;
+
+ use super::*;
+
+ #[test]
+ fn span_row_test() {
+ let table = Matrix::new(3, 3);
+ {
+ let table_str = table
+ .clone()
+ .with(Style::ascii())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Rows::single(0)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table_str,
+ static_table!(
+ "+---+----------+----------+----------+"
+ "+ N + column 0 + column 1 + column 2 +"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+ )
+ );
+
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Rows::single(0)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N + column 0 + column 1 + column 2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+ }
+ {
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Rows::new(1..2)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+ }
+ {
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Rows::single(0)).with(Span::row(4)))
+ .to_string();
+
+ assert_eq!(table, " N + column 0 + column 1 + column 2 ");
+ }
+ }
+
+ #[test]
+ fn cell_span_test() {
+ let table = Matrix::new(3, 3);
+ {
+ // first column cells row span = 2
+
+ {
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((0, 0)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ " +----------+----------+----------"
+ " | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+ }
+ {
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((1, 0)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+ }
+ {
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new((2, 0)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " | 2-0 | 2-1 | 2-2 "
+ )
+ );
+ }
+ }
+
+ {
+ // first row cells row span = 2
+
+ {
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((0, 1)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+ +----------+----------"
+ " 0 | | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+ }
+ {
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((0, 2)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+ +----------"
+ " 0 | 0-0 | | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+ }
+ {
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new((0, 3)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+ "
+ " 0 | 0-0 | 0-1 | "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+ }
+ }
+
+ {
+ // second column span=2
+ {
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((1, 1)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+ }
+ {
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((2, 1)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | | 2-1 | 2-2 "
+ )
+ );
+ }
+ }
+ {
+ // 3rd column span=2
+ {
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((1, 2)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+ }
+ {
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((2, 2)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | | 2-2 "
+ )
+ );
+ }
+ }
+ {
+ // 4th column span=2
+ {
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((1, 3)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+ }
+ {
+ let table = table
+ .clone()
+ .with(Style::psql())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new((2, 3)).with(Span::row(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | "
+ )
+ );
+ }
+ }
+ }
+
+ #[test]
+ fn span_with_panel_with_correction_test() {
+ let data = [[1, 2, 3]];
+ let table = Table::new(data)
+ .with(Modify::new((0, 0)).with(Span::row(2)))
+ .with(Style::ascii())
+ .with(BorderSpanCorrection)
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "+---+---+---+"
+ "| 0 | 1 | 2 |"
+ "| +---+---+"
+ "| | 2 | 3 |"
+ "+---+---+---+"
+ )
+ );
+
+ let data = [[1, 2, 3], [4, 5, 6]];
+ let table = Table::new(data)
+ .with(Modify::new((1, 0)).with(Span::row(2)))
+ .with(Modify::new((0, 2)).with(Span::row(3)))
+ .with(Style::ascii())
+ .with(BorderSpanCorrection)
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "+---+---+---+"
+ "| 0 | 1 | 2 |"
+ "+---+---+ |"
+ "| 1 | 2 | |"
+ "| +---+ |"
+ "| | 5 | |"
+ "+---+---+---+"
+ )
+ );
+
+ let data = [[1, 2, 3], [4, 5, 6]];
+ let table = Table::new(data)
+ .with(Modify::new((1, 0)).with(Span::row(2)))
+ .with(Modify::new((0, 2)).with(Span::row(3)))
+ .with(Modify::new((0, 1)).with(Span::row(2)))
+ .with(Style::ascii())
+ .with(BorderSpanCorrection)
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "+---+---+---+"
+ "| 0 | 1 | 2 |"
+ "+---+ | |"
+ "| 1 +---+ |"
+ "| | 5 | |"
+ "+---+---+---+"
+ )
+ );
+
+ let data = [[1, 2, 3], [4, 5, 6]];
+ let table = Table::new(data)
+ .with(Modify::new((1, 0)).with(Span::row(2)))
+ .with(Modify::new((0, 1)).with(Span::row(2)).with(Span::column(2)))
+ .with(Style::ascii())
+ .with(BorderSpanCorrection)
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "+---+-------+"
+ "| 0 | 1 |"
+ "+---+ +"
+ "| 1 +---+---+"
+ "| | 5 | 6 |"
+ "+---+---+---+"
+ )
+ );
+ }
+
+ #[test]
+ fn span_example_test() {
+ let data = [["just 1 column"; 5]; 5];
+
+ let h_span = |r, c, span| Modify::new((r, c)).with(Span::column(span));
+ let v_span = |r, c, span| Modify::new((r, c)).with(Span::row(span));
+
+ let table = Table::new(data)
+ .with(h_span(0, 0, 5).with(String::from("span all 5 columns")))
+ .with(h_span(1, 0, 4).with(String::from("span 4 columns")))
+ .with(h_span(2, 0, 2).with(String::from("span 2 columns")))
+ .with(v_span(2, 4, 4).with(String::from("just 1 column\nspan\n4\ncolumns")))
+ .with(v_span(3, 1, 2).with(String::from("span 2 columns\nspan\n2\ncolumns")))
+ .with(v_span(2, 3, 3).with(String::from("just 1 column\nspan\n3\ncolumns")))
+ .with(h_span(3, 1, 2))
+ .with(Style::modern())
+ .with(BorderSpanCorrection)
+ .with(Modify::new(Segment::all()).with(Alignment::center_vertical()))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "┌───────────────────────────────────────────────────────────────────────────────┐"
+ "│ span all 5 columns │"
+ "├───────────────────────────────────────────────────────────────┬───────────────┤"
+ "│ span 4 columns │ just 1 column │"
+ "├───────────────────────────────┬───────────────┬───────────────┼───────────────┤"
+ "│ span 2 columns │ just 1 column │ │ │"
+ "├───────────────┬───────────────┴───────────────┤ just 1 column │ │"
+ "│ just 1 column │ span 2 columns │ span │ just 1 column │"
+ "│ │ span │ 3 │ span │"
+ "├───────────────┤ 2 │ columns │ 4 │"
+ "│ just 1 column │ columns │ │ columns │"
+ "├───────────────┼───────────────┬───────────────┼───────────────┤ │"
+ "│ just 1 column │ just 1 column │ just 1 column │ just 1 column │ │"
+ "└───────────────┴───────────────┴───────────────┴───────────────┴───────────────┘"
+ )
+ )
+ }
+
+ #[test]
+ fn highlight_row_span_test() {
+ let data = [
+ ["1", "2\n2\n2\n2\n2\n2\n2\n2", "3"],
+ ["4", "5", "6"],
+ ["7", "8", "9"],
+ ];
+ let table = Table::new(data)
+ .with(Modify::new((1, 1)).with(Span::row(3)))
+ .with(Style::modern())
+ .with(Highlight::new(Columns::single(1), Border::filled('*')))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "┌───*****───┐"
+ "│ 0 * 1 * 2 │"
+ "├───*───*───┤"
+ "│ 1 * 2 * 3 │"
+ "│ * 2 * │"
+ "├───* 2 *───┤"
+ "│ 4 * 2 * 6 │"
+ "│ * 2 * │"
+ "├───* 2 *───┤"
+ "│ 7 * 2 * 9 │"
+ "│ * 2 * │"
+ "└───*****───┘"
+ )
+ );
+ }
+}
+
+#[test]
+fn highlight_row_col_span_test() {
+ let data = [
+ ["1", "2\n2\n2\n2\n2\n2\n2\n2", "3", "0"],
+ ["4", "5", "6", "0"],
+ ["7", "8", "9", "0"],
+ ];
+ let table = Table::new(data)
+ .with(Modify::new((1, 1)).with(Span::row(3)).with(Span::column(2)))
+ .with(Style::modern())
+ .with(Highlight::new(Columns::new(1..3), Border::filled('*')))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "┌───*********───┐"
+ "│ 0 * 1 │ 2 * 3 │"
+ "├───*───┼───*───┤"
+ "│ 1 * 2 * 0 │"
+ "│ * 2 * │"
+ "├───* 2 *───┤"
+ "│ 4 * 2 * 0 │"
+ "│ * 2 * │"
+ "├───* 2 *───┤"
+ "│ 7 * 2 * 0 │"
+ "│ * 2 * │"
+ "└───*********───┘"
+ )
+ );
+}
+
+test_table!(
+ column_span_bigger_then_max,
+ Matrix::new(3, 3).with(Modify::new((0, 0)).with(Span::column(100))),
+ "+---+-----+-----+-----+"
+ "| N |"
+ "+---+-----+-----+-----+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+-----+-----+-----+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+-----+-----+-----+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+-----+-----+-----+"
+);
+
+test_table!(
+ row_span_bigger_then_max,
+ Matrix::new(3, 3).with(Modify::new((0, 0)).with(Span::row(100))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+ +----------+----------+----------+"
+ "| | 0-0 | 0-1 | 0-2 |"
+ "+ +----------+----------+----------+"
+ "| | 1-0 | 1-1 | 1-2 |"
+ "+ +----------+----------+----------+"
+ "| | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ column_span_invalid_position_row,
+ Matrix::new(3, 3).with(Modify::new((1000, 0)).with(Span::column(2))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ column_span_invalid_position_column,
+ Matrix::new(3, 3).with(Modify::new((0, 1000)).with(Span::column(2))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ column_span_invalid_position_row_and_column,
+ Matrix::new(3, 3).with(Modify::new((1000, 1000)).with(Span::column(2))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ row_span_invalid_position_row,
+ Matrix::new(3, 3).with(Modify::new((1000, 0)).with(Span::row(2))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ row_span_invalid_position_column,
+ Matrix::new(3, 3).with(Modify::new((0, 1000)).with(Span::row(2))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ row_span_invalid_position_row_and_column,
+ Matrix::new(3, 3).with(Modify::new((1000, 1000)).with(Span::row(2))),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ fix_qc_0,
+ {
+ let data: [[i64; 39]; 2] = [[2542785870, 2382388818, 2879895075, 2885436543, 2331131758, 219892320, 2503640226, 3754929678, 2206481860, 686909682, 3456499235, 931699300, 1556722454, 958179233, 3896072307, 2042612749, 3354379549, 3272539286, 3926297167, 4294967295, 1650407458, 3322068437, 4294967295, 446762625, 829020202, 4150192304, 3430619243, 3460609391, 2992017103, 513091574, 1514148367, 2166549688, 1401371431, 2854075038, 1286733939, 2959901405, 4152658371, 0, 4224074215], [360331598, 3736108702, 2948800064, 2121584548, 1609988995, 469935087, 3974876615, 2193609088, 3568111892, 732365859, 0, 4294967295, 2994498036, 198522721, 1784359340, 1, 2732726754, 592359359, 3016729802, 878533877, 2997437699, 3573361662, 1111570515, 4294967295, 2245782848, 1383106893, 0, 0, 2869976103, 1611436878, 1682224972, 3249055253, 1562255501, 1370527728, 240481955, 334260406, 2247343342, 3000635978, 395723768]];
+ let row_spans = [2, 1, 27, 111, 226, 221, 121, 22, 252, 30, 115, 85, 255, 126, 26, 245, 36, 50, 255, 211, 47, 114, 174, 173, 145, 138, 78, 198, 253, 229, 151, 243, 242, 30, 52, 116, 177, 25, 1, 32, 28, 48, 225, 103, 17, 243, 0, 128, 69, 206, 221, 105, 239, 74, 184, 48, 178, 237, 120, 228, 184, 1, 132, 118, 14, 187];
+ let col_spans = [7, 91, 56, 246, 73];
+
+ let data = data.iter().map(|row| row.iter().map(ToString::to_string));
+ let rspans = create_span_list(2, 39).zip(row_spans.iter()).map(|(pos, span)| Modify::new(pos).with(Span::column(*span))).collect::<Vec<_>>();
+ let cspans = create_span_list(2, 39).zip(col_spans.iter()).map(|(pos, span)| Modify::new(pos).with(Span::row(*span))).collect::<Vec<_>>();
+
+ Builder::from_iter(data).build().with(Style::ascii()).with(rspans).with(cspans).to_string()
+ },
+ "+------+-----++++++++++++++++++++++++++++------------+------------+------------+------------+------------+-----------+-----------+------------+------------+-----------+"
+ "| 2542785870 | 2879895075 | 513091574 |"
+ "+ + +------------+------------+------------+------------+------------+-----------+-----------+------------+------------+-----------+"
+ "| | | 1611436878 | 1682224972 | 3249055253 | 1562255501 | 1370527728 | 240481955 | 334260406 | 2247343342 | 3000635978 | 395723768 |"
+ "+------+-----++++++++++++++++++++++++++++------------+------------+------------+------------+------------+-----------+-----------+------------+------------+-----------+"
+);
+
+fn create_span_list(count_rows: usize, count_cols: usize) -> impl Iterator<Item = Position> {
+ (0..count_rows).flat_map(move |r| (0..count_cols).map(move |c| (r, c)))
+}
diff --git a/vendor/tabled/tests/settings/split_test.rs b/vendor/tabled/tests/settings/split_test.rs
new file mode 100644
index 000000000..ac13d8071
--- /dev/null
+++ b/vendor/tabled/tests/settings/split_test.rs
@@ -0,0 +1,277 @@
+#![cfg(feature = "std")]
+
+use std::iter::FromIterator;
+
+use tabled::{builder::Builder, settings::split::Split, Table};
+
+use testing_table::test_table;
+
+test_table!(
+ split_column_test,
+ Table::from_iter(['a'..='z']).with(Split::column(12)),
+ "+---+---+---+---+---+---+---+---+---+---+---+---+"
+ "| 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 | | | | | | | | | | |"
+ "+---+---+---+---+---+---+---+---+---+---+---+---+"
+);
+
+test_table!(
+ split_column_2_test,
+ Table::from_iter(['a'..='z'])
+ .with(Split::column(12))
+ .with(Split::column(4)),
+ "+---+---+---+---+"
+ "| 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 | | |"
+ "+---+---+---+---+"
+);
+
+test_table!(
+ split_column_retain_test,
+ Table::from_iter(['a'..='z'])
+ .with(Split::column(12))
+ .with(Split::column(4).retain()),
+ "+---+---+---+---+"
+ "| 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 | | |"
+ "+---+---+---+---+"
+ "| | | | |"
+ "+---+---+---+---+"
+ "| | | | |"
+ "+---+---+---+---+"
+);
+
+test_table!(
+ split_row_test,
+ Table::from_iter(['a'..='z'])
+ .with(Split::column(12))
+ .with(Split::column(4))
+ .with(Split::row(1).concat()), // take it back to the original shape
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+ "| 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 |"
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+);
+
+test_table!(
+ split_row_2_test,
+ Table::from_iter(['a'..='z'])
+ .with(Split::column(12))
+ .with(Split::column(4))
+ .with(Split::row(2).concat()),
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+ "| a | b | c | d | i | j | k | l | q | r | s | t | y | z |"
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+ "| e | f | g | h | m | n | o | p | u | v | w | x | | |"
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+);
+
+test_table!(
+ split_colum_index_beyond_size_test,
+ Table::from_iter(['a'..='z'])
+ .with(Split::column(10000)),
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+ "| 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 |"
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+);
+
+test_table!(
+ split_row_index_beyond_size_test,
+ Table::from_iter(['a'..='z'])
+ .with(Split::row(10000)),
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+ "| 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 |"
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+);
+
+test_table!(
+ split_empty_table_test,
+ Builder::default().build().with(Split::column(10000)),
+ ""
+);
+
+test_table!(
+ split_column_zero_argument_test,
+ Table::from_iter(['a'..='z']).with(Split::column(0)),
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+ "| 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 |"
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+);
+
+test_table!(
+ split_row_zero_argument_test,
+ Table::from_iter(['a'..='z']).with(Split::row(0)),
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+ "| 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 |"
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+);
+
+test_table!(
+ split_blank_table_test,
+ Table::from_iter([vec![String::new(); 26]]).with(Split::column(12)),
+ "+--+--+--+--+--+--+--+--+--+--+--+--+"
+ "| | | | | | | | | | | | |" // first section is protected
+ "+--+--+--+--+--+--+--+--+--+--+--+--+"
+);
+
+test_table!(
+ split_blank_table_2_test,
+ Table::from_iter([vec![String::new(); 26]]).with(Split::column(12).retain()),
+ "+--+--+--+--+--+--+--+--+--+--+--+--+"
+ "| | | | | | | | | | | | |"
+ "+--+--+--+--+--+--+--+--+--+--+--+--+"
+ "| | | | | | | | | | | | |"
+ "+--+--+--+--+--+--+--+--+--+--+--+--+"
+ "| | | | | | | | | | | | |"
+ "+--+--+--+--+--+--+--+--+--+--+--+--+"
+);
+
+test_table!(
+ split_zip_test,
+ Table::from_iter(['a'..='z'])
+ .with(Split::column(6))
+ .with(Split::row(2)),
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+ "| a | m | y | b | n | z | c | o | d | p | e | q | f | r |"
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+ "| g | s | | h | t | | i | u | j | v | k | w | l | x |"
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+);
+
+test_table!(
+ split_concat_test,
+ Table::from_iter(['a'..='z'])
+ .with(Split::column(6))
+ .with(Split::row(2).concat()),
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+ "| a | b | c | d | e | f | m | n | o | p | q | r | y | z |"
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+ "| g | h | i | j | k | l | s | t | u | v | w | x | | |"
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+);
+
+test_table!(
+ split_clean_test,
+ Table::from_iter(['a'..='z'])
+ .with(Split::column(12))
+ .with(Split::row(2)),
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+ "| a | y | b | z | c | d | e | f | g | h | i | j | k | l |"
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+ "| m | | n | | o | p | q | r | s | t | u | v | w | x |"
+ "+---+---+---+---+---+---+---+---+---+---+---+---+---+---+"
+);
+
+test_table!(
+ split_retain_test,
+ Table::from_iter(['a'..='z'])
+ .with(Split::column(12))
+ .with(Split::row(2).retain()),
+ "+---+---+---+---+---+--+---+--+---+--+---+--+---+--+---+--+---+--+---+--+---+--+---+--+"
+ "| a | y | b | z | c | | d | | e | | f | | g | | h | | i | | j | | k | | l | |"
+ "+---+---+---+---+---+--+---+--+---+--+---+--+---+--+---+--+---+--+---+--+---+--+---+--+"
+ "| m | | n | | o | | p | | q | | r | | s | | t | | u | | v | | w | | x | |"
+ "+---+---+---+---+---+--+---+--+---+--+---+--+---+--+---+--+---+--+---+--+---+--+---+--+"
+);
+
+test_table!(
+ split_mostly_blank_test,
+ Table::from_iter([vec![
+ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", "", "", "A",
+ ]]).with(Split::column(5))
+ .with(Split::row(2)),
+ "+--+--+--+---+--+"
+ "| | | | | |"
+ "+--+--+--+---+--+"
+ "| | | | A | |"
+ "+--+--+--+---+--+"
+);
+
+test_table!(
+ split_mostly_blank_retain_test,
+ Table::from_iter([vec![
+ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", "", "", "A",
+ ]]).with(Split::column(5).retain()),
+ "+--+--+--+---+--+"
+ "| | | | | |"
+ "+--+--+--+---+--+"
+ "| | | | | |"
+ "+--+--+--+---+--+"
+ "| | | | | |"
+ "+--+--+--+---+--+"
+ "| | | | | |"
+ "+--+--+--+---+--+"
+ "| | | | | |"
+ "+--+--+--+---+--+"
+ "| | | | | |"
+ "+--+--+--+---+--+"
+ "| | | | A | |"
+ "+--+--+--+---+--+"
+);
+
+test_table!(
+ split_scattered_values_test,
+ Table::from_iter([vec![
+ "", "", "", "", "", "g", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", "", "", "A",
+ ]]).with(Split::column(5)),
+ "+---+--+--+---+--+"
+ "| | | | | |"
+ "+---+--+--+---+--+"
+ "| g | | | | |"
+ "+---+--+--+---+--+"
+ "| | | | A | |"
+ "+---+--+--+---+--+"
+);
+
+test_table!(
+ split_scattered_values_column_and_row_test,
+ Table::from_iter([vec![
+ "", "", "", "", "", "g", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", "", "", "A",
+ ]]).with(Split::column(5)).with(Split::row(2)),
+ "+---+--+--+--+---+--+"
+ "| | | | | A | |"
+ "+---+--+--+--+---+--+"
+ "| g | | | | | |"
+ "+---+--+--+--+---+--+"
+);
+
+test_table!(
+ split_scattered_values_column_and_row_retain_test,
+ Table::from_iter([vec![
+ "", "", "", "", "", "g", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", "", "", "A",
+ ]]).with(Split::column(5).retain()).with(Split::row(2).retain()),
+ "+---+--+--+--+--+--+--+--+--+--+--+--+--+--+--+---+--+--+--+--+"
+ "| | | | | | | | | | | | | | | | A | | | | |"
+ "+---+--+--+--+--+--+--+--+--+--+--+--+--+--+--+---+--+--+--+--+"
+ "| g | | | | | | | | | | | | | | | | | | | |"
+ "+---+--+--+--+--+--+--+--+--+--+--+--+--+--+--+---+--+--+--+--+"
+);
diff --git a/vendor/tabled/tests/settings/style_test.rs b/vendor/tabled/tests/settings/style_test.rs
new file mode 100644
index 000000000..884192471
--- /dev/null
+++ b/vendor/tabled/tests/settings/style_test.rs
@@ -0,0 +1,2606 @@
+#![cfg(feature = "std")]
+
+use std::iter::FromIterator;
+
+use tabled::{
+ builder::Builder,
+ settings::{
+ object::{Columns, Rows, Segment},
+ style::{
+ Border, BorderChar, BorderColor, BorderSpanCorrection, BorderText, HorizontalLine,
+ Line, Offset, RawStyle, Style, VerticalLine,
+ },
+ Color, Format, Highlight, Modify, Padding, Span,
+ },
+ Table,
+};
+
+use crate::matrix::Matrix;
+use testing_table::{static_table, test_table};
+
+#[cfg(feature = "color")]
+use ::{owo_colors::OwoColorize, std::convert::TryFrom};
+
+test_table!(
+ default_style,
+ Matrix::new(3, 3).with(Style::ascii()),
+ "+---+----------+----------+----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+----------+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+---+----------+----------+----------+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+---+----------+----------+----------+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+---+----------+----------+----------+"
+);
+
+test_table!(
+ psql_style,
+ Matrix::new(3, 3).with(Style::psql()),
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | 0-0 | 0-1 | 0-2 "
+ " 1 | 1-0 | 1-1 | 1-2 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+);
+
+test_table!(
+ markdown_style,
+ Matrix::new(3, 3).with(Style::markdown()),
+ "| N | column 0 | column 1 | column 2 |"
+ "|---|----------|----------|----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ modern_style,
+ Matrix::new(3, 3).with(Style::modern()),
+ "┌───┬──────────┬──────────┬──────────┐"
+ "│ N │ column 0 │ column 1 │ column 2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 0 │ 0-0 │ 0-1 │ 0-2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+ "└───┴──────────┴──────────┴──────────┘"
+);
+
+test_table!(
+ rounded_style,
+ Matrix::new(3, 3).with(Style::rounded()),
+ "╭───┬──────────┬──────────┬──────────╮"
+ "│ N │ column 0 │ column 1 │ column 2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 0 │ 0-0 │ 0-1 │ 0-2 │"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+ "╰───┴──────────┴──────────┴──────────╯"
+);
+
+test_table!(
+ sharp_style,
+ Matrix::new(3, 3).with(Style::sharp()),
+ "┌───┬──────────┬──────────┬──────────┐"
+ "│ N │ column 0 │ column 1 │ column 2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 0 │ 0-0 │ 0-1 │ 0-2 │"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+ "└───┴──────────┴──────────┴──────────┘"
+);
+
+test_table!(
+ modern_clean_style,
+ Matrix::new(3, 3).with(Style::modern().remove_horizontal().horizontals(vec![HorizontalLine::new(1, Style::modern().get_horizontal())])),
+ "┌───┬──────────┬──────────┬──────────┐"
+ "│ N │ column 0 │ column 1 │ column 2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 0 │ 0-0 │ 0-1 │ 0-2 │"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+ "└───┴──────────┴──────────┴──────────┘"
+);
+
+test_table!(
+ blank_style,
+ Matrix::new(3, 3).with(Style::blank()),
+ " N column 0 column 1 column 2 "
+ " 0 0-0 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+);
+
+test_table!(
+ extended_style,
+ Matrix::new(3, 3).with(Style::extended()),
+ "╔═══╦══════════╦══════════╦══════════╗"
+ "║ N ║ column 0 ║ column 1 ║ column 2 ║"
+ "╠═══╬══════════╬══════════╬══════════╣"
+ "║ 0 ║ 0-0 ║ 0-1 ║ 0-2 ║"
+ "╠═══╬══════════╬══════════╬══════════╣"
+ "║ 1 ║ 1-0 ║ 1-1 ║ 1-2 ║"
+ "╠═══╬══════════╬══════════╬══════════╣"
+ "║ 2 ║ 2-0 ║ 2-1 ║ 2-2 ║"
+ "╚═══╩══════════╩══════════╩══════════╝"
+);
+
+test_table!(
+ ascii_dots_style,
+ Matrix::new(3, 3).with(Style::dots()),
+ "......................................"
+ ": N : column 0 : column 1 : column 2 :"
+ ":...:..........:..........:..........:"
+ ": 0 : 0-0 : 0-1 : 0-2 :"
+ ":...:..........:..........:..........:"
+ ": 1 : 1-0 : 1-1 : 1-2 :"
+ ":...:..........:..........:..........:"
+ ": 2 : 2-0 : 2-1 : 2-2 :"
+ ":...:..........:..........:..........:"
+);
+
+test_table!(
+ re_structured_text_style,
+ Matrix::new(3, 3).with(Style::re_structured_text()),
+ "=== ========== ========== =========="
+ " N column 0 column 1 column 2 "
+ "=== ========== ========== =========="
+ " 0 0-0 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ "=== ========== ========== =========="
+);
+
+test_table!(
+ ascii_rounded_style,
+ Matrix::new(3, 3).with(Style::ascii_rounded()),
+ ".------------------------------------."
+ "| N | column 0 | column 1 | column 2 |"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "'------------------------------------'"
+);
+
+test_table!(
+ style_head_changes,
+ Matrix::new(3, 3).with(Style::modern().remove_horizontal()),
+ "┌───┬──────────┬──────────┬──────────┐"
+ "│ N │ column 0 │ column 1 │ column 2 │"
+ "│ 0 │ 0-0 │ 0-1 │ 0-2 │"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+ "└───┴──────────┴──────────┴──────────┘"
+);
+
+test_table!(
+ style_frame_changes,
+ Matrix::new(3, 3).with(Style::modern().remove_top().remove_bottom().remove_horizontal()),
+ "│ N │ column 0 │ column 1 │ column 2 │"
+ "│ 0 │ 0-0 │ 0-1 │ 0-2 │"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+);
+
+test_table!(
+ custom_style,
+ Matrix::new(3, 3)
+ .with(Style::blank()
+ .bottom('*')
+ .vertical('\'')
+ .horizontal('`')
+ .intersection('\'')
+ .intersection_bottom('\'')
+ .horizontals(vec![HorizontalLine::new(1, Line::full('x', '*', 'q', 'w'))])),
+ " N ' column 0 ' column 1 ' column 2 "
+ "qxxx*xxxxxxxxxx*xxxxxxxxxx*xxxxxxxxxxw"
+ " 0 ' 0-0 ' 0-1 ' 0-2 "
+ " ```'``````````'``````````'`````````` "
+ " 1 ' 1-0 ' 1-1 ' 1-2 "
+ " ```'``````````'``````````'`````````` "
+ " 2 ' 2-0 ' 2-1 ' 2-2 "
+ " ***'**********'**********'********** "
+);
+
+test_table!(
+ style_single_cell_0,
+ Matrix::table(0, 0),
+ "+---+"
+ "| N |"
+ "+---+"
+);
+
+test_table!(
+ style_single_cell_1,
+ Matrix::table(0, 0).with(Style::blank()),
+ " N "
+);
+
+test_table!(
+ top_border_override_first_test,
+ Matrix::table(2, 2).with(BorderText::new("-Table").horizontal(Rows::first())),
+ "-Table---------+----------+"
+ "| N | column 0 | column 1 |"
+ "+---+----------+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ top_border_override_last_test,
+ Matrix::table(2, 2).with(BorderText::new("-Table").horizontal(Rows::last())),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "+---+----------+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "-Table---------+----------+"
+);
+
+test_table!(
+ top_border_override_new_test,
+ Matrix::table(2, 2)
+ .with(BorderText::new("-Table").horizontal(1))
+ .with(BorderText::new("-Table").horizontal(2)),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "-Table---------+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "-Table---------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ top_border_override_new_doesnt_panic_when_index_is_invalid,
+ Matrix::table(2, 2).with(BorderText::new("-Table").horizontal(100)),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "+---+----------+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ top_override_doesnt_work_with_style_with_no_top_border_test,
+ Matrix::table(2, 2)
+ .with(Style::psql())
+ .with(BorderText::new("-Table").horizontal(Rows::first())),
+ " N | column 0 | column 1 "
+ "---+----------+----------"
+ " 0 | 0-0 | 0-1 "
+ " 1 | 1-0 | 1-1 "
+);
+
+test_table!(
+ top_border_override_cleared_after_restyling_test,
+ Matrix::table(2, 2)
+ .with(BorderText::new("-Table").horizontal(Rows::first()))
+ .with(Style::ascii()),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "+---+----------+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ top_border_override_with_big_string_test,
+ Matrix::table(2, 2)
+ .with(BorderText::new("-Tableeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1231").horizontal(Rows::first())),
+ "-Tableeeeeeeeeeeeeeeeeeeeee"
+ "| N | column 0 | column 1 |"
+ "+---+----------+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ border_text_0,
+ Matrix::table(2, 2)
+ .with(Style::empty())
+ .with(Modify::new(Rows::first()).with(Border::default().bottom('-')))
+ .with(BorderText::new("-Table").horizontal(1)),
+ " N column 0 column 1 "
+ "-Table-----------------"
+ " 0 0-0 0-1 "
+ " 1 1-0 1-1 "
+);
+
+test_table!(
+ border_color_global,
+ { Matrix::table(2, 2).with(BorderColor::default().bottom(Color::FG_RED)) },
+ "+---+----------+----------+\n\
+ | N | column 0 | column 1 |\n\
+ +\u{1b}[31m---\u{1b}[39m+\u{1b}[31m----------\u{1b}[39m+\u{1b}[31m----------\u{1b}[39m+\n\
+ | 0 | 0-0 | 0-1 |\n\
+ +\u{1b}[31m---\u{1b}[39m+\u{1b}[31m----------\u{1b}[39m+\u{1b}[31m----------\u{1b}[39m+\n\
+ | 1 | 1-0 | 1-1 |\n\
+ +\u{1b}[31m---\u{1b}[39m+\u{1b}[31m----------\u{1b}[39m+\u{1b}[31m----------\u{1b}[39m+"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ border_text_colored,
+ Matrix::table(2, 2)
+ .with(BorderText::new("-Table").horizontal(1))
+ .with(BorderText::new("-Table213123").horizontal(2))
+ .with(Modify::new(Rows::single(1)).with(BorderColor::default().bottom(Color::FG_RED)))
+ .with(Modify::new(Rows::single(2)).with(BorderColor::default().bottom(Color::try_from(" ".blue().on_green().to_string()).unwrap()))),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "-Table---------+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "-\u{1b}[31mTab\u{1b}[39ml\u{1b}[31me213123---\u{1b}[39m+\u{1b}[31m----------\u{1b}[39m+"
+ "| 1 | 1-0 | 1-1 |"
+ "+\u{1b}[34m\u{1b}[42m---\u{1b}[39m\u{1b}[49m+\u{1b}[34m\u{1b}[42m----------\u{1b}[39m\u{1b}[49m+\u{1b}[34m\u{1b}[42m----------\u{1b}[39m\u{1b}[49m+"
+);
+
+test_table!(
+ border_text_offset_test_0,
+ Matrix::table(2, 2).with(BorderText::new("-Table").horizontal(1).offset(Offset::Begin(5))),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "+---+-Table----+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ border_text_offset_test_1,
+ Matrix::table(2, 2).with(BorderText::new("-Table").horizontal(1).offset(Offset::Begin(15))),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "+---+-----------Table-----+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ border_text_offset_test_2,
+ Matrix::table(2, 2).with(BorderText::new("Table").horizontal(1).offset(Offset::End(5))),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "+---+----------+------Table"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ border_text_offset_test_3,
+ Matrix::table(2, 2).with(BorderText::new("Table").horizontal(1).offset(Offset::End(15))),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "+---+-------Table---------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ border_text_offset_test_4,
+ Matrix::table(2, 2).with(BorderText::new("Table").horizontal(1).offset(Offset::End(21))),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "+---+-Table----+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ border_text_offset_test_5,
+ Matrix::table(2, 2).with(BorderText::new("Table").horizontal(1).offset(Offset::End(25))),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "+-Table--------+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ border_text_offset_test_6,
+ Matrix::table(2, 2).with(BorderText::new("-Table").horizontal(1).offset(Offset::Begin(21))),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "+---+----------+------Table"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ border_override_color,
+ Matrix::table(2, 2).with(BorderText::new("-Table").horizontal(Rows::first()).color(Color::FG_BLUE)),
+ "\u{1b}[34m-\u{1b}[39m\u{1b}[34mT\u{1b}[39m\u{1b}[34ma\u{1b}[39m\u{1b}[34mb\u{1b}[39m\u{1b}[34ml\u{1b}[39m\u{1b}[34me\u{1b}[39m---------+----------+"
+ "| N | column 0 | column 1 |"
+ "+---+----------+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ empty_style,
+ Matrix::new(3, 3)
+ .with(Style::empty())
+ .with(Modify::new(Segment::all()).with(Padding::zero())),
+ "Ncolumn 0column 1column 2"
+ "0 0-0 0-1 0-2 "
+ "1 1-0 1-1 1-2 "
+ "2 2-0 2-1 2-2 "
+);
+
+test_table!(
+ single_column_style_0,
+ Matrix::table(2, 0).with(Style::modern()),
+ "┌───┐"
+ "│ N │"
+ "├───┤"
+ "│ 0 │"
+ "├───┤"
+ "│ 1 │"
+ "└───┘"
+);
+
+test_table!(
+ single_column_style_1,
+ Matrix::table(2, 0).with(Style::blank()),
+ " N "
+ " 0 "
+ " 1 "
+);
+
+test_table!(
+ single_column_last_row_style,
+ Matrix::table(3, 0).with(Style::re_structured_text()),
+ "==="
+ " N "
+ "==="
+ " 0 "
+ " 1 "
+ " 2 "
+ "==="
+);
+
+test_table!(
+ single_cell_style,
+ Builder::from_iter([[""]]).build().with(Style::modern()),
+ "┌──┐"
+ "│ │"
+ "└──┘"
+);
+
+test_table!(
+ border_test_0,
+ Matrix::table(2, 2).with(Modify::new(Rows::single(1)).with(Border::filled('*').top('#'))),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "*###*##########*##########*"
+ "* 0 * 0-0 * 0-1 *"
+ "***************************"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ border_test_1,
+ Matrix::table(2, 2)
+ .with(Style::empty())
+ .with(Modify::new(Rows::single(1)).with(Border::filled('*').top('#'))),
+ " N column 0 column 1 "
+ "*###*##########*##########*"
+ "* 0 * 0-0 * 0-1 *"
+ "***************************"
+ " 1 1-0 1-1 "
+);
+
+test_table!(
+ style_frame_test_0,
+ Matrix::table(2, 2).with(Highlight::new(Rows::single(1), Style::modern().get_frame())),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "┌─────────────────────────┐"
+ "│ 0 | 0-0 | 0-1 │"
+ "└─────────────────────────┘"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ style_frame_test_1,
+ Matrix::table(2, 2)
+ .with(Style::blank())
+ .with(Highlight::new(Rows::single(0), Style::extended().get_frame()))
+ .with(Highlight::new(Rows::single(2), Style::extended().get_frame())),
+ "╔═════════════════════════╗"
+ "║ N column 0 column 1 ║"
+ "╚═════════════════════════╝"
+ " 0 0-0 0-1 "
+ "╔═════════════════════════╗"
+ "║ 1 1-0 1-1 ║"
+ "╚═════════════════════════╝"
+);
+
+test_table!(
+ single_column_off_horizontal_test,
+ Matrix::table(3, 0).with(Style::ascii().remove_horizontal().remove_vertical()),
+ "+---+"
+ "| N |"
+ "| 0 |"
+ "| 1 |"
+ "| 2 |"
+ "+---+"
+);
+
+test_table!(
+ single_row_test,
+ Matrix::table(0, 3).with(Style::modern()),
+ "┌───┬──────────┬──────────┬──────────┐"
+ "│ N │ column 0 │ column 1 │ column 2 │"
+ "└───┴──────────┴──────────┴──────────┘"
+);
+
+test_table!(
+ empty_border_text_doesnt_panic_test,
+ Matrix::table(2, 2).with(BorderText::new("").horizontal(0)),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "+---+----------+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ span_correct_test_0,
+ Matrix::table(6, 4)
+ .with(Modify::new((0, 3)).with(Span::column(2)))
+ .with(Modify::new((1, 0)).with(Span::column(3)))
+ .with(Modify::new((2, 0)).with(Span::column(2)))
+ .with(Modify::new((2, 3)).with(Span::column(2)))
+ .with(Modify::new((3, 0)).with(Span::column(5)))
+ .with(Modify::new((4, 1)).with(Span::column(4)))
+ .with(Modify::new((5, 0)).with(Span::column(5)))
+ .with(Modify::new((6, 0)).with(Span::column(5)))
+ .with(BorderSpanCorrection),
+ "+---+----------+----------+-----------+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+---+----------+----------+-----+-----+"
+ "| 0 | 0-2 | 0-3 |"
+ "+--------------+----------+-----+-----+"
+ "| 1 | 1-1 | 1-2 |"
+ "+--------------+----------+-----------+"
+ "| 2 |"
+ "+---+---------------------------------+"
+ "| 3 | 3-0 |"
+ "+---+---------------------------------+"
+ "| 4 |"
+ "+-------------------------------------+"
+ "| 5 |"
+ "+-------------------------------------+"
+);
+
+test_table!(
+ span_correct_test_1,
+ Matrix::table(6, 4)
+ .with(Modify::new((0, 0)).with(Span::column(5)))
+ .with(Modify::new((1, 0)).with(Span::column(3)))
+ .with(Modify::new((2, 0)).with(Span::column(2)))
+ .with(Modify::new((2, 3)).with(Span::column(2)))
+ .with(Modify::new((3, 0)).with(Span::column(5)))
+ .with(Modify::new((4, 1)).with(Span::column(4)))
+ .with(Modify::new((5, 0)).with(Span::column(5)))
+ .with(Modify::new((6, 0)).with(Span::column(5)))
+ .with(BorderSpanCorrection),
+ "+----------------------+"
+ "| N |"
+ "+----------+-----+-----+"
+ "| 0 | 0-2 | 0-3 |"
+ "+----+-----+-----+-----+"
+ "| 1 | 1-1 | 1-2 |"
+ "+----+-----+-----------+"
+ "| 2 |"
+ "+---+------------------+"
+ "| 3 | 3-0 |"
+ "+---+------------------+"
+ "| 4 |"
+ "+----------------------+"
+ "| 5 |"
+ "+----------------------+"
+);
+
+test_table!(
+ style_settings_usage_test_0,
+ Matrix::new(3, 3)
+ .insert((1, 1), "a longer string")
+ .with({
+ let mut style: RawStyle = Style::modern().into();
+ style
+ .set_bottom(Some('a'))
+ .set_left(Some('b'))
+ .set_right(None)
+ .set_top(None)
+ .set_intersection(Some('x'))
+ .set_intersection_top(None)
+ .set_corner_top_left(None)
+ .set_corner_top_right(None);
+ style
+ }),
+ "b N │ column 0 │ column 1 │ column 2 "
+ "├───x─────────────────x──────────x──────────┤"
+ "b 0 │ a longer string │ 0-1 │ 0-2 "
+ "├───x─────────────────x──────────x──────────┤"
+ "b 1 │ 1-0 │ 1-1 │ 1-2 "
+ "├───x─────────────────x──────────x──────────┤"
+ "b 2 │ 2-0 │ 2-1 │ 2-2 "
+ "└aaa┴aaaaaaaaaaaaaaaaa┴aaaaaaaaaa┴aaaaaaaaaa┘"
+);
+
+test_table!(
+ style_settings_usage_test_1,
+ Matrix::new(3, 3)
+ .insert((1, 1), "a longer string")
+ .with({
+ let mut style: RawStyle = Style::modern().into();
+ style.set_bottom(None);
+ style
+ }),
+ "┌───┬─────────────────┬──────────┬──────────┐"
+ "│ N │ column 0 │ column 1 │ column 2 │"
+ "├───┼─────────────────┼──────────┼──────────┤"
+ "│ 0 │ a longer string │ 0-1 │ 0-2 │"
+ "├───┼─────────────────┼──────────┼──────────┤"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "├───┼─────────────────┼──────────┼──────────┤"
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+ "└ ┴ ┴ ┴ ┘"
+);
+
+test_table!(
+ style_settings_usage_test_2,
+ Matrix::new(3, 3)
+ .insert((1, 1), "a longer string")
+ .with({
+ let mut style: RawStyle = Style::modern().into();
+ style.set_bottom(None);
+ style
+ })
+ .with(Modify::new(Rows::last()).with(Border::default().corner_bottom_left('*'))),
+ "┌───┬─────────────────┬──────────┬──────────┐"
+ "│ N │ column 0 │ column 1 │ column 2 │"
+ "├───┼─────────────────┼──────────┼──────────┤"
+ "│ 0 │ a longer string │ 0-1 │ 0-2 │"
+ "├───┼─────────────────┼──────────┼──────────┤"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "├───┼─────────────────┼──────────┼──────────┤"
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+ "* * * * ┘"
+);
+
+test_table!(
+ border_none_test_0,
+ Matrix::table(2, 2)
+ .with(Style::ascii())
+ .with(Modify::new(Rows::single(1)).with(Border::filled('*').top('#')))
+ .with(Modify::new(Rows::single(1)).with(Border::empty())),
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "+---+----------+----------+"
+ "| 0 | 0-0 | 0-1 |"
+ "+---+----------+----------+"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+);
+
+test_table!(
+ border_none_test_1,
+ Matrix::table(2, 2)
+ .with(Style::empty())
+ .with(Modify::new(Rows::single(1)).with(Border::filled('*').top('#')))
+ .with(Modify::new(Columns::single(1)).with(Border::empty())),
+ " N column 0 column 1 "
+ "*### ##########*"
+ "* 0 0-0 0-1 *"
+ "**** ***********"
+ " 1 1-0 1-1 "
+);
+
+#[test]
+fn custom_style_test() {
+ macro_rules! test_style {
+ ($style:expr, $expected:expr $(,)*) => {
+ let table = Matrix::new(3, 3).with($style).to_string();
+ assert_eq!(table, $expected);
+ };
+ }
+
+ // Single
+
+ test_style!(
+ Style::empty().top('-'),
+ static_table!(
+ "---------------------------------"
+ " N column 0 column 1 column 2 "
+ " 0 0-0 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ ),
+ );
+ test_style!(
+ Style::empty().bottom('-'),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 0-0 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ "---------------------------------"
+ ),
+ );
+ test_style!(
+ Style::empty().left('-'),
+ static_table!(
+ "- N column 0 column 1 column 2 "
+ "- 0 0-0 0-1 0-2 "
+ "- 1 1-0 1-1 1-2 "
+ "- 2 2-0 2-1 2-2 "
+ ),
+ );
+ test_style!(
+ Style::empty().right('-'),
+ static_table!(
+ " N column 0 column 1 column 2 -"
+ " 0 0-0 0-1 0-2 -"
+ " 1 1-0 1-1 1-2 -"
+ " 2 2-0 2-1 2-2 -"
+ ),
+ );
+ test_style!(
+ Style::empty().horizontal('-'),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ "---------------------------------"
+ " 0 0-0 0-1 0-2 "
+ "---------------------------------"
+ " 1 1-0 1-1 1-2 "
+ "---------------------------------"
+ " 2 2-0 2-1 2-2 "
+ ),
+ );
+ test_style!(
+ Style::empty().horizontals(vec![HorizontalLine::new(1, Line::default()).main(Some('-'))]),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ "---------------------------------"
+ " 0 0-0 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ ),
+ );
+ test_style!(
+ Style::empty().vertical('-'),
+ static_table!(
+ " N - column 0 - column 1 - column 2 "
+ " 0 - 0-0 - 0-1 - 0-2 "
+ " 1 - 1-0 - 1-1 - 1-2 "
+ " 2 - 2-0 - 2-1 - 2-2 "
+ ),
+ );
+
+ // Combinations
+
+ test_style!(
+ Style::empty().top('-').bottom('+'),
+ static_table!(
+ "---------------------------------"
+ " N column 0 column 1 column 2 "
+ " 0 0-0 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ "+++++++++++++++++++++++++++++++++"
+ )
+ );
+ test_style!(
+ Style::empty().top('-').left('+'),
+ static_table!(
+ "+---------------------------------"
+ "+ N column 0 column 1 column 2 "
+ "+ 0 0-0 0-1 0-2 "
+ "+ 1 1-0 1-1 1-2 "
+ "+ 2 2-0 2-1 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty().top('-').right('+'),
+ static_table!(
+ "---------------------------------+"
+ " N column 0 column 1 column 2 +"
+ " 0 0-0 0-1 0-2 +"
+ " 1 1-0 1-1 1-2 +"
+ " 2 2-0 2-1 2-2 +"
+ )
+ );
+ test_style!(
+ Style::empty().top('-').horizontal('+'),
+ static_table!(
+ "---------------------------------"
+ " N column 0 column 1 column 2 "
+ "+++++++++++++++++++++++++++++++++"
+ " 0 0-0 0-1 0-2 "
+ "+++++++++++++++++++++++++++++++++"
+ " 1 1-0 1-1 1-2 "
+ "+++++++++++++++++++++++++++++++++"
+ " 2 2-0 2-1 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty().top('-').vertical('+'),
+ static_table!(
+ "---+----------+----------+----------"
+ " N + column 0 + column 1 + column 2 "
+ " 0 + 0-0 + 0-1 + 0-2 "
+ " 1 + 1-0 + 1-1 + 1-2 "
+ " 2 + 2-0 + 2-1 + 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty()
+ .top('-')
+ .horizontals(vec![HorizontalLine::new(1, Line::default()).main(Some('+'))]),
+ static_table!(
+ "---------------------------------"
+ " N column 0 column 1 column 2 "
+ "+++++++++++++++++++++++++++++++++"
+ " 0 0-0 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ );
+
+ test_style!(
+ Style::empty().bottom('-').top('+'),
+ static_table!(
+ "+++++++++++++++++++++++++++++++++"
+ " N column 0 column 1 column 2 "
+ " 0 0-0 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ "---------------------------------"
+ )
+ );
+ test_style!(
+ Style::empty().bottom('-').left('+'),
+ static_table!(
+ "+ N column 0 column 1 column 2 "
+ "+ 0 0-0 0-1 0-2 "
+ "+ 1 1-0 1-1 1-2 "
+ "+ 2 2-0 2-1 2-2 "
+ "+---------------------------------"
+ )
+ );
+ test_style!(
+ Style::empty().bottom('-').right('+'),
+ static_table!(
+ " N column 0 column 1 column 2 +"
+ " 0 0-0 0-1 0-2 +"
+ " 1 1-0 1-1 1-2 +"
+ " 2 2-0 2-1 2-2 +"
+ "---------------------------------+"
+ )
+ );
+ test_style!(
+ Style::empty().bottom('-').vertical('+'),
+ static_table!(
+ " N + column 0 + column 1 + column 2 "
+ " 0 + 0-0 + 0-1 + 0-2 "
+ " 1 + 1-0 + 1-1 + 1-2 "
+ " 2 + 2-0 + 2-1 + 2-2 "
+ "---+----------+----------+----------"
+ )
+ );
+ test_style!(
+ Style::empty().bottom('-').horizontal('+'),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ "+++++++++++++++++++++++++++++++++"
+ " 0 0-0 0-1 0-2 "
+ "+++++++++++++++++++++++++++++++++"
+ " 1 1-0 1-1 1-2 "
+ "+++++++++++++++++++++++++++++++++"
+ " 2 2-0 2-1 2-2 "
+ "---------------------------------"
+ )
+ );
+ test_style!(
+ Style::empty()
+ .bottom('-')
+ .horizontals(vec![HorizontalLine::new(1, Line::default()).main(Some('+'))]),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ "+++++++++++++++++++++++++++++++++"
+ " 0 0-0 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ "---------------------------------"
+ )
+ );
+
+ test_style!(
+ Style::empty().left('-').top('+'),
+ static_table!(
+ "++++++++++++++++++++++++++++++++++"
+ "- N column 0 column 1 column 2 "
+ "- 0 0-0 0-1 0-2 "
+ "- 1 1-0 1-1 1-2 "
+ "- 2 2-0 2-1 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty().left('-').bottom('+'),
+ static_table!(
+ "- N column 0 column 1 column 2 "
+ "- 0 0-0 0-1 0-2 "
+ "- 1 1-0 1-1 1-2 "
+ "- 2 2-0 2-1 2-2 "
+ "++++++++++++++++++++++++++++++++++"
+ )
+ );
+ test_style!(
+ Style::empty().left('-').right('+'),
+ static_table!(
+ "- N column 0 column 1 column 2 +"
+ "- 0 0-0 0-1 0-2 +"
+ "- 1 1-0 1-1 1-2 +"
+ "- 2 2-0 2-1 2-2 +"
+ )
+ );
+ test_style!(
+ Style::empty().left('-').vertical('+'),
+ static_table!(
+ "- N + column 0 + column 1 + column 2 "
+ "- 0 + 0-0 + 0-1 + 0-2 "
+ "- 1 + 1-0 + 1-1 + 1-2 "
+ "- 2 + 2-0 + 2-1 + 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty().left('-').horizontal('+'),
+ static_table!(
+ "- N column 0 column 1 column 2 "
+ "++++++++++++++++++++++++++++++++++"
+ "- 0 0-0 0-1 0-2 "
+ "++++++++++++++++++++++++++++++++++"
+ "- 1 1-0 1-1 1-2 "
+ "++++++++++++++++++++++++++++++++++"
+ "- 2 2-0 2-1 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty()
+ .left('-')
+ .horizontals(vec![HorizontalLine::new(1, Line::default()).main(Some('+'))]),
+ static_table!(
+ "- N column 0 column 1 column 2 "
+ " +++++++++++++++++++++++++++++++++"
+ "- 0 0-0 0-1 0-2 "
+ "- 1 1-0 1-1 1-2 "
+ "- 2 2-0 2-1 2-2 "
+ )
+ );
+
+ test_style!(
+ Style::empty().right('-').top('+'),
+ static_table!(
+ "++++++++++++++++++++++++++++++++++"
+ " N column 0 column 1 column 2 -"
+ " 0 0-0 0-1 0-2 -"
+ " 1 1-0 1-1 1-2 -"
+ " 2 2-0 2-1 2-2 -"
+ )
+ );
+ test_style!(
+ Style::empty().right('-').bottom('+'),
+ static_table!(
+ " N column 0 column 1 column 2 -"
+ " 0 0-0 0-1 0-2 -"
+ " 1 1-0 1-1 1-2 -"
+ " 2 2-0 2-1 2-2 -"
+ "++++++++++++++++++++++++++++++++++"
+ )
+ );
+ test_style!(
+ Style::empty().right('-').left('+'),
+ static_table!(
+ "+ N column 0 column 1 column 2 -"
+ "+ 0 0-0 0-1 0-2 -"
+ "+ 1 1-0 1-1 1-2 -"
+ "+ 2 2-0 2-1 2-2 -"
+ )
+ );
+ test_style!(
+ Style::empty().right('-').vertical('+'),
+ static_table!(
+ " N + column 0 + column 1 + column 2 -"
+ " 0 + 0-0 + 0-1 + 0-2 -"
+ " 1 + 1-0 + 1-1 + 1-2 -"
+ " 2 + 2-0 + 2-1 + 2-2 -"
+ )
+ );
+ test_style!(
+ Style::empty().right('-').horizontal('+'),
+ static_table!(
+ " N column 0 column 1 column 2 -"
+ "++++++++++++++++++++++++++++++++++"
+ " 0 0-0 0-1 0-2 -"
+ "++++++++++++++++++++++++++++++++++"
+ " 1 1-0 1-1 1-2 -"
+ "++++++++++++++++++++++++++++++++++"
+ " 2 2-0 2-1 2-2 -"
+ )
+ );
+ test_style!(
+ Style::empty()
+ .right('-')
+ .horizontals(vec![HorizontalLine::new(1, Line::default()).main(Some('+'))]),
+ static_table!(
+ " N column 0 column 1 column 2 -"
+ "+++++++++++++++++++++++++++++++++ "
+ " 0 0-0 0-1 0-2 -"
+ " 1 1-0 1-1 1-2 -"
+ " 2 2-0 2-1 2-2 -"
+ )
+ );
+
+ test_style!(
+ Style::empty().vertical('-').top('+'),
+ static_table!(
+ "++++++++++++++++++++++++++++++++++++"
+ " N - column 0 - column 1 - column 2 "
+ " 0 - 0-0 - 0-1 - 0-2 "
+ " 1 - 1-0 - 1-1 - 1-2 "
+ " 2 - 2-0 - 2-1 - 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty().vertical('-').bottom('+'),
+ static_table!(
+ " N - column 0 - column 1 - column 2 "
+ " 0 - 0-0 - 0-1 - 0-2 "
+ " 1 - 1-0 - 1-1 - 1-2 "
+ " 2 - 2-0 - 2-1 - 2-2 "
+ "++++++++++++++++++++++++++++++++++++"
+ )
+ );
+ test_style!(
+ Style::empty().vertical('-').left('+'),
+ static_table!(
+ "+ N - column 0 - column 1 - column 2 "
+ "+ 0 - 0-0 - 0-1 - 0-2 "
+ "+ 1 - 1-0 - 1-1 - 1-2 "
+ "+ 2 - 2-0 - 2-1 - 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty().vertical('-').right('+'),
+ static_table!(
+ " N - column 0 - column 1 - column 2 +"
+ " 0 - 0-0 - 0-1 - 0-2 +"
+ " 1 - 1-0 - 1-1 - 1-2 +"
+ " 2 - 2-0 - 2-1 - 2-2 +"
+ )
+ );
+ test_style!(
+ Style::empty().vertical('-').horizontal('+'),
+ static_table!(
+ " N - column 0 - column 1 - column 2 "
+ "++++++++++++++++++++++++++++++++++++"
+ " 0 - 0-0 - 0-1 - 0-2 "
+ "++++++++++++++++++++++++++++++++++++"
+ " 1 - 1-0 - 1-1 - 1-2 "
+ "++++++++++++++++++++++++++++++++++++"
+ " 2 - 2-0 - 2-1 - 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty()
+ .vertical('-')
+ .horizontals(vec![HorizontalLine::new(1, Line::default()).main(Some('+'))]),
+ static_table!(
+ " N - column 0 - column 1 - column 2 "
+ "+++ ++++++++++ ++++++++++ ++++++++++"
+ " 0 - 0-0 - 0-1 - 0-2 "
+ " 1 - 1-0 - 1-1 - 1-2 "
+ " 2 - 2-0 - 2-1 - 2-2 "
+ )
+ );
+
+ test_style!(
+ Style::empty().horizontal('-').top('+'),
+ static_table!(
+ "+++++++++++++++++++++++++++++++++"
+ " N column 0 column 1 column 2 "
+ "---------------------------------"
+ " 0 0-0 0-1 0-2 "
+ "---------------------------------"
+ " 1 1-0 1-1 1-2 "
+ "---------------------------------"
+ " 2 2-0 2-1 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty().horizontal('-').bottom('+'),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ "---------------------------------"
+ " 0 0-0 0-1 0-2 "
+ "---------------------------------"
+ " 1 1-0 1-1 1-2 "
+ "---------------------------------"
+ " 2 2-0 2-1 2-2 "
+ "+++++++++++++++++++++++++++++++++"
+ )
+ );
+ test_style!(
+ Style::empty().horizontal('-').left('+'),
+ static_table!(
+ "+ N column 0 column 1 column 2 "
+ "+---------------------------------"
+ "+ 0 0-0 0-1 0-2 "
+ "+---------------------------------"
+ "+ 1 1-0 1-1 1-2 "
+ "+---------------------------------"
+ "+ 2 2-0 2-1 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty().horizontal('-').right('+'),
+ static_table!(
+ " N column 0 column 1 column 2 +"
+ "---------------------------------+"
+ " 0 0-0 0-1 0-2 +"
+ "---------------------------------+"
+ " 1 1-0 1-1 1-2 +"
+ "---------------------------------+"
+ " 2 2-0 2-1 2-2 +"
+ )
+ );
+ test_style!(
+ Style::empty().horizontal('-').vertical('+'),
+ static_table!(
+ " N + column 0 + column 1 + column 2 "
+ "---+----------+----------+----------"
+ " 0 + 0-0 + 0-1 + 0-2 "
+ "---+----------+----------+----------"
+ " 1 + 1-0 + 1-1 + 1-2 "
+ "---+----------+----------+----------"
+ " 2 + 2-0 + 2-1 + 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty()
+ .horizontal('-')
+ .horizontals(vec![HorizontalLine::new(1, Line::default()).main(Some('+'))]),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ "+++++++++++++++++++++++++++++++++"
+ " 0 0-0 0-1 0-2 "
+ "---------------------------------"
+ " 1 1-0 1-1 1-2 "
+ "---------------------------------"
+ " 2 2-0 2-1 2-2 "
+ )
+ );
+
+ test_style!(
+ Style::empty()
+ .horizontals(vec![HorizontalLine::new(1, Line::default()).main(Some('-'))])
+ .top('+'),
+ static_table!(
+ "+++++++++++++++++++++++++++++++++"
+ " N column 0 column 1 column 2 "
+ "---------------------------------"
+ " 0 0-0 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty()
+ .horizontals(vec![HorizontalLine::new(1, Line::default()).main(Some('-'))])
+ .bottom('+'),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ "---------------------------------"
+ " 0 0-0 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ "+++++++++++++++++++++++++++++++++"
+ )
+ );
+ test_style!(
+ Style::empty()
+ .horizontals(vec![HorizontalLine::new(1, Line::default()).main(Some('-'))])
+ .left('+'),
+ static_table!(
+ "+ N column 0 column 1 column 2 "
+ "+---------------------------------"
+ "+ 0 0-0 0-1 0-2 "
+ "+ 1 1-0 1-1 1-2 "
+ "+ 2 2-0 2-1 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty()
+ .horizontals(vec![HorizontalLine::new(1, Line::default()).main(Some('-'))])
+ .right('+'),
+ static_table!(
+ " N column 0 column 1 column 2 +"
+ "---------------------------------+"
+ " 0 0-0 0-1 0-2 +"
+ " 1 1-0 1-1 1-2 +"
+ " 2 2-0 2-1 2-2 +"
+ )
+ );
+ test_style!(
+ Style::empty()
+ .horizontals(vec![HorizontalLine::new(1, Line::default()).main(Some('-'))])
+ .vertical('+'),
+ static_table!(
+ " N + column 0 + column 1 + column 2 "
+ "---+----------+----------+----------"
+ " 0 + 0-0 + 0-1 + 0-2 "
+ " 1 + 1-0 + 1-1 + 1-2 "
+ " 2 + 2-0 + 2-1 + 2-2 "
+ )
+ );
+ test_style!(
+ Style::empty()
+ .horizontals(vec![HorizontalLine::new(1, Line::default()).main(Some('-'))])
+ .horizontal('+'),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ "---------------------------------"
+ " 0 0-0 0-1 0-2 "
+ "+++++++++++++++++++++++++++++++++"
+ " 1 1-0 1-1 1-2 "
+ "+++++++++++++++++++++++++++++++++"
+ " 2 2-0 2-1 2-2 "
+ )
+ );
+
+ // Full
+
+ test_style!(
+ Style::empty()
+ .top('-')
+ .bottom('+')
+ .left('|')
+ .right('*')
+ .horizontal('x')
+ .horizontals(vec![HorizontalLine::new(1, Line::filled('z'))])
+ .vertical('#'),
+ static_table!(
+ "|---#----------#----------#----------*"
+ "| N # column 0 # column 1 # column 2 *"
+ "zzzz#zzzzzzzzzz#zzzzzzzzzz#zzzzzzzzzzz"
+ "| 0 # 0-0 # 0-1 # 0-2 *"
+ "xxxx#xxxxxxxxxx#xxxxxxxxxx#xxxxxxxxxxx"
+ "| 1 # 1-0 # 1-1 # 1-2 *"
+ "xxxx#xxxxxxxxxx#xxxxxxxxxx#xxxxxxxxxxx"
+ "| 2 # 2-0 # 2-1 # 2-2 *"
+ "|+++#++++++++++#++++++++++#++++++++++*"
+ ),
+ );
+
+ let full_style = Style::empty()
+ .top('-')
+ .bottom('+')
+ .left('|')
+ .right('*')
+ .horizontal('x')
+ .horizontals(vec![HorizontalLine::new(1, Line::filled(','))])
+ .vertical('#')
+ .intersection_bottom('@')
+ .intersection_top('!')
+ .intersection_left('=')
+ .intersection_right('$')
+ .intersection('+')
+ .corner_top_left(';')
+ .corner_bottom_left('?')
+ .corner_top_right('.')
+ .corner_bottom_right('%');
+ test_style!(
+ full_style.clone(),
+ static_table!(
+ ";---!----------!----------!----------."
+ "| N # column 0 # column 1 # column 2 *"
+ ",,,,#,,,,,,,,,,#,,,,,,,,,,#,,,,,,,,,,,"
+ "| 0 # 0-0 # 0-1 # 0-2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 1 # 1-0 # 1-1 # 1-2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 2 # 2-0 # 2-1 # 2-2 *"
+ "?+++@++++++++++@++++++++++@++++++++++%"
+ )
+ );
+
+ // Overwrite intersections and corners
+
+ test_style!(
+ full_style.clone().top('q'),
+ static_table!(
+ "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
+ "| N # column 0 # column 1 # column 2 *"
+ ",,,,#,,,,,,,,,,#,,,,,,,,,,#,,,,,,,,,,,"
+ "| 0 # 0-0 # 0-1 # 0-2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 1 # 1-0 # 1-1 # 1-2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 2 # 2-0 # 2-1 # 2-2 *"
+ "?+++@++++++++++@++++++++++@++++++++++%"
+ )
+ );
+ test_style!(
+ full_style.clone().bottom('q'),
+ static_table!(
+ ";---!----------!----------!----------."
+ "| N # column 0 # column 1 # column 2 *"
+ ",,,,#,,,,,,,,,,#,,,,,,,,,,#,,,,,,,,,,,"
+ "| 0 # 0-0 # 0-1 # 0-2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 1 # 1-0 # 1-1 # 1-2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 2 # 2-0 # 2-1 # 2-2 *"
+ "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
+ )
+ );
+ test_style!(
+ full_style.clone().left('w'),
+ static_table!(
+ "w---!----------!----------!----------."
+ "w N # column 0 # column 1 # column 2 *"
+ "w,,,#,,,,,,,,,,#,,,,,,,,,,#,,,,,,,,,,,"
+ "w 0 # 0-0 # 0-1 # 0-2 *"
+ "wxxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "w 1 # 1-0 # 1-1 # 1-2 *"
+ "wxxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "w 2 # 2-0 # 2-1 # 2-2 *"
+ "w+++@++++++++++@++++++++++@++++++++++%"
+ )
+ );
+ test_style!(
+ full_style.clone().right('i'),
+ static_table!(
+ ";---!----------!----------!----------i"
+ "| N # column 0 # column 1 # column 2 i"
+ ",,,,#,,,,,,,,,,#,,,,,,,,,,#,,,,,,,,,,i"
+ "| 0 # 0-0 # 0-1 # 0-2 i"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxxi"
+ "| 1 # 1-0 # 1-1 # 1-2 i"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxxi"
+ "| 2 # 2-0 # 2-1 # 2-2 i"
+ "?+++@++++++++++@++++++++++@++++++++++i"
+ )
+ );
+ test_style!(
+ full_style.clone().horizontal('q'),
+ static_table!(
+ ";---!----------!----------!----------."
+ "| N # column 0 # column 1 # column 2 *"
+ ",,,,#,,,,,,,,,,#,,,,,,,,,,#,,,,,,,,,,,"
+ "| 0 # 0-0 # 0-1 # 0-2 *"
+ "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
+ "| 1 # 1-0 # 1-1 # 1-2 *"
+ "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
+ "| 2 # 2-0 # 2-1 # 2-2 *"
+ "?+++@++++++++++@++++++++++@++++++++++%"
+ )
+ );
+ test_style!(
+ full_style.clone().vertical('q'),
+ static_table!(
+ ";---q----------q----------q----------."
+ "| N q column 0 q column 1 q column 2 *"
+ ",,,,q,,,,,,,,,,q,,,,,,,,,,q,,,,,,,,,,,"
+ "| 0 q 0-0 q 0-1 q 0-2 *"
+ "=xxxqxxxxxxxxxxqxxxxxxxxxxqxxxxxxxxxx$"
+ "| 1 q 1-0 q 1-1 q 1-2 *"
+ "=xxxqxxxxxxxxxxqxxxxxxxxxxqxxxxxxxxxx$"
+ "| 2 q 2-0 q 2-1 q 2-2 *"
+ "?+++q++++++++++q++++++++++q++++++++++%"
+ )
+ );
+ test_style!(
+ full_style
+ .clone()
+ .horizontals(vec![HorizontalLine::new(1, Line::filled('q'))]),
+ static_table!(
+ ";---!----------!----------!----------."
+ "| N # column 0 # column 1 # column 2 *"
+ "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
+ "| 0 # 0-0 # 0-1 # 0-2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 1 # 1-0 # 1-1 # 1-2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 2 # 2-0 # 2-1 # 2-2 *"
+ "?+++@++++++++++@++++++++++@++++++++++%"
+ )
+ );
+
+ // Turn off borders
+
+ let empty_table = static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 0-0 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ );
+ test_style!(Style::empty().top('-').remove_top(), empty_table);
+ test_style!(Style::empty().bottom('-').remove_bottom(), empty_table);
+ test_style!(Style::empty().right('-').remove_right(), empty_table);
+ test_style!(Style::empty().left('-').remove_left(), empty_table);
+ test_style!(
+ Style::empty().horizontal('-').remove_horizontal(),
+ empty_table
+ );
+ test_style!(Style::empty().vertical('-').remove_vertical(), empty_table);
+ test_style!(
+ Style::empty().horizontals(vec![HorizontalLine::new(
+ 1,
+ Line::new(Some('-'), None, None, None)
+ )]),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ "---------------------------------"
+ " 0 0-0 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ );
+
+ test_style!(
+ full_style.clone().remove_top(),
+ static_table!(
+ "| N # column 0 # column 1 # column 2 *"
+ ",,,,#,,,,,,,,,,#,,,,,,,,,,#,,,,,,,,,,,"
+ "| 0 # 0-0 # 0-1 # 0-2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 1 # 1-0 # 1-1 # 1-2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 2 # 2-0 # 2-1 # 2-2 *"
+ "?+++@++++++++++@++++++++++@++++++++++%"
+ )
+ );
+ test_style!(
+ full_style.clone().remove_bottom(),
+ static_table!(
+ ";---!----------!----------!----------."
+ "| N # column 0 # column 1 # column 2 *"
+ ",,,,#,,,,,,,,,,#,,,,,,,,,,#,,,,,,,,,,,"
+ "| 0 # 0-0 # 0-1 # 0-2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 1 # 1-0 # 1-1 # 1-2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 2 # 2-0 # 2-1 # 2-2 *"
+ )
+ );
+ test_style!(
+ full_style.clone().remove_right(),
+ static_table!(
+ ";---!----------!----------!----------"
+ "| N # column 0 # column 1 # column 2 "
+ ",,,,#,,,,,,,,,,#,,,,,,,,,,#,,,,,,,,,,"
+ "| 0 # 0-0 # 0-1 # 0-2 "
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx"
+ "| 1 # 1-0 # 1-1 # 1-2 "
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx"
+ "| 2 # 2-0 # 2-1 # 2-2 "
+ "?+++@++++++++++@++++++++++@++++++++++"
+ )
+ );
+ test_style!(
+ full_style.clone().remove_left(),
+ static_table!(
+ "---!----------!----------!----------."
+ " N # column 0 # column 1 # column 2 *"
+ ",,,#,,,,,,,,,,#,,,,,,,,,,#,,,,,,,,,,,"
+ " 0 # 0-0 # 0-1 # 0-2 *"
+ "xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ " 1 # 1-0 # 1-1 # 1-2 *"
+ "xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ " 2 # 2-0 # 2-1 # 2-2 *"
+ "+++@++++++++++@++++++++++@++++++++++%"
+ )
+ );
+ test_style!(
+ full_style.clone().remove_horizontal(),
+ static_table!(
+ ";---!----------!----------!----------."
+ "| N # column 0 # column 1 # column 2 *"
+ ",,,,#,,,,,,,,,,#,,,,,,,,,,#,,,,,,,,,,,"
+ "| 0 # 0-0 # 0-1 # 0-2 *"
+ "| 1 # 1-0 # 1-1 # 1-2 *"
+ "| 2 # 2-0 # 2-1 # 2-2 *"
+ "?+++@++++++++++@++++++++++@++++++++++%"
+ )
+ );
+ test_style!(
+ full_style.clone().remove_vertical(),
+ static_table!(
+ ";---------------------------------."
+ "| N column 0 column 1 column 2 *"
+ ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"
+ "| 0 0-0 0-1 0-2 *"
+ "=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx$"
+ "| 1 1-0 1-1 1-2 *"
+ "=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx$"
+ "| 2 2-0 2-1 2-2 *"
+ "?+++++++++++++++++++++++++++++++++%"
+ )
+ );
+ test_style!(
+ full_style.remove_horizontals(),
+ static_table!(
+ ";---!----------!----------!----------."
+ "| N # column 0 # column 1 # column 2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 0 # 0-0 # 0-1 # 0-2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 1 # 1-0 # 1-1 # 1-2 *"
+ "=xxx+xxxxxxxxxx+xxxxxxxxxx+xxxxxxxxxx$"
+ "| 2 # 2-0 # 2-1 # 2-2 *"
+ "?+++@++++++++++@++++++++++@++++++++++%"
+ )
+ );
+}
+
+#[test]
+fn test_default_border_usage() {
+ macro_rules! test_border {
+ ($modify:expr, $expected:expr) => {
+ let table = Matrix::new(3, 3)
+ .insert((1, 1), "a longer string")
+ .with(Style::empty())
+ .with($modify)
+ .to_string();
+
+ assert_eq!(table, $expected);
+ };
+ }
+
+ test_border! {
+ Modify::new((3, 2)).with(Border::default().corner_bottom_left('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ " * "
+ )
+ }
+ test_border! {
+ Modify::new((3, 2)).with(Border::default().corner_bottom_right('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ " * "
+ )
+ }
+ test_border! {
+ Modify::new((3, 2)).with(Border::default().bottom('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ " ********** "
+ )
+ }
+ test_border! {
+ Modify::new((3, 2)).with(Border::default().bottom('*').corner_bottom_left('#')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ " #********** "
+ )
+ }
+ test_border! {
+ Modify::new((3, 2)).with(Border::default().bottom('*').corner_bottom_right('#')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ " **********# "
+ )
+ }
+ test_border! {
+ Modify::new((3, 2)).with(Border::default().left('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 * 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((3, 2)).with(Border::default().corner_top_left('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " * "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((3, 2)).with(Border::default().left('#').corner_top_left('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " * "
+ " 2 2-0 # 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((3, 2)).with(Border::default().left('#').corner_bottom_left('@').corner_top_left('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " * "
+ " 2 2-0 # 2-1 2-2 "
+ " @ "
+ )
+ }
+ test_border! {
+ Modify::new((3, 2)).with(Border::default().right('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 * 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((3, 2)).with(Border::default().corner_top_right('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " * "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((3, 2)).with(Border::default().right('#').corner_top_right('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " * "
+ " 2 2-0 2-1 # 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((3, 2)).with(Border::default().right('#').corner_top_right('*').corner_bottom_right('@')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " * "
+ " 2 2-0 2-1 # 2-2 "
+ " @ "
+ )
+ }
+ test_border! {
+ Modify::new((3, 2)).with(Border::default().right('#').corner_top_right('*').corner_bottom_left('@')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " * "
+ " 2 2-0 2-1 # 2-2 "
+ " @ "
+ )
+ }
+ test_border! {
+ Modify::new((3, 2)).with(Border::filled('@')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " @@@@@@@@@@@@ "
+ " 2 2-0 @ 2-1 @ 2-2 "
+ " @@@@@@@@@@@@ "
+ )
+ }
+
+ test_border! {
+ Modify::new((1, 2)).with(Border::default().corner_bottom_left('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " * "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((1, 2)).with(Border::default().corner_bottom_right('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " * "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((1, 2)).with(Border::default().bottom('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " ********** "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((1, 2)).with(Border::default().bottom('*').corner_bottom_left('#')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " #********** "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((1, 2)).with(Border::default().bottom('*').corner_bottom_right('#')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " **********# "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((1, 2)).with(Border::default().left('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string * 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((1, 2)).with(Border::default().corner_top_left('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " * "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((1, 2)).with(Border::default().left('#').corner_top_left('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " * "
+ " 0 a longer string # 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((1, 2)).with(Border::default().left('#').corner_bottom_left('@').corner_top_left('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " * "
+ " 0 a longer string # 0-1 0-2 "
+ " @ "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((1, 2)).with(Border::default().right('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 * 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((1, 2)).with(Border::default().corner_top_right('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " * "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((1, 2)).with(Border::default().right('#').corner_top_right('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " * "
+ " 0 a longer string 0-1 # 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((1, 2)).with(Border::default().right('#').corner_top_right('*').corner_bottom_right('@')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " * "
+ " 0 a longer string 0-1 # 0-2 "
+ " @ "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((1, 2)).with(Border::default().right('#').corner_top_right('*').corner_bottom_left('@')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " * "
+ " 0 a longer string 0-1 # 0-2 "
+ " @ "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((1, 2)).with(Border::filled('@')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " @@@@@@@@@@@@ "
+ " 0 a longer string @ 0-1 @ 0-2 "
+ " @@@@@@@@@@@@ "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+
+ test_border! {
+ Modify::new((0, 3)).with(Border::default().corner_bottom_left('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " * "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((0, 3)).with(Border::default().corner_bottom_right('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " *"
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((0, 3)).with(Border::default().bottom('*')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " **********"
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((0, 3)).with(Border::default().bottom('*').corner_bottom_left('#')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " #**********"
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((0, 3)).with(Border::default().bottom('*').corner_bottom_right('#')),
+ static_table!(
+ " N column 0 column 1 column 2 "
+ " **********#"
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((0, 3)).with(Border::default().left('*')),
+ static_table!(
+ " N column 0 column 1 * column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((0, 3)).with(Border::default().corner_top_left('*')),
+ static_table!(
+ " * "
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((0, 3)).with(Border::default().left('#').corner_top_left('*')),
+ static_table!(
+ " * "
+ " N column 0 column 1 # column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((0, 3)).with(Border::default().left('#').corner_bottom_left('@').corner_top_left('*')),
+ static_table!(
+ " * "
+ " N column 0 column 1 # column 2 "
+ " @ "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((0, 3)).with(Border::default().right('*')),
+ static_table!(
+ " N column 0 column 1 column 2 *"
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((0, 3)).with(Border::default().corner_top_right('*')),
+ static_table!(
+ " *"
+ " N column 0 column 1 column 2 "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((0, 3)).with(Border::default().right('#').corner_top_right('*')),
+ static_table!(
+ " *"
+ " N column 0 column 1 column 2 #"
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((0, 3)).with(Border::default().right('#').corner_top_right('*').corner_bottom_right('@')),
+ static_table!(
+ " *"
+ " N column 0 column 1 column 2 #"
+ " @"
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((0, 3)).with(Border::default().right('#').corner_top_right('*').corner_bottom_left('@')),
+ static_table!(
+ " *"
+ " N column 0 column 1 column 2 #"
+ " @ "
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+ test_border! {
+ Modify::new((0, 3)).with(Border::filled('@')),
+ static_table!(
+ " @@@@@@@@@@@@"
+ " N column 0 column 1 @ column 2 @"
+ " @@@@@@@@@@@@"
+ " 0 a longer string 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ )
+ }
+}
+
+#[cfg(feature = "color")]
+#[test]
+fn border_colored_test() {
+ let table = Matrix::table(2, 2)
+ .with(Style::ascii())
+ .with(
+ Modify::new(Rows::single(1))
+ .with(
+ BorderColor::filled(Color::try_from('*'.blue().to_string()).unwrap())
+ .top(Color::try_from('#'.truecolor(12, 220, 100).to_string()).unwrap()),
+ )
+ .with(Border::filled('*').top('#')),
+ )
+ .to_string();
+
+ assert_eq!(
+ ansi_str::AnsiStr::ansi_strip(&table),
+ static_table!(
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "*###*##########*##########*"
+ "* 0 * 0-0 * 0-1 *"
+ "***************************"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+ )
+ );
+
+ assert_eq!(
+ table,
+ static_table!(
+ "+---+----------+----------+"
+ "| N | column 0 | column 1 |"
+ "\u{1b}[34m*\u{1b}[39m\u{1b}[38;2;12;220;100m###\u{1b}[39m\u{1b}[34m*\u{1b}[39m\u{1b}[38;2;12;220;100m##########\u{1b}[39m\u{1b}[34m*\u{1b}[39m\u{1b}[38;2;12;220;100m##########\u{1b}[39m\u{1b}[34m*\u{1b}[39m"
+ "\u{1b}[34m*\u{1b}[39m 0 \u{1b}[34m*\u{1b}[39m 0-0 \u{1b}[34m*\u{1b}[39m 0-1 \u{1b}[34m*\u{1b}[39m"
+ "\u{1b}[34m***************************\u{1b}[39m"
+ "| 1 | 1-0 | 1-1 |"
+ "+---+----------+----------+"
+ )
+ );
+
+ let table = Matrix::table(2, 2)
+ .with(Style::empty())
+ .with(
+ Modify::new(Rows::single(1))
+ .with(
+ BorderColor::filled(Color::try_from('*'.blue().to_string()).unwrap())
+ .top(Color::try_from('#'.truecolor(12, 220, 100).to_string()).unwrap()),
+ )
+ .with(Border::filled('*').top('#')),
+ )
+ .to_string();
+
+ assert_eq!(
+ ansi_str::AnsiStr::ansi_strip(&table),
+ static_table!(
+ " N column 0 column 1 "
+ "*###*##########*##########*"
+ "* 0 * 0-0 * 0-1 *"
+ "***************************"
+ " 1 1-0 1-1 "
+ )
+ );
+
+ assert_eq!(
+ table,
+ " N column 0 column 1 \n\u{1b}[34m*\u{1b}[39m\u{1b}[38;2;12;220;100m###\u{1b}[39m\u{1b}[34m*\u{1b}[39m\u{1b}[38;2;12;220;100m##########\u{1b}[39m\u{1b}[34m*\u{1b}[39m\u{1b}[38;2;12;220;100m##########\u{1b}[39m\u{1b}[34m*\u{1b}[39m\n\u{1b}[34m*\u{1b}[39m 0 \u{1b}[34m*\u{1b}[39m 0-0 \u{1b}[34m*\u{1b}[39m 0-1 \u{1b}[34m*\u{1b}[39m\n\u{1b}[34m***************************\u{1b}[39m\n 1 1-0 1-1 ",
+ );
+}
+
+#[cfg(feature = "color")]
+#[test]
+fn style_with_color_test() {
+ let mut style: RawStyle = Style::ascii().into();
+ style
+ .set_left(Some('['))
+ .set_right(Some(']'))
+ .set_top(Some('-'))
+ .set_bottom(Some('-'))
+ .set_vertical(Some('|'))
+ .set_intersection(Some('+'));
+ style
+ .set_color_left(Color::FG_RED)
+ .set_color_right(Color::FG_RED)
+ .set_color_top(Color::FG_BLUE)
+ .set_color_bottom(Color::FG_BLUE)
+ .set_color_vertical(Color::FG_YELLOW)
+ .set_color_intersection(Color::try_from(' '.purple().to_string()).unwrap());
+
+ let table = Matrix::new(3, 3).with(style).to_string();
+
+ assert_eq!(
+ ansi_str::AnsiStr::ansi_strip(&table),
+ static_table!(
+ "+---+----------+----------+----------+"
+ "[ N | column 0 | column 1 | column 2 ]"
+ "+---+----------+----------+----------+"
+ "[ 0 | 0-0 | 0-1 | 0-2 ]"
+ "+---+----------+----------+----------+"
+ "[ 1 | 1-0 | 1-1 | 1-2 ]"
+ "+---+----------+----------+----------+"
+ "[ 2 | 2-0 | 2-1 | 2-2 ]"
+ "+---+----------+----------+----------+"
+ )
+ );
+
+ assert_eq!(table, "+\u{1b}[34m---\u{1b}[39m+\u{1b}[34m----------\u{1b}[39m+\u{1b}[34m----------\u{1b}[39m+\u{1b}[34m----------\u{1b}[39m+\n\u{1b}[31m[\u{1b}[39m N \u{1b}[33m|\u{1b}[39m column 0 \u{1b}[33m|\u{1b}[39m column 1 \u{1b}[33m|\u{1b}[39m column 2 \u{1b}[31m]\u{1b}[39m\n+---\u{1b}[35m+\u{1b}[39m----------\u{1b}[35m+\u{1b}[39m----------\u{1b}[35m+\u{1b}[39m----------+\n\u{1b}[31m[\u{1b}[39m 0 \u{1b}[33m|\u{1b}[39m 0-0 \u{1b}[33m|\u{1b}[39m 0-1 \u{1b}[33m|\u{1b}[39m 0-2 \u{1b}[31m]\u{1b}[39m\n+---\u{1b}[35m+\u{1b}[39m----------\u{1b}[35m+\u{1b}[39m----------\u{1b}[35m+\u{1b}[39m----------+\n\u{1b}[31m[\u{1b}[39m 1 \u{1b}[33m|\u{1b}[39m 1-0 \u{1b}[33m|\u{1b}[39m 1-1 \u{1b}[33m|\u{1b}[39m 1-2 \u{1b}[31m]\u{1b}[39m\n+---\u{1b}[35m+\u{1b}[39m----------\u{1b}[35m+\u{1b}[39m----------\u{1b}[35m+\u{1b}[39m----------+\n\u{1b}[31m[\u{1b}[39m 2 \u{1b}[33m|\u{1b}[39m 2-0 \u{1b}[33m|\u{1b}[39m 2-1 \u{1b}[33m|\u{1b}[39m 2-2 \u{1b}[31m]\u{1b}[39m\n+\u{1b}[34m---\u{1b}[39m+\u{1b}[34m----------\u{1b}[39m+\u{1b}[34m----------\u{1b}[39m+\u{1b}[34m----------\u{1b}[39m+");
+}
+
+test_table!(
+ empty_line_clears_lines,
+ Matrix::new(3, 3).with(Style::rounded().remove_horizontals()),
+ "╭───┬──────────┬──────────┬──────────╮"
+ "│ N │ column 0 │ column 1 │ column 2 │"
+ "│ 0 │ 0-0 │ 0-1 │ 0-2 │"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+ "╰───┴──────────┴──────────┴──────────╯"
+);
+
+test_table!(
+ empty_line_clears_lines_1,
+ Matrix::new(3, 3).with(Style::rounded().remove_horizontals()),
+ "╭───┬──────────┬──────────┬──────────╮"
+ "│ N │ column 0 │ column 1 │ column 2 │"
+ "│ 0 │ 0-0 │ 0-1 │ 0-2 │"
+ "│ 1 │ 1-0 │ 1-1 │ 1-2 │"
+ "│ 2 │ 2-0 │ 2-1 │ 2-2 │"
+ "╰───┴──────────┴──────────┴──────────╯"
+);
+
+test_table!(
+ border_color,
+ {
+ use tabled::settings::Color;
+ Matrix::new(3, 3).with(Style::psql()).with(Color::BG_GREEN)
+ },
+ " \u{1b}[42mN\u{1b}[49m | \u{1b}[42mcolumn 0\u{1b}[49m | \u{1b}[42mcolumn 1\u{1b}[49m | \u{1b}[42mcolumn 2\u{1b}[49m \n---+----------+----------+----------\n \u{1b}[42m0\u{1b}[49m | \u{1b}[42m0-0\u{1b}[49m | \u{1b}[42m0-1\u{1b}[49m | \u{1b}[42m0-2\u{1b}[49m \n \u{1b}[42m1\u{1b}[49m | \u{1b}[42m1-0\u{1b}[49m | \u{1b}[42m1-1\u{1b}[49m | \u{1b}[42m1-2\u{1b}[49m \n \u{1b}[42m2\u{1b}[49m | \u{1b}[42m2-0\u{1b}[49m | \u{1b}[42m2-1\u{1b}[49m | \u{1b}[42m2-2\u{1b}[49m "
+);
+
+test_table!(
+ text_color,
+ {
+ use tabled::settings::Color;
+ Matrix::new(3, 3).with(Style::psql()).with(Modify::new(Segment::all()).with(Color::BG_BLACK))
+ },
+ " \u{1b}[40mN\u{1b}[49m | \u{1b}[40mcolumn 0\u{1b}[49m | \u{1b}[40mcolumn 1\u{1b}[49m | \u{1b}[40mcolumn 2\u{1b}[49m \n---+----------+----------+----------\n \u{1b}[40m0\u{1b}[49m | \u{1b}[40m0-0\u{1b}[49m | \u{1b}[40m0-1\u{1b}[49m | \u{1b}[40m0-2\u{1b}[49m \n \u{1b}[40m1\u{1b}[49m | \u{1b}[40m1-0\u{1b}[49m | \u{1b}[40m1-1\u{1b}[49m | \u{1b}[40m1-2\u{1b}[49m \n \u{1b}[40m2\u{1b}[49m | \u{1b}[40m2-0\u{1b}[49m | \u{1b}[40m2-1\u{1b}[49m | \u{1b}[40m2-2\u{1b}[49m "
+);
+
+test_table!(
+ verticals_0,
+ Matrix::new(3, 3)
+ .with(Style::rounded().verticals(vec![VerticalLine::new(0, Line::filled('+')), VerticalLine::new(4, Line::filled('+'))])),
+ "+───┬──────────┬──────────┬──────────+"
+ "+ N │ column 0 │ column 1 │ column 2 +"
+ "├───┼──────────┼──────────┼──────────┤"
+ "+ 0 │ 0-0 │ 0-1 │ 0-2 +"
+ "+ 1 │ 1-0 │ 1-1 │ 1-2 +"
+ "+ 2 │ 2-0 │ 2-1 │ 2-2 +"
+ "+───┴──────────┴──────────┴──────────+"
+);
+
+test_table!(
+ verticals_1,
+ Matrix::new(3, 3)
+ .with(Style::rounded().verticals((1..4).map(|i| VerticalLine::new(i, Line::filled('+'))))),
+ "╭───+──────────+──────────+──────────╮"
+ "│ N + column 0 + column 1 + column 2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 0 + 0-0 + 0-1 + 0-2 │"
+ "│ 1 + 1-0 + 1-1 + 1-2 │"
+ "│ 2 + 2-0 + 2-1 + 2-2 │"
+ "╰───+──────────+──────────+──────────╯"
+);
+
+test_table!(
+ verticals_2,
+ Matrix::new(3, 3).with(Style::rounded().verticals(vec![VerticalLine::new(1, Line::filled('+'))])),
+ "╭───+──────────┬──────────┬──────────╮"
+ "│ N + column 0 │ column 1 │ column 2 │"
+ "├───┼──────────┼──────────┼──────────┤"
+ "│ 0 + 0-0 │ 0-1 │ 0-2 │"
+ "│ 1 + 1-0 │ 1-1 │ 1-2 │"
+ "│ 2 + 2-0 │ 2-1 │ 2-2 │"
+ "╰───+──────────┴──────────┴──────────╯"
+);
+
+test_table!(
+ verticals_3,
+ Matrix::new(3, 3).with(Style::ascii().verticals([VerticalLine::new(1, Line::filled('*'))])),
+ "+---*----------+----------+----------+"
+ "| N * column 0 | column 1 | column 2 |"
+ "+---*----------+----------+----------+"
+ "| 0 * 0-0 | 0-1 | 0-2 |"
+ "+---*----------+----------+----------+"
+ "| 1 * 1-0 | 1-1 | 1-2 |"
+ "+---*----------+----------+----------+"
+ "| 2 * 2-0 | 2-1 | 2-2 |"
+ "+---*----------+----------+----------+"
+);
+
+test_table!(
+ verticals_4,
+ Matrix::new(3, 3).with(Style::ascii().verticals((0..10).map(|i| VerticalLine::new(i, Line::new(Some('*'), Some('x'), Some('c'), Some('2')))))),
+ "c---c----------c----------c----------c"
+ "* N * column 0 * column 1 * column 2 *"
+ "x---x----------x----------x----------x"
+ "* 0 * 0-0 * 0-1 * 0-2 *"
+ "x---x----------x----------x----------x"
+ "* 1 * 1-0 * 1-1 * 1-2 *"
+ "x---x----------x----------x----------x"
+ "* 2 * 2-0 * 2-1 * 2-2 *"
+ "2---2----------2----------2----------2"
+);
+
+test_table!(
+ vertical_line_0,
+ Matrix::new(3, 3)
+ .with(HorizontalLine::new(1, Line::new(Some('8'), Some('8'), Some('8'), Some('8'))))
+ .with(VerticalLine::new(1, Line::new(Some('*'), Some('x'), Some('c'), Some('2')))),
+ "+---c----------+----------+----------+"
+ "| N * column 0 | column 1 | column 2 |"
+ "88888888888888888888888888888888888888"
+ "| 0 * 0-0 | 0-1 | 0-2 |"
+ "+---x----------+----------+----------+"
+ "| 1 * 1-0 | 1-1 | 1-2 |"
+ "+---x----------+----------+----------+"
+ "| 2 * 2-0 | 2-1 | 2-2 |"
+ "+---2----------+----------+----------+"
+);
+
+test_table!(
+ vertical_line_1,
+ Matrix::new(3, 3)
+ .with(Style::empty())
+ .with(VerticalLine::new(1, Line::new(Some('*'), Some('x'), Some('c'), Some('2')))),
+ " c "
+ " N * column 0 column 1 column 2 "
+ " 0 * 0-0 0-1 0-2 "
+ " 1 * 1-0 1-1 1-2 "
+ " 2 * 2-0 2-1 2-2 "
+ " 2 "
+);
+
+test_table!(
+ vertical_line_2,
+ Matrix::new(3, 3)
+ .with(Style::empty())
+ .with(VerticalLine::new(1, Line::new(None, Some('x'), Some('c'), Some('2')))),
+ " c "
+ " N column 0 column 1 column 2 "
+ " 0 0-0 0-1 0-2 "
+ " 1 1-0 1-1 1-2 "
+ " 2 2-0 2-1 2-2 "
+ " 2 "
+);
+
+test_table!(
+ vertical_line_3,
+ Matrix::new(3, 3)
+ .with(Style::empty())
+ .with(VerticalLine::new(1, Line::new(Some('*'), Some('x'), None, None))),
+ " N * column 0 column 1 column 2 "
+ " 0 * 0-0 0-1 0-2 "
+ " 1 * 1-0 1-1 1-2 "
+ " 2 * 2-0 2-1 2-2 "
+);
+
+test_table!(
+ override_horizontal_border_on_line,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Rows::single(1))
+ .with(BorderChar::horizontal(':', Offset::Begin(0)))
+ .with(BorderChar::horizontal(':', Offset::End(0)))
+ ),
+ "| N | column 0 | column 1 | column 2 |"
+ "|:-:|:--------:|:--------:|:--------:|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ override_horizontal_border_on_borders,
+ Matrix::new(3, 3)
+ .with(Modify::new(Rows::new(..5))
+ .with(BorderChar::horizontal(':', Offset::Begin(0)))
+ .with(BorderChar::horizontal('y', Offset::Begin(3)))
+ .with(BorderChar::horizontal(':', Offset::End(0)))
+ .with(BorderChar::horizontal('x', Offset::End(3)))
+ ),
+ "+:-:+:--y--x--:+:--y--x--:+:--y--x--:+"
+ "| N | column 0 | column 1 | column 2 |"
+ "+:-:+:--y--x--:+:--y--x--:+:--y--x--:+"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "+:-:+:--y--x--:+:--y--x--:+:--y--x--:+"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "+:-:+:--y--x--:+:--y--x--:+:--y--x--:+"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ "+:-:+:--y--x--:+:--y--x--:+:--y--x--:+"
+);
+
+test_table!(
+ override_horizontal_border_on_border,
+ Matrix::new(3, 3)
+ .with(Modify::new(Rows::new(..5))
+ .with(Border::filled('['))
+ .with(BorderChar::horizontal(':', Offset::Begin(0)))
+ .with(BorderChar::horizontal('y', Offset::Begin(3)))
+ .with(BorderChar::horizontal(':', Offset::End(0)))
+ .with(BorderChar::horizontal('x', Offset::End(3)))
+ ),
+ "[:[:[:[[y[[x[[:[:[[y[[x[[:[:[[y[[x[[:["
+ "[ N [ column 0 [ column 1 [ column 2 ["
+ "[:[:[:[[y[[x[[:[:[[y[[x[[:[:[[y[[x[[:["
+ "[ 0 [ 0-0 [ 0-1 [ 0-2 ["
+ "[:[:[:[[y[[x[[:[:[[y[[x[[:[:[[y[[x[[:["
+ "[ 1 [ 1-0 [ 1-1 [ 1-2 ["
+ "[:[:[:[[y[[x[[:[:[[y[[x[[:[:[[y[[x[[:["
+ "[ 2 [ 2-0 [ 2-1 [ 2-2 ["
+ "[:[:[:[[y[[x[[:[:[[y[[x[[:[:[[y[[x[[:["
+);
+
+test_table!(
+ override_vertical_border_on_line,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Columns::single(1))
+ .with(BorderChar::vertical(':', Offset::Begin(0)))
+ ),
+ "| N : column 0 | column 1 | column 2 |"
+ "|---|----------|----------|----------|"
+ "| 0 : 0-0 | 0-1 | 0-2 |"
+ "| 1 : 1-0 | 1-1 | 1-2 |"
+ "| 2 : 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ override_vertical_border_on_line_1,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Columns::single(1))
+ .with(BorderChar::vertical(':', Offset::End(0)))
+ ),
+ "| N : column 0 | column 1 | column 2 |"
+ "|---|----------|----------|----------|"
+ "| 0 : 0-0 | 0-1 | 0-2 |"
+ "| 1 : 1-0 | 1-1 | 1-2 |"
+ "| 2 : 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ override_vertical_border_on_line_multiline,
+ Matrix::new(3, 3)
+ .with(Modify::new(Rows::single(1)).with(Format::content(|s| format!("\nsome text\ntext\n{s}\ntext\ntext\n"))))
+ .with(Style::markdown())
+ .with(Modify::new(Columns::single(1))
+ .with(BorderChar::vertical(':', Offset::Begin(4)))
+ ),
+ "| N | column 0 | column 1 | column 2 |"
+ "|-----------|-----------|-----------|-----------|"
+ "| | | | |"
+ "| some text | some text | some text | some text |"
+ "| text | text | text | text |"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| text : text | text | text |"
+ "| text | text | text | text |"
+ "| | | | |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ override_vertical_border_on_line_multiline_2,
+ Matrix::new(3, 3)
+ .with(Modify::new(Rows::single(1)).with(Format::content(|s| format!("\nsome text\ntext\n{s}\ntext\ntext\n"))))
+ .with(Style::markdown())
+ .with(Modify::new(Columns::single(1))
+ .with(BorderChar::vertical(':', Offset::End(4)))
+ ),
+ "| N | column 0 | column 1 | column 2 |"
+ "|-----------|-----------|-----------|-----------|"
+ "| | | | |"
+ "| some text | some text | some text | some text |"
+ "| text : text | text | text |"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| text | text | text | text |"
+ "| text | text | text | text |"
+ "| | | | |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ override_vertical_and_horizontal_border_on_line,
+ Matrix::new(3, 3)
+ .with(Modify::new(Rows::single(1)).with(Format::content(|s| format!("\nsome text\ntext\n{s}\ntext\ntext\n"))))
+ .with(Style::markdown())
+ .with(Modify::new(Columns::new(..5))
+ .with(BorderChar::vertical('y', Offset::Begin(0)))
+ .with(BorderChar::vertical('^', Offset::End(0)))
+ )
+ .with(Modify::new(Rows::single(1))
+ .with(BorderChar::horizontal('x', Offset::Begin(0)))
+ .with(BorderChar::horizontal('@', Offset::End(0)))
+ ),
+ "y N y column 0 y column 1 y column 2 y"
+ "|x---------@|x---------@|x---------@|x---------@|"
+ "y y y y y"
+ "| some text | some text | some text | some text |"
+ "| text | text | text | text |"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| text | text | text | text |"
+ "| text | text | text | text |"
+ "^ ^ ^ ^ ^"
+ "y 1 y 1-0 y 1-1 y 1-2 y"
+ "y 2 y 2-0 y 2-1 y 2-2 y"
+);
+
+test_table!(
+ table_format_alignment_left_test,
+ format!("{:<}", Table::new(vec!["hello", "world", "!"])),
+ "+-------+"
+ "| &str |"
+ "+-------+"
+ "| hello |"
+ "+-------+"
+ "| world |"
+ "+-------+"
+ "| ! |"
+ "+-------+"
+);
+
+test_table!(
+ table_format_alignment_right_test,
+ format!("{:>}", Table::new(vec!["hello", "world", "!"])),
+ "+-------+"
+ "| &str |"
+ "+-------+"
+ "| hello |"
+ "+-------+"
+ "| world |"
+ "+-------+"
+ "| ! |"
+ "+-------+"
+);
+
+test_table!(
+ table_format_alignment_center_test,
+ format!("{:^}", Table::new(vec!["hello", "world", "!"])),
+ "+-------+"
+ "| &str |"
+ "+-------+"
+ "| hello |"
+ "+-------+"
+ "| world |"
+ "+-------+"
+ "| ! |"
+ "+-------+"
+);
+
+test_table!(
+ table_format_width_0_test,
+ format!("{:<13}", Table::new(vec!["hello", "world", "!"])),
+ " +-------+"
+ " | &str |"
+ " +-------+"
+ " | hello |"
+ " +-------+"
+ " | world |"
+ " +-------+"
+ " | ! |"
+ " +-------+"
+);
+
+test_table!(
+ table_format_width_1_test,
+ format!("{:>13}", Table::new(vec!["hello", "world", "!"])),
+ "+-------+ "
+ "| &str | "
+ "+-------+ "
+ "| hello | "
+ "+-------+ "
+ "| world | "
+ "+-------+ "
+ "| ! | "
+ "+-------+ "
+);
+
+test_table!(
+ table_format_width_2_test,
+ format!("{:^13}", Table::new(vec!["hello", "world", "!"])),
+ " +-------+ "
+ " | &str | "
+ " +-------+ "
+ " | hello | "
+ " +-------+ "
+ " | world | "
+ " +-------+ "
+ " | ! | "
+ " +-------+ "
+);
+
+test_table!(
+ table_format_width_3_test,
+ format!("{:x^13}", Table::new(vec!["hello", "world", "!"])),
+ "xx+-------+xx"
+ "xx| &str |xx"
+ "xx+-------+xx"
+ "xx| hello |xx"
+ "xx+-------+xx"
+ "xx| world |xx"
+ "xx+-------+xx"
+ "xx| ! |xx"
+ "xx+-------+xx"
+);
+
+test_table!(
+ table_format_width_4_test,
+ format!("{:x<13}", Table::new(vec!["hello", "world", "!"])),
+ "xxxx+-------+"
+ "xxxx| &str |"
+ "xxxx+-------+"
+ "xxxx| hello |"
+ "xxxx+-------+"
+ "xxxx| world |"
+ "xxxx+-------+"
+ "xxxx| ! |"
+ "xxxx+-------+"
+);
+
+test_table!(
+ table_format_width_5_test,
+ format!("{:x>13}", Table::new(vec!["hello", "world", "!"])),
+ "+-------+xxxx"
+ "| &str |xxxx"
+ "+-------+xxxx"
+ "| hello |xxxx"
+ "+-------+xxxx"
+ "| world |xxxx"
+ "+-------+xxxx"
+ "| ! |xxxx"
+ "+-------+xxxx"
+);
+
+test_table!(
+ table_style_no_bottom_no_new_line,
+ Matrix::table(0, 0).with(Style::markdown().remove_horizontals()),
+ "| N |"
+);
diff --git a/vendor/tabled/tests/settings/width_test.rs b/vendor/tabled/tests/settings/width_test.rs
new file mode 100644
index 000000000..d2bc84a46
--- /dev/null
+++ b/vendor/tabled/tests/settings/width_test.rs
@@ -0,0 +1,2836 @@
+#![cfg(feature = "std")]
+
+use tabled::{
+ grid::util::string::string_width_multiline,
+ settings::{
+ formatting::{TabSize, TrimStrategy},
+ object::{Columns, Object, Rows, Segment},
+ peaker::{PriorityMax, PriorityMin},
+ width::{Justify, MinWidth, SuffixLimit, Width},
+ Alignment, Margin, Modify, Padding, Panel, Settings, Span, Style,
+ },
+};
+
+use crate::matrix::Matrix;
+use testing_table::{is_lines_equal, static_table, test_table};
+
+#[cfg(feature = "color")]
+use ::{ansi_str::AnsiStr, owo_colors::OwoColorize};
+
+#[cfg(all(feature = "derive", feature = "color"))]
+use ::owo_colors::AnsiColors;
+
+test_table!(
+ max_width,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Columns::new(1..).not(Rows::single(0))).with(Width::truncate(1))),
+ "| N | column 0 | column 1 | column 2 |"
+ "|---|----------|----------|----------|"
+ "| 0 | 0 | 0 | 0 |"
+ "| 1 | 1 | 1 | 1 |"
+ "| 2 | 2 | 2 | 2 |"
+);
+
+test_table!(
+ max_width_with_suffix,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(
+ Modify::new(Columns::new(1..).not(Rows::single(0)))
+ .with(Width::truncate(2).suffix("...")),
+ ),
+ "| N | column 0 | column 1 | column 2 |"
+ "|---|----------|----------|----------|"
+ "| 0 | .. | .. | .. |"
+ "| 1 | .. | .. | .. |"
+ "| 2 | .. | .. | .. |"
+);
+
+test_table!(
+ max_width_doesnt_icrease_width_if_it_is_smaller,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Columns::new(1..).not(Rows::single(0))).with(Width::truncate(50))),
+ "| N | column 0 | column 1 | column 2 |"
+ "|---|----------|----------|----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ max_width_wrapped,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Columns::new(1..).not(Rows::single(0))).with(Width::wrap(2))),
+ "| N | column 0 | column 1 | column 2 |"
+ "|---|----------|----------|----------|"
+ "| 0 | 0- | 0- | 0- |"
+ "| | 0 | 1 | 2 |"
+ "| 1 | 1- | 1- | 1- |"
+ "| | 0 | 1 | 2 |"
+ "| 2 | 2- | 2- | 2- |"
+ "| | 0 | 1 | 2 |"
+);
+
+test_table!(
+ max_width_wrapped_does_nothing_if_str_is_smaller,
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Columns::new(1..).not(Rows::single(0))).with(Width::wrap(100))),
+ "| N | column 0 | column 1 | column 2 |"
+ "|---|----------|----------|----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+);
+
+test_table!(
+ max_width_wrapped_keep_words_0,
+ {
+ let table = Matrix::iter(vec!["this is a long sentence"])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words()))
+ .to_string();
+
+ assert!(is_lines_equal(&table, 17 + 2 + 2));
+
+ table
+ },
+ "| &str |"
+ "|-------------------|"
+ "| this is a long |"
+ "| sentence |"
+);
+
+test_table!(
+ max_width_wrapped_keep_words_1,
+ {
+ let table = Matrix::iter(vec!["this is a long sentence"])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words()))
+ .to_string();
+
+ assert!(is_lines_equal(&table, 17 + 2 + 2));
+
+ table
+ },
+ "| &str |"
+ "|-------------------|"
+ "| this is a long |"
+ "| sentence |"
+);
+
+test_table!(
+ max_width_wrapped_keep_words_2,
+ {
+ let table = Matrix::iter(vec!["this is a long sentence"])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words()))
+ .to_string();
+
+ assert!(is_lines_equal(&table, 17 + 2 + 2));
+
+ table
+ },
+ "| &str |"
+ "|-------------------|"
+ "| this is a long |"
+ "| sentence |"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ max_width_wrapped_keep_words_3,
+ {
+ let table = Matrix::iter(vec!["this is a long sentence"])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words()))
+ .to_string();
+
+ assert!(is_lines_equal(&table, 17 + 2 + 2));
+
+ table
+ },
+ // 'sentence' doesn't have a space ' sentence' because we use left alignment
+ "| &str |"
+ "|-------------------|"
+ "| this is a long |"
+ "| sentence |"
+);
+
+#[cfg(not(feature = "color"))]
+test_table!(
+ max_width_wrapped_keep_words_3,
+ {
+ let table = Matrix::iter(vec!["this is a long sentence"])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words()))
+ .to_string();
+
+ assert!(is_lines_equal(&table, 17 + 2 + 2));
+
+ table
+ },
+ // 'sentence' doesn't have a space ' sentence' because we use left alignment
+ "| &str |"
+ "|-------------------|"
+ "| this is a long |"
+ "| sentence |"
+);
+
+test_table!(
+ max_width_wrapped_keep_words_4,
+ {
+ let table = Matrix::iter(vec!["this"])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Width::wrap(10).keep_words()))
+ .to_string();
+
+ assert!(is_lines_equal(&table, 8));
+
+ table
+ },
+ "| &str |"
+ "|------|"
+ "| this |"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ max_width_wrapped_keep_words_color_0,
+ {
+ let table = Matrix::iter(vec!["this is a long sentence".on_black().green().to_string()])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words()))
+ .to_string();
+
+ AnsiStr::ansi_strip(&table).to_string()
+ },
+ "| String |"
+ "|-------------------|"
+ "| this is a long |"
+ "| sentence |"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ max_width_wrapped_keep_words_color_0_1,
+ Matrix::iter(vec!["this is a long sentence".on_black().green().to_string()])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words())),
+ "| String |"
+ "|-------------------|"
+ "| \u{1b}[32m\u{1b}[40mthis is a long \u{1b}[39m\u{1b}[49m |"
+ "| \u{1b}[32m\u{1b}[40msentence\u{1b}[39m\u{1b}[49m |"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ max_width_wrapped_keep_words_color_1,
+ {
+ let table = Matrix::iter(vec!["this is a long sentence".on_black().green().to_string()])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words()))
+ .to_string();
+
+ AnsiStr::ansi_strip(&table).to_string()
+ },
+ "| String |"
+ "|-------------------|"
+ "| this is a long |"
+ "| sentence |"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ max_width_wrapped_keep_words_color_1_1,
+ Matrix::iter(vec!["this is a long sentence".on_black().green().to_string()])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words())),
+ "| String |"
+ "|-------------------|"
+ "| \u{1b}[32m\u{1b}[40mthis is a long \u{1b}[39m\u{1b}[49m |"
+ "| \u{1b}[32m\u{1b}[40msentence\u{1b}[39m\u{1b}[49m |"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ max_width_wrapped_keep_words_color_2,
+ {
+ let table = Matrix::iter(vec!["this is a long sentence".on_black().green().to_string()])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words()))
+ .to_string();
+
+ AnsiStr::ansi_strip(&table).to_string()
+ },
+ "| String |"
+ "|-------------------|"
+ "| this is a long |"
+ "| sentence |"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ max_width_wrapped_keep_words_color_2_1,
+ Matrix::iter(vec!["this is a long sentence".on_black().green().to_string()])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words())),
+ "| String |"
+ "|-------------------|"
+ "| \u{1b}[32m\u{1b}[40mthis is a long \u{1b}[39m\u{1b}[49m |"
+ "| \u{1b}[32m\u{1b}[40msentence\u{1b}[39m\u{1b}[49m |"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ max_width_wrapped_keep_words_color_3,
+ {
+ let table = Matrix::iter(vec!["this is a long sentence".on_black().green().to_string()])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words()))
+ .to_string();
+
+ AnsiStr::ansi_strip(&table).to_string()
+ },
+ "| String |"
+ "|-------------------|"
+ "| this is a long |"
+ "| sentence |"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ max_width_wrapped_keep_words_color_3_1,
+ Matrix::iter(vec!["this is a long sentence".on_black().green().to_string()])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words())),
+ "| String |"
+ "|-------------------|"
+ "| \u{1b}[32m\u{1b}[40mthis is a long \u{1b}[39m\u{1b}[49m |"
+ "| \u{1b}[32m\u{1b}[40m sentence\u{1b}[39m\u{1b}[49m |"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ max_width_wrapped_keep_words_color_4,
+ {
+ let table = Matrix::iter(vec!["this".on_black().green().to_string()])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Width::wrap(10).keep_words()))
+ .to_string();
+
+ AnsiStr::ansi_strip(&table).to_string()
+ },
+ "| String |"
+ "|--------|"
+ "| this |"
+);
+
+#[cfg(feature = "color")]
+test_table!(
+ max_width_wrapped_keep_words_color_4_1,
+ Matrix::iter(vec!["this".on_black().green().to_string()])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Width::wrap(10).keep_words())),
+ "| String |"
+ "|--------|"
+ "| \u{1b}[32;40mthis\u{1b}[0m |"
+);
+
+test_table!(
+ max_width_wrapped_keep_words_long_word,
+ Matrix::iter(["this is a long sentencesentencesentence"])
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words())),
+ "| &str |"
+ "|-------------------|"
+ "| this is a long se |"
+ "| ntencesentencesen |"
+ "| tence |"
+);
+
+#[cfg(feature = "color")]
+#[test]
+fn max_width_wrapped_keep_words_long_word_color() {
+ let data = vec!["this is a long sentencesentencesentence"
+ .on_black()
+ .green()
+ .to_string()];
+ let table = Matrix::iter(data)
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Modify::new(Segment::all()).with(Width::wrap(17).keep_words()))
+ .to_string();
+
+ assert_eq!(
+ ansi_str::AnsiStr::ansi_strip(&table),
+ static_table!(
+ "| String |"
+ "|-------------------|"
+ "| this is a long se |"
+ "| ntencesentencesen |"
+ "| tence |"
+ )
+ );
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| String |"
+ "|-------------------|"
+ "| \u{1b}[32m\u{1b}[40mthis is a long se\u{1b}[39m\u{1b}[49m |"
+ "| \u{1b}[32m\u{1b}[40mntencesentencesen\u{1b}[39m\u{1b}[49m |"
+ "| \u{1b}[32m\u{1b}[40mtence\u{1b}[39m\u{1b}[49m |"
+ )
+ );
+}
+
+#[cfg(feature = "color")]
+#[test]
+fn max_width_keep_words_1() {
+ use tabled::settings::style::HorizontalLine;
+
+ let table = Matrix::iter(["asdf"])
+ .with(Width::wrap(7).keep_words())
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "+-----+"
+ "| &st |"
+ "| r |"
+ "+-----+"
+ "| asd |"
+ "| f |"
+ "+-----+"
+ )
+ );
+
+ let table = Matrix::iter(["qweqw eqwe"])
+ .with(Width::wrap(8).keep_words())
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "+------+"
+ "| &str |"
+ "+------+"
+ "| qweq |"
+ "| w |"
+ "| eqwe |"
+ "+------+"
+ )
+ );
+
+ let table = Matrix::iter([
+ ["123 45678", "qweqw eqwe", "..."],
+ ["0", "1", "..."],
+ ["0", "1", "..."],
+ ])
+ .with(
+ Style::modern()
+ .remove_horizontal()
+ .horizontals([HorizontalLine::new(1, Style::modern().get_horizontal())]),
+ )
+ .with(Width::wrap(21).keep_words().priority::<PriorityMax>())
+ .with(Alignment::center())
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "┌──────┬──────┬─────┐"
+ "│ 0 │ 1 │ 2 │"
+ "├──────┼──────┼─────┤"
+ "│ 123 │ qweq │ ... │"
+ "│ 4567 │ w │ │"
+ "│ 8 │ eqwe │ │"
+ "│ 0 │ 1 │ ... │"
+ "│ 0 │ 1 │ ... │"
+ "└──────┴──────┴─────┘"
+ )
+ );
+}
+
+#[cfg(feature = "color")]
+#[test]
+fn max_width_wrapped_collored() {
+ let data = &[
+ "asd".red().to_string(),
+ "zxc2".blue().to_string(),
+ "asdasd".on_black().green().to_string(),
+ ];
+
+ let table = Matrix::iter(data)
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Width::wrap(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ "| St |\n| ri |\n| ng |\n|----|\n| \u{1b}[31mas\u{1b}[39m |\n| \u{1b}[31md\u{1b}[39m |\n| \u{1b}[34mzx\u{1b}[39m |\n| \u{1b}[34mc2\u{1b}[39m |\n| \u{1b}[32m\u{1b}[40mas\u{1b}[39m\u{1b}[49m |\n| \u{1b}[32m\u{1b}[40mda\u{1b}[39m\u{1b}[49m |\n| \u{1b}[32m\u{1b}[40msd\u{1b}[39m\u{1b}[49m |"
+ );
+}
+
+#[test]
+fn dont_change_content_if_width_is_less_then_max_width() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Width::truncate(1000).suffix("...")))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|---|----------|----------|----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+}
+
+#[test]
+fn max_width_with_emoji() {
+ let data = &["🤠", "😳🥵🥶😱😨", "🚴🏻‍♀️🚴🏻🚴🏻‍♂️🚵🏻‍♀️🚵🏻🚵🏻‍♂️"];
+
+ let table = Matrix::iter(data)
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Width::truncate(6).suffix("...")))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| &str |"
+ "|--------|"
+ "| 🤠 |"
+ "| 😳�... |"
+ "| 🚴�... |"
+ )
+ );
+}
+
+#[cfg(feature = "color")]
+#[test]
+fn color_chars_are_stripped() {
+ let data = &[
+ "asd".red().to_string(),
+ "zxc".blue().to_string(),
+ "asdasd".on_black().green().to_string(),
+ ];
+
+ let table = Matrix::iter(data)
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Width::truncate(4).suffix("...")))
+ .to_string();
+
+ assert_eq!(
+ ansi_str::AnsiStr::ansi_strip(&table),
+ static_table!(
+ "| S... |"
+ "|------|"
+ "| asd |"
+ "| zxc |"
+ "| a... |"
+ )
+ );
+
+ assert_eq!(
+ table,
+ "| S... |\n|------|\n| \u{1b}[31masd\u{1b}[39m |\n| \u{1b}[34mzxc\u{1b}[39m |\n| \u{1b}[32;40ma\u{1b}[39m\u{1b}[49m... |",
+ );
+}
+
+#[test]
+fn min_width() {
+ let mut table = Matrix::table(3, 3);
+ table
+ .with(Style::markdown())
+ .with(Modify::new(Rows::single(0)).with(MinWidth::new(12)));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|--------------|--------------|--------------|--------------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ ),
+ );
+
+ table.with(Modify::new(Segment::all()).with(TrimStrategy::None));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|--------------|--------------|--------------|--------------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ ),
+ );
+}
+
+#[test]
+fn min_width_with_filler() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Rows::single(0)).with(MinWidth::new(12).fill_with('.')))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| N........... | column 0.... | column 1.... | column 2.... |"
+ "|--------------|--------------|--------------|--------------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+}
+
+#[test]
+fn min_width_one_column() {
+ let mut table = Matrix::table(3, 3);
+ table
+ .with(Style::markdown())
+ .with(Modify::new((0, 0)).with(MinWidth::new(5)));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|-------|----------|----------|----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+
+ table.with(Modify::new(Segment::all()).with(TrimStrategy::None));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|-------|----------|----------|----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+}
+
+#[test]
+fn min_width_on_smaller_content() {
+ assert_eq!(
+ Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Rows::single(0)).with(MinWidth::new(1)))
+ .to_string(),
+ Matrix::new(3, 3).with(Style::markdown()).to_string()
+ );
+}
+
+#[test]
+fn min_with_max_width() {
+ let mut table = Matrix::table(3, 3);
+ table
+ .with(Style::markdown())
+ .with(Modify::new(Rows::single(0)).with(MinWidth::new(3)))
+ .with(Modify::new(Rows::single(0)).with(Width::truncate(3)));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ "| N | col | col | col |"
+ "|-----|-----|-----|-----|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+
+ table.with(Modify::new(Segment::all()).with(TrimStrategy::None));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ "| N | col | col | col |"
+ "|-----|-----|-----|-----|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+}
+
+#[test]
+fn min_with_max_width_truncate_suffix() {
+ let mut table = Matrix::table(3, 3);
+ table
+ .with(Style::markdown())
+ .with(Modify::new(Rows::single(0)).with(MinWidth::new(3)))
+ .with(Modify::new(Rows::single(0)).with(Width::truncate(3).suffix("...")));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ "| N | ... | ... | ... |"
+ "|-----|-----|-----|-----|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+
+ table.with(Modify::new(Segment::all()).with(TrimStrategy::None));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ "| N | ... | ... | ... |"
+ "|-----|-----|-----|-----|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+}
+
+#[test]
+fn min_with_max_width_truncate_suffix_limit_replace() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(
+ Modify::new(Rows::single(0)).with(
+ Width::truncate(3)
+ .suffix("...")
+ .suffix_limit(SuffixLimit::Replace('x')),
+ ),
+ )
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | xxx | xxx | xxx |"
+ "|---|-----|-----|-----|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+}
+
+#[test]
+fn min_with_max_width_truncate_suffix_limit_cut() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(
+ Modify::new(Rows::single(0)).with(
+ Width::truncate(3)
+ .suffix("qwert")
+ .suffix_limit(SuffixLimit::Cut),
+ ),
+ )
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | qwe | qwe | qwe |"
+ "|---|-----|-----|-----|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+}
+
+#[test]
+fn min_with_max_width_truncate_suffix_limit_ignore() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(
+ Modify::new(Rows::single(0)).with(
+ Width::truncate(3)
+ .suffix("qwert")
+ .suffix_limit(SuffixLimit::Ignore),
+ ),
+ )
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | col | col | col |"
+ "|---|-----|-----|-----|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+}
+
+#[cfg(feature = "color")]
+#[test]
+fn min_with_max_width_truncate_suffix_try_color() {
+ let data = &[
+ "asd".red().to_string(),
+ "zxc".blue().to_string(),
+ "asdasd".on_black().green().to_string(),
+ ];
+
+ let table = Matrix::iter(data)
+ .with(Style::markdown())
+ .with(Width::truncate(7).suffix("..").suffix_try_color(true))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| S.. |"
+ "|-----|"
+ "| \u{1b}[31masd\u{1b}[39m |"
+ "| \u{1b}[34mzxc\u{1b}[39m |"
+ "| \u{1b}[32;40ma\u{1b}[39m\u{1b}[49m\u{1b}[32m\u{1b}[40m..\u{1b}[39m\u{1b}[49m |"
+ )
+ );
+}
+
+#[cfg(feature = "color")]
+#[test]
+fn min_width_color() {
+ let data = &[
+ "asd".red().to_string(),
+ "zxc".blue().to_string(),
+ "asdasd".on_black().green().to_string(),
+ ];
+
+ let table = Matrix::iter(data)
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(MinWidth::new(10)))
+ .to_string();
+
+ assert_eq!(
+ ansi_str::AnsiStr::ansi_strip(&table),
+ static_table!(
+ "| String |"
+ "|------------|"
+ "| asd |"
+ "| zxc |"
+ "| asdasd |"
+ )
+ );
+
+ assert_eq!(
+ table,
+ "| String |\n|------------|\n| \u{1b}[31masd\u{1b}[39m |\n| \u{1b}[34mzxc\u{1b}[39m |\n| \u{1b}[32;40masdasd\u{1b}[0m |",
+ );
+}
+
+#[cfg(feature = "color")]
+#[test]
+fn min_width_color_with_smaller_then_width() {
+ let data = &[
+ "asd".red().to_string(),
+ "zxc".blue().to_string(),
+ "asdasd".on_black().green().to_string(),
+ ];
+
+ assert_eq!(
+ Matrix::iter(data)
+ .with(Modify::new(Segment::all()).with(MinWidth::new(1)))
+ .to_string(),
+ Matrix::iter(data).to_string()
+ );
+}
+
+#[test]
+fn total_width_big() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Width::truncate(80))
+ .with(MinWidth::new(80))
+ .to_string();
+
+ assert_eq!(string_width_multiline(&table), 80);
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|--------------|---------------------|--------------------|--------------------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(TrimStrategy::None))
+ .with(Settings::new(Width::truncate(80), Width::increase(80)))
+ .to_string();
+
+ assert_eq!(string_width_multiline(&table), 80);
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|--------------|---------------------|--------------------|--------------------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+}
+
+#[test]
+fn total_width_big_with_panel() {
+ let table = Matrix::new(3, 3)
+ .with(Panel::horizontal(0, "Hello World"))
+ .with(
+ Modify::new(Segment::all())
+ .with(Alignment::center())
+ .with(Padding::zero()),
+ )
+ .with(Style::markdown())
+ .with(Width::truncate(80))
+ .with(MinWidth::new(80))
+ .to_string();
+
+ assert!(is_lines_equal(&table, 80));
+ assert_eq!(
+ table,
+ static_table!(
+ "| Hello World |"
+ "|--------------|---------------------|--------------------|--------------------|"
+ "| N | column 0 | column 1 | column 2 |"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+}
+
+#[test]
+fn total_width_big_with_panel_with_wrapping_doesnt_affect_increase() {
+ let table1 = Matrix::new(3, 3)
+ .with(Panel::horizontal(0, "Hello World"))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::markdown())
+ .with(Width::wrap(80))
+ .with(MinWidth::new(80))
+ .to_string();
+
+ let table2 = Matrix::new(3, 3)
+ .with(Panel::horizontal(0, "Hello World"))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::markdown())
+ .with(Width::truncate(80))
+ .with(MinWidth::new(80))
+ .to_string();
+
+ assert_eq!(table1, table2);
+}
+
+#[test]
+fn total_width_small() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Width::truncate(14))
+ .with(MinWidth::new(14))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| | | | c |"
+ "|--|--|--|---|"
+ "| | | | 0 |"
+ "| | | | 1 |"
+ "| | | | 2 |"
+ )
+ );
+ assert!(is_lines_equal(&table, 14));
+}
+
+#[test]
+fn total_width_smaller_then_content() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Width::truncate(8))
+ .with(MinWidth::new(8))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| | | | |"
+ "|--|--|--|--|"
+ "| | | | |"
+ "| | | | |"
+ "| | | | |"
+ )
+ );
+}
+
+#[test]
+fn total_width_small_with_panel() {
+ let table = Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::markdown())
+ .with(Width::truncate(20))
+ .with(MinWidth::new(20))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| | co | co | col |"
+ "|--|----|----|-----|"
+ "| | 0- | 0- | 0-2 |"
+ "| | 1- | 1- | 1-2 |"
+ "| | 2- | 2- | 2-2 |"
+ )
+ );
+ assert!(is_lines_equal(&table, 20));
+
+ let table = Matrix::iter(Vec::<usize>::new())
+ .with(Panel::horizontal(0, "Hello World"))
+ .with(
+ Modify::new(Segment::all())
+ .with(Alignment::center())
+ .with(Padding::zero()),
+ )
+ .with(Width::truncate(5))
+ .with(MinWidth::new(5))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!("+---+" "|Hel|" "+---+" "|usi|" "+---+")
+ );
+ assert!(is_lines_equal(&table, 5));
+
+ let table = Matrix::table(1, 2)
+ .with(Panel::horizontal(0, "Hello World"))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::markdown())
+ .with(Width::truncate(20))
+ .with(MinWidth::new(20))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| Hello World |"
+ "|--|-------|-------|"
+ "| | colum | colum |"
+ "| | 0-0 | 0-1 |"
+ )
+ );
+ assert!(is_lines_equal(&table, 20));
+
+ let table = Matrix::new(3, 3)
+ .with(Panel::horizontal(0, "Hello World"))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::markdown())
+ .with(Width::truncate(20))
+ .with(MinWidth::new(20))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| Hello World |"
+ "|--|----|----|-----|"
+ "| | co | co | col |"
+ "| | 0- | 0- | 0-2 |"
+ "| | 1- | 1- | 1-2 |"
+ "| | 2- | 2- | 2-2 |"
+ )
+ );
+ assert!(is_lines_equal(&table, 20));
+
+ let table = Matrix::new(3, 3)
+ .with(Panel::horizontal(0, "Hello World"))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::markdown())
+ .with(Width::truncate(6))
+ .with(MinWidth::new(6))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| Hello Wor |"
+ "|--|--|--|--|"
+ "| | | | |"
+ "| | | | |"
+ "| | | | |"
+ "| | | | |"
+ )
+ );
+ assert!(is_lines_equal(&table, 13));
+
+ let table = Matrix::new(3, 3)
+ .with(Panel::horizontal(0, "Hello World"))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::markdown())
+ .with(Width::truncate(14))
+ .with(MinWidth::new(14))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| Hello Worl |"
+ "|--|--|--|---|"
+ "| | | | c |"
+ "| | | | 0 |"
+ "| | | | 1 |"
+ "| | | | 2 |"
+ )
+ );
+ assert!(is_lines_equal(&table, 14));
+
+ let table = Matrix::new(3, 3)
+ .with(Panel::horizontal(0, "Hello World 123"))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::markdown())
+ .with(Width::truncate(14))
+ .with(MinWidth::new(14))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| Hello Worl |"
+ "|--|--|--|---|"
+ "| | | | c |"
+ "| | | | 0 |"
+ "| | | | 1 |"
+ "| | | | 2 |"
+ )
+ );
+ assert!(is_lines_equal(&table, 14));
+}
+
+#[cfg(feature = "color")]
+#[test]
+fn total_width_wrapping() {
+ let table = Matrix::new(3, 3)
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::markdown())
+ .with(Width::wrap(20))
+ .with(MinWidth::new(20))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| | co | co | col |"
+ "| | lu | lu | umn |"
+ "| | mn | mn | 2 |"
+ "| | 0 | 1 | |"
+ "|--|----|----|-----|"
+ "| | 0- | 0- | 0-2 |"
+ "| | 0 | 1 | |"
+ "| | 1- | 1- | 1-2 |"
+ "| | 0 | 1 | |"
+ "| | 2- | 2- | 2-2 |"
+ "| | 0 | 1 | |"
+ )
+ );
+ assert!(is_lines_equal(&table, 20));
+
+ let table = Matrix::new(3, 3)
+ .insert((3, 2), "some loong string")
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::markdown())
+ .with(Width::wrap(20).keep_words())
+ .with(MinWidth::new(20))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| | | column | |"
+ "| | | 1 | |"
+ "|--|--|---------|--|"
+ "| | | 0-1 | |"
+ "| | | 1-1 | |"
+ "| | | some | |"
+ "| | | loong | |"
+ "| | | string | |"
+ )
+ );
+ assert!(is_lines_equal(&table, 20));
+}
+
+#[test]
+fn total_width_small_with_panel_using_wrapping() {
+ let table = Matrix::new(3, 3)
+ .with(Panel::horizontal(0, "Hello World"))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::markdown())
+ .with(Width::wrap(20))
+ .with(MinWidth::new(20))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| Hello World |"
+ "|--|----|----|-----|"
+ "| | co | co | col |"
+ "| | lu | lu | umn |"
+ "| | mn | mn | 2 |"
+ "| | 0 | 1 | |"
+ "| | 0- | 0- | 0-2 |"
+ "| | 0 | 1 | |"
+ "| | 1- | 1- | 1-2 |"
+ "| | 0 | 1 | |"
+ "| | 2- | 2- | 2-2 |"
+ "| | 0 | 1 | |"
+ )
+ );
+ assert!(is_lines_equal(&table, 20));
+
+ let table = Matrix::new(3, 3)
+ .with(Panel::horizontal(0, "Hello World"))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::markdown())
+ .with(Width::wrap(14))
+ .with(MinWidth::new(14))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| Hello Worl |"
+ "| d |"
+ "|--|--|--|---|"
+ "| | | | c |"
+ "| | | | o |"
+ "| | | | l |"
+ "| | | | u |"
+ "| | | | m |"
+ "| | | | n |"
+ "| | | | |"
+ "| | | | 2 |"
+ "| | | | 0 |"
+ "| | | | - |"
+ "| | | | 2 |"
+ "| | | | 1 |"
+ "| | | | - |"
+ "| | | | 2 |"
+ "| | | | 2 |"
+ "| | | | - |"
+ "| | | | 2 |"
+ )
+ );
+ assert!(is_lines_equal(&table, 14));
+
+ let table = Matrix::new(3, 3)
+ .with(Panel::horizontal(0, "Hello World 123"))
+ .with(Modify::new(Segment::all()).with(Alignment::center()))
+ .with(Style::markdown())
+ .with(Width::wrap(14))
+ .with(MinWidth::new(14))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| Hello Worl |"
+ "| d 123 |"
+ "|--|--|--|---|"
+ "| | | | c |"
+ "| | | | o |"
+ "| | | | l |"
+ "| | | | u |"
+ "| | | | m |"
+ "| | | | n |"
+ "| | | | |"
+ "| | | | 2 |"
+ "| | | | 0 |"
+ "| | | | - |"
+ "| | | | 2 |"
+ "| | | | 1 |"
+ "| | | | - |"
+ "| | | | 2 |"
+ "| | | | 2 |"
+ "| | | | - |"
+ "| | | | 2 |"
+ )
+ );
+ assert!(is_lines_equal(&table, 14));
+}
+
+#[test]
+fn max_width_with_span() {
+ let mut table = Matrix::new(3, 3).insert((1, 1), "a long string").to_table();
+ table
+ .with(Style::psql())
+ .with(Modify::new((1, 1)).with(Span::column(2)))
+ .with(Modify::new((2, 2)).with(Span::column(2)));
+
+ table.with(Width::truncate(40));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ " N | column 0 | column 1 | column 2 "
+ "---+----------+----------+----------"
+ " 0 | a long string | 0-2 "
+ " 1 | 1-0 | 1-1 "
+ " 2 | 2-0 | 2-1 | 2-2 "
+ )
+ );
+ assert!(is_lines_equal(&table.to_string(), 36));
+
+ table.with(Width::truncate(20));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ " | col | col | col "
+ "--+-----+-----+-----"
+ " | a long st | 0-2 "
+ " | 1-0 | 1-1 "
+ " | 2-0 | 2-1 | 2-2 "
+ )
+ );
+ assert!(is_lines_equal(&table.to_string(), 20));
+
+ table.with(Width::truncate(10));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ " | | | "
+ "--+--+--+--"
+ " | a l | "
+ " | | 1-1 "
+ " | | | "
+ )
+ );
+ assert!(is_lines_equal(&table.to_string(), 11));
+}
+
+#[test]
+fn min_width_works_with_right_alignment() {
+ let json = r#"
+ {
+ "some": "random",
+ "json": [
+ { "1": "2" },
+ { "1": "2" },
+ { "1": "2" }
+ ]
+ }
+ "#;
+
+ let mut table = Matrix::iter([json]);
+ table
+ .with(Style::markdown())
+ .with(
+ Modify::new(Segment::all())
+ .with(Alignment::right())
+ .with(TrimStrategy::None),
+ )
+ .with(MinWidth::new(50));
+
+ assert_eq!(string_width_multiline(&table.to_string()), 50);
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ "| &str |"
+ "|------------------------------------------------|"
+ "| |"
+ "| { |"
+ "| \"some\": \"random\", |"
+ "| \"json\": [ |"
+ "| { \"1\": \"2\" }, |"
+ "| { \"1\": \"2\" }, |"
+ "| { \"1\": \"2\" } |"
+ "| ] |"
+ "| } |"
+ "| |"
+ )
+ );
+
+ table
+ .with(Modify::new(Segment::all()).with(TrimStrategy::Horizontal))
+ .with(MinWidth::new(50));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ r#"| &str |"#
+ r#"|------------------------------------------------|"#
+ r#"| |"#
+ r#"| { |"#
+ r#"| "some": "random", |"#
+ r#"| "json": [ |"#
+ r#"| { "1": "2" }, |"#
+ r#"| { "1": "2" }, |"#
+ r#"| { "1": "2" } |"#
+ r#"| ] |"#
+ r#"| } |"#
+ r#"| |"#
+ )
+ );
+ assert!(is_lines_equal(&table.to_string(), 50));
+
+ table
+ .with(Modify::new(Segment::all()).with(TrimStrategy::Both))
+ .with(MinWidth::new(50));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ r#"| &str |"#
+ r#"|------------------------------------------------|"#
+ r#"| { |"#
+ r#"| "some": "random", |"#
+ r#"| "json": [ |"#
+ r#"| { "1": "2" }, |"#
+ r#"| { "1": "2" }, |"#
+ r#"| { "1": "2" } |"#
+ r#"| ] |"#
+ r#"| } |"#
+ r#"| |"#
+ r#"| |"#
+ )
+ );
+ assert!(is_lines_equal(&table.to_string(), 50));
+
+ let mut table = Matrix::iter([json]);
+ table
+ .with(Style::markdown())
+ .with(
+ Modify::new(Segment::all())
+ .with(Alignment::center())
+ .with(TrimStrategy::None),
+ )
+ .with(MinWidth::new(50));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ "| &str |"
+ "|------------------------------------------------|"
+ "| |"
+ "| { |"
+ "| \"some\": \"random\", |"
+ "| \"json\": [ |"
+ "| { \"1\": \"2\" }, |"
+ "| { \"1\": \"2\" }, |"
+ "| { \"1\": \"2\" } |"
+ "| ] |"
+ "| } |"
+ "| |"
+ )
+ );
+ assert_eq!(string_width_multiline(&table.to_string()), 50);
+
+ table
+ .with(Modify::new(Segment::all()).with(TrimStrategy::Horizontal))
+ .with(MinWidth::new(50));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ r#"| &str |"#
+ r#"|------------------------------------------------|"#
+ r#"| |"#
+ r#"| { |"#
+ r#"| "some": "random", |"#
+ r#"| "json": [ |"#
+ r#"| { "1": "2" }, |"#
+ r#"| { "1": "2" }, |"#
+ r#"| { "1": "2" } |"#
+ r#"| ] |"#
+ r#"| } |"#
+ r#"| |"#
+ )
+ );
+ assert!(is_lines_equal(&table.to_string(), 50));
+
+ table
+ .with(Modify::new(Segment::all()).with(TrimStrategy::Both))
+ .with(MinWidth::new(50));
+
+ assert_eq!(
+ table.to_string(),
+ static_table!(
+ r#"| &str |"#
+ r#"|------------------------------------------------|"#
+ r#"| { |"#
+ r#"| "some": "random", |"#
+ r#"| "json": [ |"#
+ r#"| { "1": "2" }, |"#
+ r#"| { "1": "2" }, |"#
+ r#"| { "1": "2" } |"#
+ r#"| ] |"#
+ r#"| } |"#
+ r#"| |"#
+ r#"| |"#
+ )
+ );
+ assert!(is_lines_equal(&table.to_string(), 50));
+}
+
+#[test]
+fn min_width_with_span_1() {
+ let data = [
+ ["0", "1"],
+ ["a long string which will affect min width logic", ""],
+ ["2", "3"],
+ ];
+
+ let table = Matrix::iter(data)
+ .with(Style::markdown())
+ .with(Modify::new((1, 0)).with(Span::column(2)))
+ .with(MinWidth::new(100))
+ .to_string();
+
+ assert_eq!(string_width_multiline(&table), 100);
+ assert_eq!(
+ table,
+ static_table!(
+ "| 0 | 1 |"
+ "|------------------------------------------------------------------------|-------------------------|"
+ "| 0 |"
+ "| a long string which will affect min width logic | |"
+ "| 2 | 3 |"
+ )
+ );
+ assert!(is_lines_equal(&table, 100));
+}
+
+#[test]
+fn min_width_with_span_2() {
+ let data = [
+ ["0", "1"],
+ ["a long string which will affect min width logic", ""],
+ ["2", "3"],
+ ];
+
+ let table = Matrix::iter(data)
+ .with(Style::markdown())
+ .with(Modify::new((2, 0)).with(Span::column(2)))
+ .with(MinWidth::new(100))
+ .to_string();
+
+ assert_eq!(string_width_multiline(&table), 100);
+ assert_eq!(
+ table,
+ static_table!(
+ "| 0 | 1 |"
+ "|-------------------------------------------------|------------------------------------------------|"
+ "| 0 | 1 |"
+ "| a long string which will affect min width logic |"
+ "| 2 | 3 |"
+ )
+ );
+}
+
+#[test]
+fn justify_width_constant_test() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Justify::new(3))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | col | col | col |"
+ "|-----|-----|-----|-----|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+}
+
+#[test]
+fn justify_width_constant_different_sizes_test() {
+ let table = Matrix::new(3, 3)
+ .insert((1, 1), "Hello World")
+ .insert((3, 2), "multi\nline string\n")
+ .with(Style::markdown())
+ .with(Justify::new(3))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | col | col | col |"
+ "|-----|-----|-----|-----|"
+ "| 0 | Hel | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | mul | 2-2 |"
+ )
+ );
+}
+
+#[test]
+fn justify_width_constant_0_test() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Justify::new(0))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| | | | |"
+ "|--|--|--|--|"
+ "| | | | |"
+ "| | | | |"
+ "| | | | |"
+ )
+ );
+}
+
+#[test]
+fn justify_width_min_test() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Justify::min())
+ .to_string();
+
+ println!("{table}");
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | c | c | c |"
+ "|---|---|---|---|"
+ "| 0 | 0 | 0 | 0 |"
+ "| 1 | 1 | 1 | 1 |"
+ "| 2 | 2 | 2 | 2 |"
+ )
+ );
+}
+
+#[test]
+fn justify_width_max_test() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Justify::max())
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|----------|----------|----------|----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+}
+
+#[test]
+fn max_width_when_cell_has_tabs() {
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "\tHello\tWorld\t")
+ .with(TabSize::new(4))
+ .with(Style::markdown())
+ .with(Modify::new(Columns::new(..)).with(Width::truncate(1)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | c | c | c |"
+ "|---|---|---|---|"
+ "| 0 | 0 | 0 | 0 |"
+ "| 1 | | 1 | 1 |"
+ "| 2 | 2 | 2 | 2 |"
+ )
+ );
+}
+
+#[test]
+fn max_width_table_when_cell_has_tabs() {
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "\tHello\tWorld\t")
+ .with(TabSize::new(4))
+ .with(Style::markdown())
+ .with(Width::truncate(15))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| | co | | |"
+ "|--|----|--|--|"
+ "| | 0- | | |"
+ "| | | | |"
+ "| | 2- | | |"
+ )
+ );
+}
+
+// WE GOT [["", "column 0", "column 1 ", "column 2 "], ["", "0-0 ", "0-1 ", "0-2 "], ["", "Hello World With Big Line; Here w", "1-1", "1-2"], ["", "2-0 ", "Hello World With Big L", "2-2"]]
+// [2, 10, 11, 12]
+// 40 55 40
+
+// BEFORE ADJ [2, 10, 11, 12]
+
+// WE GOT [["", "column 0", "column 1", "column 2"], ["", "0-0", "0-1", "0-2"], ["", "Hello World With Big Line; Here w", "1-1", "1-2"], ["", "2-0", "Hello World With Big L", "2-2"]]
+// [2, 11, 12, 11]
+// 41 55 40
+
+// adj [2, 10, 10, 10]
+
+#[test]
+fn max_width_truncate_with_big_span() {
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line; Here we gooooooo")
+ .with(Style::markdown())
+ .with(Modify::new((2, 1)).with(Span::column(3)))
+ .with(Width::truncate(40))
+ .to_string();
+
+ assert_eq!(string_width_multiline(&table), 40);
+ assert_eq!(
+ table,
+ static_table!(
+ "| | column 0 | column 1 | column 2 |"
+ "|--|-----------|-----------|-----------|"
+ "| | 0-0 | 0-1 | 0-2 |"
+ "| | Hello World With Big Line; Here w |"
+ "| | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line; Here we gooooooo")
+ .insert((3, 2), "Hello World With Big Line; Here")
+ .with(Style::markdown())
+ .with(Modify::new((2, 1)).with(Span::column(3)))
+ .with(Modify::new((3, 2)).with(Span::column(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|---|-----------|----------------|----------------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | Hello World With Big Line; Here we gooooooo |"
+ "| 2 | 2-0 | Hello World With Big Line; Here |"
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line; Here we gooooooo")
+ .insert((3, 2), "Hello World With Big Line; Here")
+ .with(Style::markdown())
+ .with(Modify::new((2, 1)).with(Span::column(3)))
+ .with(Modify::new((3, 2)).with(Span::column(2)))
+ .with(Width::truncate(40))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| | colum | column 1 | column 2 |"
+ "|--|-------|-------------|-------------|"
+ "| | 0-0 | 0-1 | 0-2 |"
+ "| | Hello World With Big Line; Here w |"
+ "| | 2-0 | Hello World With Big Line |"
+ )
+ );
+ assert_eq!(string_width_multiline(&table), 40);
+
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line; Here we gooooooo")
+ .insert((3, 2), "Hello World With Big Line; Here")
+ .with(Style::markdown())
+ .with(Modify::new((2, 1)).with(Span::column(2)))
+ .with(Modify::new((3, 2)).with(Span::column(2)))
+ .with(Width::truncate(40))
+ .to_string();
+
+ assert_eq!(string_width_multiline(&table), 40);
+ assert_eq!(
+ table,
+ static_table!(
+ "| | column 0 | column 1 | c |"
+ "|--|---------------|---------------|---|"
+ "| | 0-0 | 0-1 | 0 |"
+ "| | Hello World With Big Line; He | 1 |"
+ "| | 2-0 | Hello World With |"
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line; Here w")
+ .insert((3, 2), "Hello World With Big L")
+ .with(Style::markdown())
+ .with(Modify::new((2, 1)).with(Span::column(3)))
+ .with(Modify::new((3, 2)).with(Span::column(2)))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|---|----------|------------|-----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | Hello World With Big Line; Here w |"
+ "| 2 | 2-0 | Hello World With Big L |"
+ )
+ );
+}
+
+#[test]
+fn max_width_truncate_priority_max() {
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Width::truncate(35).priority::<PriorityMax>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 35));
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | column | column | column |"
+ "|---|---------|---------|---------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | Hello W | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Width::truncate(20).priority::<PriorityMax>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 20));
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | co | co | co |"
+ "|---|----|----|----|"
+ "| 0 | 0- | 0- | 0- |"
+ "| 1 | He | 1- | 1- |"
+ "| 2 | 2- | 2- | 2- |"
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Width::truncate(0).priority::<PriorityMax>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 13));
+ assert_eq!(
+ table,
+ static_table!(
+ "| | | | |"
+ "|--|--|--|--|"
+ "| | | | |"
+ "| | | | |"
+ "| | | | |"
+ )
+ );
+}
+
+#[test]
+fn max_width_truncate_priority_max_with_span() {
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Modify::new((2, 1)).with(Span::column(2)))
+ .with(Width::truncate(15).priority::<PriorityMax>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 15));
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | c | | |"
+ "|---|---|--|--|"
+ "| 0 | 0 | | |"
+ "| 1 | Hell | |"
+ "| 2 | 2 | | |"
+ )
+ );
+}
+
+#[test]
+fn max_width_wrap_priority_max() {
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Width::wrap(35).priority::<PriorityMax>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 35));
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | column | column | column |"
+ "| | 0 | 1 | 2 |"
+ "|---|---------|---------|---------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | Hello W | 1-1 | 1-2 |"
+ "| | orld Wi | | |"
+ "| | th Big | | |"
+ "| | Line | | |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Width::wrap(20).priority::<PriorityMax>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 20));
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | co | co | co |"
+ "| | lu | lu | lu |"
+ "| | mn | mn | mn |"
+ "| | 0 | 1 | 2 |"
+ "|---|----|----|----|"
+ "| 0 | 0- | 0- | 0- |"
+ "| | 0 | 1 | 2 |"
+ "| 1 | He | 1- | 1- |"
+ "| | ll | 1 | 2 |"
+ "| | o | | |"
+ "| | Wo | | |"
+ "| | rl | | |"
+ "| | d | | |"
+ "| | Wi | | |"
+ "| | th | | |"
+ "| | B | | |"
+ "| | ig | | |"
+ "| | L | | |"
+ "| | in | | |"
+ "| | e | | |"
+ "| 2 | 2- | 2- | 2- |"
+ "| | 0 | 1 | 2 |"
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Width::wrap(0).priority::<PriorityMax>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 13));
+ assert_eq!(
+ table,
+ static_table!(
+ "| | | | |"
+ "|--|--|--|--|"
+ "| | | | |"
+ "| | | | |"
+ "| | | | |"
+ )
+ );
+}
+
+#[test]
+fn max_width_wrap_priority_max_with_span() {
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Modify::new((2, 1)).with(Span::column(2)))
+ .with(Width::wrap(15).priority::<PriorityMax>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 15));
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | c | | |"
+ "| | o | | |"
+ "| | l | | |"
+ "| | u | | |"
+ "| | m | | |"
+ "| | n | | |"
+ "| | | | |"
+ "| | 0 | | |"
+ "|---|---|--|--|"
+ "| 0 | 0 | | |"
+ "| | - | | |"
+ "| | 0 | | |"
+ "| 1 | Hell | |"
+ "| | o Wo | |"
+ "| | rld | |"
+ "| | With | |"
+ "| | Big | |"
+ "| | Lin | |"
+ "| | e | |"
+ "| 2 | 2 | | |"
+ "| | - | | |"
+ "| | 0 | | |"
+ )
+ );
+}
+
+#[test]
+fn max_width_truncate_priority_min() {
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Width::truncate(35).priority::<PriorityMin>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 35));
+ assert_eq!(
+ table,
+ static_table!(
+ "| | column 0 | | |"
+ "|--|------------------------|--|--|"
+ "| | 0-0 | | |"
+ "| | Hello World With Big L | | |"
+ "| | 2-0 | | |"
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Width::truncate(20).priority::<PriorityMin>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 20));
+ assert_eq!(
+ table,
+ static_table!(
+ "| | column | | |"
+ "|--|---------|--|--|"
+ "| | 0-0 | | |"
+ "| | Hello W | | |"
+ "| | 2-0 | | |"
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Width::truncate(0).priority::<PriorityMin>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 13));
+ assert_eq!(
+ table,
+ static_table!(
+ "| | | | |"
+ "|--|--|--|--|"
+ "| | | | |"
+ "| | | | |"
+ "| | | | |"
+ )
+ );
+}
+
+#[test]
+fn max_width_truncate_priority_min_with_span() {
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Modify::new((2, 1)).with(Span::column(2)))
+ .with(Width::truncate(15).priority::<PriorityMin>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 15));
+ assert_eq!(
+ table,
+ static_table!(
+ "| | | co | |"
+ "|--|--|----|--|"
+ "| | | 0- | |"
+ "| | Hello | |"
+ "| | | 2- | |"
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Modify::new((2, 1)).with(Span::column(2)))
+ .with(Width::truncate(17).priority::<PriorityMin>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 17));
+ assert_eq!(
+ table,
+ static_table!(
+ "| | | colu | |"
+ "|--|--|------|--|"
+ "| | | 0-1 | |"
+ "| | Hello W | |"
+ "| | | 2-1 | |"
+ )
+ );
+}
+
+#[test]
+fn max_width_wrap_priority_min() {
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Width::wrap(35).priority::<PriorityMin>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 35));
+ assert_eq!(
+ table,
+ static_table!(
+ "| | column 0 | | |"
+ "|--|------------------------|--|--|"
+ "| | 0-0 | | |"
+ "| | Hello World With Big L | | |"
+ "| | ine | | |"
+ "| | 2-0 | | |"
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Width::wrap(20).priority::<PriorityMin>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 20));
+ assert_eq!(
+ table,
+ static_table!(
+ "| | column | | |"
+ "| | 0 | | |"
+ "|--|---------|--|--|"
+ "| | 0-0 | | |"
+ "| | Hello W | | |"
+ "| | orld Wi | | |"
+ "| | th Big | | |"
+ "| | Line | | |"
+ "| | 2-0 | | |"
+ )
+ );
+
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Width::wrap(0).priority::<PriorityMin>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 13));
+ assert_eq!(
+ table,
+ static_table!(
+ "| | | | |"
+ "|--|--|--|--|"
+ "| | | | |"
+ "| | | | |"
+ "| | | | |"
+ )
+ );
+}
+
+#[test]
+fn max_width_wrap_priority_min_with_span() {
+ let table = Matrix::new(3, 3)
+ .insert((2, 1), "Hello World With Big Line")
+ .with(Style::markdown())
+ .with(Modify::new((2, 1)).with(Span::column(2)))
+ .with(Width::wrap(15).priority::<PriorityMin>())
+ .to_string();
+
+ assert!(is_lines_equal(&table, 15));
+ assert_eq!(
+ table,
+ static_table!(
+ "| | | co | |"
+ "| | | lu | |"
+ "| | | mn | |"
+ "| | | 1 | |"
+ "|--|--|----|--|"
+ "| | | 0- | |"
+ "| | | 1 | |"
+ "| | Hello | |"
+ "| | Worl | |"
+ "| | d Wit | |"
+ "| | h Big | |"
+ "| | Line | |"
+ "| | | 2- | |"
+ "| | | 1 | |"
+ )
+ );
+}
+
+#[test]
+fn min_width_priority_max() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(MinWidth::new(60).priority::<PriorityMax>())
+ .to_string();
+
+ assert_eq!(string_width_multiline(&table), 60);
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|---|----------|----------|--------------------------------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ ),
+ );
+}
+
+#[test]
+fn min_width_priority_min() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(MinWidth::new(60).priority::<PriorityMin>())
+ .to_string();
+
+ assert_eq!(string_width_multiline(&table), 60);
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|--------------|--------------|--------------|-------------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ ),
+ );
+}
+
+#[test]
+fn max_width_tab_0() {
+ let table =
+ Matrix::iter(["\t\tTigre Ecuador\tOMYA Andina\t3824909999\tCalcium carbonate\tColombia\t"])
+ .with(TabSize::new(4))
+ .with(Style::markdown())
+ .with(Width::wrap(60))
+ .to_string();
+
+ assert!(is_lines_equal(&table, 60));
+ assert_eq!(
+ table,
+ static_table!(
+ "| &str |"
+ "|----------------------------------------------------------|"
+ "| Tigre Ecuador OMYA Andina 3824909999 Ca |"
+ "| lcium carbonate Colombia |"
+ )
+ );
+}
+
+#[test]
+fn min_width_is_not_used_after_padding() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(MinWidth::new(60))
+ .with(Modify::new((0, 0)).with(Padding::new(2, 2, 0, 0)))
+ .to_string();
+
+ assert_eq!(string_width_multiline(&table), 40);
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|-----|----------|----------|----------|"
+ "| 0 | 0-0 | 0-1 | 0-2 |"
+ "| 1 | 1-0 | 1-1 | 1-2 |"
+ "| 2 | 2-0 | 2-1 | 2-2 |"
+ ),
+ );
+}
+
+#[test]
+fn min_width_is_used_after_margin() {
+ let table = Matrix::new(3, 3)
+ .with(Style::markdown())
+ .with(Margin::new(1, 1, 1, 1))
+ .with(Width::increase(60))
+ .to_string();
+
+ assert_eq!(string_width_multiline(&table), 60);
+ assert_eq!(
+ table,
+ static_table!(
+ " "
+ " | N | column 0 | column 1 | column 2 | "
+ " |--------|---------------|---------------|---------------| "
+ " | 0 | 0-0 | 0-1 | 0-2 | "
+ " | 1 | 1-0 | 1-1 | 1-2 | "
+ " | 2 | 2-0 | 2-1 | 2-2 | "
+ " "
+ ),
+ );
+}
+
+#[test]
+fn wrap_keeping_words_0() {
+ let data = vec![["Hello world"]];
+ let table = tabled::Table::new(data)
+ .with(Width::wrap(8).keep_words())
+ .to_string();
+
+ assert_eq!(
+ tabled::grid::util::string::string_width_multiline(&table),
+ 8
+ );
+
+ assert_eq!(
+ table,
+ static_table!(
+ "+------+"
+ "| 0 |"
+ "+------+"
+ "| Hell |"
+ "| o wo |"
+ "| rld |"
+ "+------+"
+ )
+ );
+}
+
+#[test]
+fn cell_truncate_multiline() {
+ let table = Matrix::new(3, 3)
+ .insert((1, 1), "H\nel\nlo World")
+ .insert((3, 2), "multi\nline string\n")
+ .with(Style::markdown())
+ .with(
+ Modify::new(Columns::new(1..2).not(Rows::single(0)))
+ .with(Width::truncate(1).multiline()),
+ )
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|---|----------|-------------|----------|"
+ "| 0 | H | 0-1 | 0-2 |"
+ "| | e | | |"
+ "| | l | | |"
+ "| 1 | 1 | 1-1 | 1-2 |"
+ "| 2 | 2 | multi | 2-2 |"
+ "| | | line string | |"
+ "| | | | |"
+ )
+ );
+}
+
+#[test]
+fn cell_truncate_multiline_with_suffix() {
+ let table = Matrix::new(3, 3)
+ .insert((1, 1), "H\nel\nlo World")
+ .insert((3, 2), "multi\nline string\n")
+ .with(Style::markdown())
+ .with(
+ Modify::new(Columns::new(1..2).not(Rows::single(0)))
+ .with(Width::truncate(1).multiline().suffix(".")),
+ )
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| N | column 0 | column 1 | column 2 |"
+ "|---|----------|-------------|----------|"
+ "| 0 | . | 0-1 | 0-2 |"
+ "| | . | | |"
+ "| | . | | |"
+ "| 1 | . | 1-1 | 1-2 |"
+ "| 2 | . | multi | 2-2 |"
+ "| | | line string | |"
+ "| | | | |"
+ )
+ );
+}
+
+#[test]
+fn table_truncate_multiline() {
+ let table = Matrix::new(3, 3)
+ .insert((1, 1), "H\nel\nlo World")
+ .insert((3, 2), "multi\nline string\n")
+ .with(Style::markdown())
+ .with(Width::truncate(20).multiline())
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| | c | colu | co |"
+ "|--|---|------|----|"
+ "| | H | 0-1 | 0- |"
+ "| | e | | |"
+ "| | l | | |"
+ "| | 1 | 1-1 | 1- |"
+ "| | 2 | mult | 2- |"
+ "| | | line | |"
+ "| | | | |"
+ )
+ );
+}
+
+#[test]
+fn table_truncate_multiline_with_suffix() {
+ let table = Matrix::new(3, 3)
+ .insert((1, 1), "H\nel\nlo World")
+ .insert((3, 2), "multi\nline string\n")
+ .with(Style::markdown())
+ .with(Width::truncate(20).suffix(".").multiline())
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| | . | col. | c. |"
+ "|--|---|------|----|"
+ "| | . | 0-1 | 0. |"
+ "| | . | | |"
+ "| | . | | |"
+ "| | . | 1-1 | 1. |"
+ "| | . | mul. | 2. |"
+ "| | | lin. | |"
+ "| | | . | |"
+ )
+ );
+}
+
+#[cfg(feature = "derive")]
+mod derived {
+ use super::*;
+
+ use tabled::Tabled;
+
+ #[test]
+ fn wrapping_as_total_multiline() {
+ #[derive(Tabled)]
+ struct D<'a>(
+ #[tabled(rename = "version")] &'a str,
+ #[tabled(rename = "published_date")] &'a str,
+ #[tabled(rename = "is_active")] &'a str,
+ #[tabled(rename = "major_feature")] &'a str,
+ );
+
+ let data = vec![
+ D("0.2.1", "2021-06-23", "true", "#[header(inline)] attribute"),
+ D("0.2.0", "2021-06-19", "false", "API changes"),
+ D("0.1.4", "2021-06-07", "false", "display_with attribute"),
+ ];
+
+ let table = Matrix::iter(&data)
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Width::wrap(57))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| ver | published_d | is_act | major_feature |"
+ "| sio | ate | ive | |"
+ "| n | | | |"
+ "|-----|-------------|--------|--------------------------|"
+ "| 0.2 | 2021-06-23 | true | #[header(inline)] attrib |"
+ "| .1 | | | ute |"
+ "| 0.2 | 2021-06-19 | false | API changes |"
+ "| .0 | | | |"
+ "| 0.1 | 2021-06-07 | false | display_with attribute |"
+ "| .4 | | | |"
+ )
+ );
+ assert!(is_lines_equal(&table, 57));
+
+ let table = Matrix::iter(&data)
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Width::wrap(57).keep_words())
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| ver | published_d | is_act | major_feature |"
+ "| sio | ate | ive | |"
+ "| n | | | |"
+ "|-----|-------------|--------|--------------------------|"
+ "| 0.2 | 2021-06-23 | true | #[header(inline)] |"
+ "| .1 | | | attribute |"
+ "| 0.2 | 2021-06-19 | false | API changes |"
+ "| .0 | | | |"
+ "| 0.1 | 2021-06-07 | false | display_with attribute |"
+ "| .4 | | | |"
+ )
+ );
+ assert!(is_lines_equal(&table, 57));
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn wrapping_as_total_multiline_color() {
+ #[derive(Tabled)]
+ struct D(
+ #[tabled(rename = "version")] String,
+ #[tabled(rename = "published_date")] String,
+ #[tabled(rename = "is_active")] String,
+ #[tabled(rename = "major_feature")] String,
+ );
+
+ let data = vec![
+ D(
+ "0.2.1".red().to_string(),
+ "2021-06-23".red().on_truecolor(8, 10, 30).to_string(),
+ "true".to_string(),
+ "#[header(inline)] attribute"
+ .blue()
+ .on_color(AnsiColors::Green)
+ .to_string(),
+ ),
+ D(
+ "0.2.0".red().to_string(),
+ "2021-06-19".green().on_truecolor(8, 100, 30).to_string(),
+ "false".to_string(),
+ "API changes".yellow().to_string(),
+ ),
+ D(
+ "0.1.4".white().to_string(),
+ "2021-06-07".red().on_truecolor(8, 10, 30).to_string(),
+ "false".to_string(),
+ "display_with attribute"
+ .red()
+ .on_color(AnsiColors::Black)
+ .to_string(),
+ ),
+ ];
+
+ let table = Matrix::iter(&data)
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Width::wrap(57))
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| ver | published_d | is_act | major_feature |"
+ "| sio | ate | ive | |"
+ "| n | | | |"
+ "|-----|-------------|--------|--------------------------|"
+ "| \u{1b}[31m0.2\u{1b}[39m | \u{1b}[48;2;8;10;30m\u{1b}[31m2021-06-23\u{1b}[39m\u{1b}[49m | true | \u{1b}[34m\u{1b}[42m#[header(inline)] attrib\u{1b}[39m\u{1b}[49m |"
+ "| \u{1b}[31m.1\u{1b}[39m | | | \u{1b}[34m\u{1b}[42mute\u{1b}[39m\u{1b}[49m |"
+ "| \u{1b}[31m0.2\u{1b}[39m | \u{1b}[48;2;8;100;30m\u{1b}[32m2021-06-19\u{1b}[39m\u{1b}[49m | false | \u{1b}[33mAPI changes\u{1b}[39m |"
+ "| \u{1b}[31m.0\u{1b}[39m | | | |"
+ "| \u{1b}[37m0.1\u{1b}[39m | \u{1b}[48;2;8;10;30m\u{1b}[31m2021-06-07\u{1b}[39m\u{1b}[49m | false | \u{1b}[31;40mdisplay_with attribute\u{1b}[0m |"
+ "| \u{1b}[37m.4\u{1b}[39m | | | |"
+ )
+ );
+ assert_eq!(string_width_multiline(&table), 57);
+
+ let table = Matrix::iter(&data)
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Width::wrap(57).keep_words())
+ .to_string();
+
+ assert_eq!(
+ table,
+ static_table!(
+ "| ver | published_d | is_act | major_feature |"
+ "| sio | ate | ive | |"
+ "| n | | | |"
+ "|-----|-------------|--------|--------------------------|"
+ "| \u{1b}[31m0.2\u{1b}[39m | \u{1b}[48;2;8;10;30m\u{1b}[31m2021-06-23\u{1b}[39m\u{1b}[49m | true | \u{1b}[34m\u{1b}[42m#[header(inline)] \u{1b}[39m\u{1b}[49m |"
+ "| \u{1b}[31m.1\u{1b}[39m | | | \u{1b}[34m\u{1b}[42mattribute\u{1b}[39m\u{1b}[49m |"
+ "| \u{1b}[31m0.2\u{1b}[39m | \u{1b}[48;2;8;100;30m\u{1b}[32m2021-06-19\u{1b}[39m\u{1b}[49m | false | \u{1b}[33mAPI changes\u{1b}[39m |"
+ "| \u{1b}[31m.0\u{1b}[39m | | | |"
+ "| \u{1b}[37m0.1\u{1b}[39m | \u{1b}[48;2;8;10;30m\u{1b}[31m2021-06-07\u{1b}[39m\u{1b}[49m | false | \u{1b}[31;40mdisplay_with attribute\u{1b}[0m |"
+ "| \u{1b}[37m.4\u{1b}[39m | | | |"
+ )
+ );
+ assert_eq!(string_width_multiline(&table), 57);
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn truncating_as_total_multiline_color() {
+ #[derive(Tabled)]
+ struct D(
+ #[tabled(rename = "version")] String,
+ #[tabled(rename = "published_date")] String,
+ #[tabled(rename = "is_active")] String,
+ #[tabled(rename = "major_feature")] String,
+ );
+
+ let data = vec![
+ D(
+ "0.2.1".red().to_string(),
+ "2021-06-23".red().on_truecolor(8, 10, 30).to_string(),
+ "true".to_string(),
+ "#[header(inline)] attribute"
+ .blue()
+ .on_color(AnsiColors::Green)
+ .to_string(),
+ ),
+ D(
+ "0.2.0".red().to_string(),
+ "2021-06-19".green().on_truecolor(8, 100, 30).to_string(),
+ "false".to_string(),
+ "API changes".yellow().to_string(),
+ ),
+ D(
+ "0.1.4".white().to_string(),
+ "2021-06-07".red().on_truecolor(8, 10, 30).to_string(),
+ "false".to_string(),
+ "display_with attribute"
+ .red()
+ .on_color(AnsiColors::Black)
+ .to_string(),
+ ),
+ ];
+
+ let table = Matrix::iter(data)
+ .with(Style::markdown())
+ .with(Modify::new(Segment::all()).with(Alignment::left()))
+ .with(Width::truncate(57))
+ .to_string();
+
+ assert_eq!(
+ ansi_str::AnsiStr::ansi_strip(&table),
+ static_table!(
+ "| ver | published_d | is_act | major_feature |"
+ "|-----|-------------|--------|--------------------------|"
+ "| 0.2 | 2021-06-23 | true | #[header(inline)] attrib |"
+ "| 0.2 | 2021-06-19 | false | API changes |"
+ "| 0.1 | 2021-06-07 | false | display_with attribute |"
+ )
+ );
+
+ assert_eq!(
+ table,
+ "| ver | published_d | is_act | major_feature |\n|-----|-------------|--------|--------------------------|\n| \u{1b}[31m0.2\u{1b}[39m | \u{1b}[48;2;8;10;30m\u{1b}[31m2021-06-23\u{1b}[39m\u{1b}[49m | true | \u{1b}[34;42m#[header(inline)] attrib\u{1b}[39m\u{1b}[49m |\n| \u{1b}[31m0.2\u{1b}[39m | \u{1b}[48;2;8;100;30m\u{1b}[32m2021-06-19\u{1b}[39m\u{1b}[49m | false | \u{1b}[33mAPI changes\u{1b}[39m |\n| \u{1b}[37m0.1\u{1b}[39m | \u{1b}[48;2;8;10;30m\u{1b}[31m2021-06-07\u{1b}[39m\u{1b}[49m | false | \u{1b}[31;40mdisplay_with attribute\u{1b}[0m |"
+ );
+ assert_eq!(string_width_multiline(&table), 57);
+ }
+
+ #[cfg(feature = "color")]
+ fn format_osc8_hyperlink(url: &str, text: &str) -> String {
+ format!(
+ "{osc}8;;{url}{st}{text}{osc}8;;{st}",
+ url = url,
+ text = text,
+ osc = "\x1b]",
+ st = "\x1b\\"
+ )
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn hyperlinks() {
+ #[derive(Tabled)]
+ struct Distribution {
+ name: String,
+ is_hyperlink: bool,
+ }
+
+ let table = |text: &str| {
+ let data = [Distribution {
+ name: text.to_owned(),
+ is_hyperlink: true,
+ }];
+ tabled::Table::new(data)
+ .with(
+ Modify::new(Segment::all())
+ .with(Width::wrap(5).keep_words())
+ .with(Alignment::left()),
+ )
+ .to_string()
+ };
+
+ let text = format_osc8_hyperlink("https://www.debian.org/", "Debian");
+ assert_eq!(
+ table(&text),
+ "+-------+-------+\n\
+ | name | is_hy |\n\
+ | | perli |\n\
+ | | nk |\n\
+ +-------+-------+\n\
+ | \u{1b}]8;;https://www.debian.org/\u{1b}\\Debia\u{1b}]8;;\u{1b}\\ | true |\n\
+ | \u{1b}]8;;https://www.debian.org/\u{1b}\\n\u{1b}]8;;\u{1b}\\ | |\n\
+ +-------+-------+"
+ );
+
+ // if there's more text than a link it will be ignored
+ let text = format!(
+ "{} :link",
+ format_osc8_hyperlink("https://www.debian.org/", "Debian"),
+ );
+ assert_eq!(
+ table(&text),
+ "+-------+-------+\n\
+ | name | is_hy |\n\
+ | | perli |\n\
+ | | nk |\n\
+ +-------+-------+\n\
+ | Debia | true |\n\
+ | n | |\n\
+ | :link | |\n\
+ +-------+-------+"
+ );
+
+ let text = format!(
+ "asd {} 2 links in a string {}",
+ format_osc8_hyperlink("https://www.debian.org/", "Debian"),
+ format_osc8_hyperlink("https://www.wikipedia.org/", "Debian"),
+ );
+ assert_eq!(
+ table(&text),
+ static_table!(
+ "+-------+-------+"
+ "| name | is_hy |"
+ "| | perli |"
+ "| | nk |"
+ "+-------+-------+"
+ "| asd D | true |"
+ "| ebian | |"
+ "| 2 | |"
+ "| links | |"
+ "| in a | |"
+ "| stri | |"
+ "| ng De | |"
+ "| bian | |"
+ "+-------+-------+"
+ )
+ );
+ }
+
+ #[cfg(feature = "color")]
+ #[test]
+ fn hyperlinks_with_color() {
+ use owo_colors::OwoColorize;
+
+ #[derive(Tabled)]
+ struct Distribution {
+ name: String,
+ is_hyperlink: bool,
+ }
+
+ let table = |text: &str| {
+ let data = [Distribution {
+ name: text.to_owned(),
+ is_hyperlink: true,
+ }];
+ tabled::Table::new(data)
+ .with(
+ Modify::new(Segment::all())
+ .with(Width::wrap(6).keep_words())
+ .with(Alignment::left()),
+ )
+ .to_string()
+ };
+
+ let text = format_osc8_hyperlink(
+ "https://www.debian.org/",
+ "Debian".red().to_string().as_str(),
+ );
+ assert_eq!(
+ table(&text),
+ static_table!(
+ "+--------+--------+"
+ "| name | is_hyp |"
+ "| | erlink |"
+ "+--------+--------+"
+ "| \u{1b}]8;;https://www.debian.org/\u{1b}\\\u{1b}[31mDebian\u{1b}[39m\u{1b}]8;;\u{1b}\\ | true |"
+ "+--------+--------+"
+ )
+ );
+
+ // if there's more text than a link it will be ignored
+ let text = format!(
+ "{} :link",
+ format_osc8_hyperlink("https://www.debian.org/", "Debian"),
+ );
+ assert_eq!(
+ table(&text),
+ static_table!(
+ "+--------+--------+"
+ "| name | is_hyp |"
+ "| | erlink |"
+ "+--------+--------+"
+ "| Debian | true |"
+ "| :link | |"
+ "+--------+--------+"
+ )
+ );
+
+ let text = format!(
+ "asd {} 2 links in a string {}",
+ format_osc8_hyperlink("https://www.debian.org/", "Debian"),
+ format_osc8_hyperlink("https://www.wikipedia.org/", "Debian"),
+ );
+ assert_eq!(
+ table(&text),
+ static_table!(
+ "+--------+--------+"
+ "| name | is_hyp |"
+ "| | erlink |"
+ "+--------+--------+"
+ "| asd | true |"
+ "| Debian | |"
+ "| 2 | |"
+ "| links | |"
+ "| in a | |"
+ "| string | |"
+ "| | |"
+ "| Debian | |"
+ "+--------+--------+"
+ )
+ );
+ }
+}