feat: add tabulate but no test.
This commit is contained in:
@ -26,6 +26,7 @@ PRIVATE
|
|||||||
yycc/carton/pycodec.cpp
|
yycc/carton/pycodec.cpp
|
||||||
yycc/carton/termcolor.cpp
|
yycc/carton/termcolor.cpp
|
||||||
yycc/carton/wcwidth.cpp
|
yycc/carton/wcwidth.cpp
|
||||||
|
yycc/carton/tabulate.cpp
|
||||||
)
|
)
|
||||||
target_sources(YYCCommonplace
|
target_sources(YYCCommonplace
|
||||||
PUBLIC
|
PUBLIC
|
||||||
@ -71,6 +72,7 @@ FILES
|
|||||||
yycc/carton/pycodec.hpp
|
yycc/carton/pycodec.hpp
|
||||||
yycc/carton/termcolor.hpp
|
yycc/carton/termcolor.hpp
|
||||||
yycc/carton/wcwidth.hpp
|
yycc/carton/wcwidth.hpp
|
||||||
|
yycc/carton/tabulate.hpp
|
||||||
)
|
)
|
||||||
# Setup header infomations
|
# Setup header infomations
|
||||||
target_include_directories(YYCCommonplace
|
target_include_directories(YYCCommonplace
|
||||||
|
192
src/yycc/carton/tabulate.cpp
Normal file
192
src/yycc/carton/tabulate.cpp
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
#include "tabulate.hpp"
|
||||||
|
#include "wcwidth.hpp"
|
||||||
|
#include "../num/safe_op.hpp"
|
||||||
|
#include "../string/reinterpret.hpp"
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
|
#define WCWIDTH ::yycc::carton::wcwidth
|
||||||
|
#define REINTERPRET ::yycc::string::reinterpret
|
||||||
|
#define SAFEOP ::yycc::num::safe_op
|
||||||
|
|
||||||
|
namespace yycc::carton::tabulate {
|
||||||
|
|
||||||
|
#pragma region Tabulate Width
|
||||||
|
|
||||||
|
TabulateWidth::TabulateWidth(size_t n) : widths(n, 0u) {}
|
||||||
|
|
||||||
|
TabulateWidth::~TabulateWidth() {}
|
||||||
|
|
||||||
|
size_t TabulateWidth::get_column_count() const {
|
||||||
|
return widths.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t TabulateWidth::get_column_width(size_t column_index) const {
|
||||||
|
return widths.at(column_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabulateWidth::update_column_width(size_t column_index, size_t new_size) {
|
||||||
|
auto& width = widths.at(column_index);
|
||||||
|
width = std::max(width, new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabulateWidth::clear() {
|
||||||
|
std::fill(widths.begin(), widths.end(), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region Tabulate Cell
|
||||||
|
|
||||||
|
TabulateCell::TabulateCell(const std::u8string_view& text) : text(text), text_width(WCWIDTH::wcswidth(text).value_or(0u)) {}
|
||||||
|
|
||||||
|
TabulateCell::~TabulateCell() {}
|
||||||
|
|
||||||
|
const std::u8string& TabulateCell::get_text() const {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t TabulateCell::get_text_width() const {
|
||||||
|
return text_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region Tabulate
|
||||||
|
|
||||||
|
/// @brief Default separator literal for Tabulate.
|
||||||
|
static constexpr char8_t SEPARATOR_BAR[] = u8"---";
|
||||||
|
/// @brief A stupid size_t ZERO literal to trigger template type deduce for std::views::iota.
|
||||||
|
static constexpr size_t ZERO = 0;
|
||||||
|
|
||||||
|
Tabulate::Tabulate(size_t n) :
|
||||||
|
n(n), header_display(true), bar_display(true), prefix_string(), rows_widths(n), header_widths(n), header(n, TabulateCell(u8"")),
|
||||||
|
bar(SEPARATOR_BAR), rows() {}
|
||||||
|
|
||||||
|
Tabulate::~Tabulate() {}
|
||||||
|
|
||||||
|
void Tabulate::print(std::ostream& dst) const {
|
||||||
|
// Get column count
|
||||||
|
auto n = this->get_column_count();
|
||||||
|
|
||||||
|
// Create width recorder for final printing
|
||||||
|
// according to whether we show table header and separator bar.
|
||||||
|
auto widths = this->rows_widths;
|
||||||
|
if (this->header_display) {
|
||||||
|
for (auto index : std::views::iota(ZERO, n)) {
|
||||||
|
widths.update_column_width(index, this->header_widths.get_column_width(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this->bar_display) {
|
||||||
|
auto bar_width = this->bar.get_text_width();
|
||||||
|
for (auto index : std::views::iota(ZERO, n)) {
|
||||||
|
widths.update_column_width(index, bar_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the maximum space char count to build a string filled with spaces,
|
||||||
|
// for the convenient about following printing.
|
||||||
|
size_t max_space = 1;
|
||||||
|
for (auto index : std::views::iota(ZERO, n)) {
|
||||||
|
max_space = std::max(max_space, widths.get_column_width(index));
|
||||||
|
}
|
||||||
|
std::u8string spaces(max_space, u8' ');
|
||||||
|
std::u8string_view spaces_view(spaces);
|
||||||
|
|
||||||
|
// Print table
|
||||||
|
// Define a convenient macro
|
||||||
|
#define CVT(data) REINTERPRET::as_ordinary_view(data)
|
||||||
|
// Show header
|
||||||
|
if (this->header_display) {
|
||||||
|
dst << CVT(this->prefix_string);
|
||||||
|
for (const auto [index, item] : std::views::enumerate(header)) {
|
||||||
|
auto diff = SAFEOP::saturating_sub(widths.get_column_width(index), item.get_text_width());
|
||||||
|
dst << CVT(item.get_text()) << CVT(spaces_view.substr(0, diff)) << " ";
|
||||||
|
}
|
||||||
|
dst << std::endl;
|
||||||
|
}
|
||||||
|
// Show bar
|
||||||
|
if (this->bar_display) {
|
||||||
|
dst << CVT(this->prefix_string);
|
||||||
|
auto bar_width = this->bar.get_text_width();
|
||||||
|
for (auto index : std::views::iota(ZERO, n)) {
|
||||||
|
auto diff = SAFEOP::saturating_sub(widths.get_column_width(index), bar_width);
|
||||||
|
dst << CVT(this->bar.get_text()) << CVT(spaces_view.substr(0, diff)) << " ";
|
||||||
|
}
|
||||||
|
dst << std::endl;
|
||||||
|
}
|
||||||
|
// Show data
|
||||||
|
for (const auto& row : this->rows) {
|
||||||
|
dst << CVT(this->prefix_string);
|
||||||
|
for (const auto [index, item] : std::views::enumerate(row)) {
|
||||||
|
auto diff = SAFEOP::saturating_sub(widths.get_column_width(index), item.get_text_width());
|
||||||
|
dst << CVT(item.get_text()) << CVT(spaces_view.substr(0, diff)) << " ";
|
||||||
|
}
|
||||||
|
dst << std::endl;
|
||||||
|
}
|
||||||
|
// Undef macro
|
||||||
|
#undef CVT
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Tabulate::get_column_count() const {
|
||||||
|
return this->n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tabulate::show_header(bool show_header) {
|
||||||
|
this->header_display = show_header;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tabulate::show_bar(bool show_bar) {
|
||||||
|
this->bar_display = show_bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tabulate::set_prefix(const std::u8string_view& prefix) {
|
||||||
|
this->prefix_string = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tabulate::set_header(std::initializer_list<std::u8string_view> hdr) {
|
||||||
|
// Check data size.
|
||||||
|
if (hdr.size() != get_column_count()) {
|
||||||
|
throw std::invalid_argument("the size of given header is not equal to column count");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change header data and update header width recorder.
|
||||||
|
header.clear();
|
||||||
|
header_widths.clear();
|
||||||
|
for (const auto [index, item] : std::views::enumerate(hdr)) {
|
||||||
|
auto cell = header.emplace_back(item);
|
||||||
|
header_widths.update_column_width(index, cell.get_text_width());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tabulate::set_bar(const std::u8string_view& bar) {
|
||||||
|
this->bar = bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tabulate::add_row(std::initializer_list<std::u8string_view> row) {
|
||||||
|
// Check data size.
|
||||||
|
if (row.size() != get_column_count()) {
|
||||||
|
throw std::invalid_argument("the size of given row is not equal to column count");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare inserted row, and update data width recorder.
|
||||||
|
Row inserted_row;
|
||||||
|
inserted_row.reserve(row.size());
|
||||||
|
for (const auto [index, item] : std::views::enumerate(row)) {
|
||||||
|
auto cell = inserted_row.emplace_back(item);
|
||||||
|
rows_widths.update_column_width(index, cell.get_text_width());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert row
|
||||||
|
rows.emplace_back(std::move(inserted_row));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tabulate::clear() {
|
||||||
|
// Clear data and data width recorder.
|
||||||
|
rows.clear();
|
||||||
|
rows_widths.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
} // namespace yycc::carton::tabulate
|
198
src/yycc/carton/tabulate.hpp
Normal file
198
src/yycc/carton/tabulate.hpp
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../macro/class_copy_move.hpp"
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace yycc::carton::tabulate {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @brief Assistant class recording column width.
|
||||||
|
*/
|
||||||
|
class TabulateWidth {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Create width recorder with given column count.
|
||||||
|
* @param[in] n Column count.
|
||||||
|
*/
|
||||||
|
TabulateWidth(size_t n);
|
||||||
|
~TabulateWidth();
|
||||||
|
YYCC_DEFAULT_COPY_MOVE(TabulateWidth)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Get column count.
|
||||||
|
* @return Column count.
|
||||||
|
*/
|
||||||
|
size_t get_column_count() const;
|
||||||
|
/**
|
||||||
|
* @brief Get column width of given index.
|
||||||
|
* @param[in] column_index Column index for fetching width.
|
||||||
|
* @return Column width of given index.
|
||||||
|
*/
|
||||||
|
size_t get_column_width(size_t column_index) const;
|
||||||
|
/**
|
||||||
|
* @brief Update column width of given index with given value.
|
||||||
|
* @details The width of column will be updated to the maximum between given value and old value.
|
||||||
|
* @param[in] column_index Column index for updating width.
|
||||||
|
* @param[in] new_size New width value.
|
||||||
|
*/
|
||||||
|
void update_column_width(size_t column_index, size_t new_size);
|
||||||
|
/**
|
||||||
|
* @brief Clear all width data
|
||||||
|
* @details All width data will be reset to zero.
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<size_t> widths;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @brief Assistant class holding table cell data.
|
||||||
|
* @details
|
||||||
|
* This class holds the data of table cell.
|
||||||
|
* Also make a cache for the width this cell's text occupied in console,
|
||||||
|
* to avoid duplicated calculation for occupied width.
|
||||||
|
*/
|
||||||
|
class TabulateCell {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Build cell with given text.
|
||||||
|
* @param[in] text Data of cell.
|
||||||
|
*/
|
||||||
|
TabulateCell(const std::u8string_view& text);
|
||||||
|
~TabulateCell();
|
||||||
|
YYCC_DEFAULT_COPY_MOVE(TabulateCell)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Get the text of cell.
|
||||||
|
* @return The text of cell.
|
||||||
|
*/
|
||||||
|
const std::u8string& get_text() const;
|
||||||
|
/**
|
||||||
|
* @brief Get width this cell's text occupied in console.
|
||||||
|
* @return The width this cell occupied.
|
||||||
|
*/
|
||||||
|
size_t get_text_width() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// @brief The data of cell.
|
||||||
|
std::u8string text;
|
||||||
|
/// @brief The width cache of this data occupied in console.
|
||||||
|
size_t text_width;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @brief The type representing one row of data in table.
|
||||||
|
*/
|
||||||
|
using Row = std::vector<TabulateCell>;
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @brief The type representing row collection in table.
|
||||||
|
*/
|
||||||
|
using Rows = std::vector<Row>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Main class of Tabulate
|
||||||
|
*/
|
||||||
|
class Tabulate {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Create Tabulate class with given column count.
|
||||||
|
* @details
|
||||||
|
* In default, the separator bar of table is 3 dash.
|
||||||
|
* Header and separator bar are also shown in default.
|
||||||
|
* @param[in] n Column count of table.
|
||||||
|
*/
|
||||||
|
Tabulate(size_t n);
|
||||||
|
~Tabulate();
|
||||||
|
YYCC_DELETE_COPY_MOVE(Tabulate)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Print table into given stream.
|
||||||
|
* @details In default, stream is \c stdout.
|
||||||
|
* @param[in] dst The stream printed into.
|
||||||
|
*/
|
||||||
|
void print(std::ostream& dst = std::cout) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the column count of table.
|
||||||
|
* @return Column count of table.
|
||||||
|
*/
|
||||||
|
size_t get_column_count() const;
|
||||||
|
/**
|
||||||
|
* @brief Change whether show table header when printing.
|
||||||
|
* @param[in] show_header True for showing, otherwise false.
|
||||||
|
*/
|
||||||
|
void show_header(bool show_header);
|
||||||
|
/**
|
||||||
|
* @brief Change whether show separator bar when printing.
|
||||||
|
* @param[in] show_bar True for showing, otherwise false.
|
||||||
|
*/
|
||||||
|
void show_bar(bool show_bar);
|
||||||
|
/**
|
||||||
|
* @brief Modify the prefix string of table.
|
||||||
|
* @details
|
||||||
|
* The prefix string of table is the string
|
||||||
|
* which will be printed before each lines of output.
|
||||||
|
* It is usually used for indent.
|
||||||
|
* @param[in] prefix The prefix string.
|
||||||
|
*/
|
||||||
|
void set_prefix(const std::u8string_view& prefix);
|
||||||
|
/**
|
||||||
|
* @brief Modify the header of table.
|
||||||
|
* @param[in] hdr An initializer list holding header texts one by one.
|
||||||
|
* @exception std::invalid_argument The size of given header is mismatch with column count.
|
||||||
|
*/
|
||||||
|
void set_header(std::initializer_list<std::u8string_view> hdr);
|
||||||
|
/**
|
||||||
|
* @brief Modify separator bar string of table.
|
||||||
|
* @param[in] bar New separator bar string.
|
||||||
|
*/
|
||||||
|
void set_bar(const std::u8string_view& bar);
|
||||||
|
/**
|
||||||
|
* @brief Add one data row into table.
|
||||||
|
* @param[in] row An initializer list holding row texts one by one.
|
||||||
|
* @exception std::invalid_argument The size of given header is mismatch with column count.
|
||||||
|
*/
|
||||||
|
void add_row(std::initializer_list<std::u8string_view> row);
|
||||||
|
/**
|
||||||
|
* @brief Clear all data rows of table.
|
||||||
|
* @details Table header and separator bar will not be changed.
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// @brief The column count of table.
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
/// @brief Whether showing table header.
|
||||||
|
bool header_display;
|
||||||
|
/// @brief Whether showing table separator bar between header and data.
|
||||||
|
bool bar_display;
|
||||||
|
/// @brief The prefix string presented in each lines of output table.
|
||||||
|
std::u8string prefix_string;
|
||||||
|
|
||||||
|
/// @brief Width recorder for header.
|
||||||
|
TabulateWidth header_widths;
|
||||||
|
/// @brief Width recorder for data.
|
||||||
|
TabulateWidth rows_widths;
|
||||||
|
|
||||||
|
/// @brief The header of table.
|
||||||
|
Row header;
|
||||||
|
/// @brief The separator bar of table.
|
||||||
|
TabulateCell bar;
|
||||||
|
/// @brief The data of table.
|
||||||
|
Rows rows;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -3,9 +3,10 @@ add_executable(YYCCTestbench "")
|
|||||||
# Setup testbench sources
|
# Setup testbench sources
|
||||||
target_sources(YYCCTestbench
|
target_sources(YYCCTestbench
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
main.cpp
|
||||||
|
|
||||||
shared/literals.cpp
|
shared/literals.cpp
|
||||||
|
|
||||||
main.cpp
|
|
||||||
yycc/macro/version_cmp.cpp
|
yycc/macro/version_cmp.cpp
|
||||||
yycc/macro/os_detector.cpp
|
yycc/macro/os_detector.cpp
|
||||||
yycc/macro/compiler_detector.cpp
|
yycc/macro/compiler_detector.cpp
|
||||||
@ -35,6 +36,7 @@ PRIVATE
|
|||||||
yycc/carton/pycodec.cpp
|
yycc/carton/pycodec.cpp
|
||||||
yycc/carton/termcolor.cpp
|
yycc/carton/termcolor.cpp
|
||||||
yycc/carton/wcwidth.cpp
|
yycc/carton/wcwidth.cpp
|
||||||
|
yycc/carton/tabulate.cpp
|
||||||
)
|
)
|
||||||
target_sources(YYCCTestbench
|
target_sources(YYCCTestbench
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
13
testbench/yycc/carton/tabulate.cpp
Normal file
13
testbench/yycc/carton/tabulate.cpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <yycc.hpp>
|
||||||
|
#include <yycc/carton/tabulate.hpp>
|
||||||
|
|
||||||
|
#define TABULATE ::yycc::carton::tabulate
|
||||||
|
|
||||||
|
namespace yycctest::carton::tabulate {
|
||||||
|
|
||||||
|
TEST(CartonTabulate, Main) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user