Skip to content

Commit

Permalink
[Feature] - Support of multi line cells in SimpleTable
Browse files Browse the repository at this point in the history
  • Loading branch information
sjanel committed Mar 10, 2024
1 parent 1d1bddb commit e145ef3
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 66 deletions.
9 changes: 3 additions & 6 deletions src/engine/src/queryresultprinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1316,14 +1316,11 @@ void QueryResultPrinter::printDustSweeper(
SimpleTable simpleTable("Exchange", "Account", "Trades", "Final Amount");
for (const auto &[exchangePtr, tradedAmountsVectorWithFinalAmount] :
tradedAmountsVectorWithFinalAmountPerExchange) {
string tradesStr;
SimpleTable::Cell tradesCell;
for (const auto &tradedAmounts : tradedAmountsVectorWithFinalAmount.tradedAmountsVector) {
if (!tradesStr.empty()) {
tradesStr.append(", ");
}
tradesStr.append(tradedAmounts.str());
tradesCell.emplace_back(tradedAmounts.str());
}
simpleTable.emplace_back(exchangePtr->name(), exchangePtr->keyName(), std::move(tradesStr),
simpleTable.emplace_back(exchangePtr->name(), exchangePtr->keyName(), std::move(tradesCell),
tradedAmountsVectorWithFinalAmount.finalAmount.str());
}
printTable(simpleTable);
Expand Down
16 changes: 9 additions & 7 deletions src/engine/test/queryresultprinter_private_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1660,13 +1660,15 @@ TEST_F(QueryResultPrinterDustSweeperTest, FormattedTable) {
basicQueryResultPrinter(ApiOutputType::kFormattedTable)
.printDustSweeper(tradedAmountsVectorWithFinalAmountPerExchange, cur);
static constexpr std::string_view kExpected = R"(
+----------+-----------+-------------------------------------------------------+--------------+
| Exchange | Account | Trades | Final Amount |
+----------+-----------+-------------------------------------------------------+--------------+
| binance | testuser1 | 98.47 ETH -> 0.00005 BTC | 0 ETH |
| huobi | testuser1 | | 1.56 ETH |
| huobi | testuser2 | 0.45609 EUR -> 98.47 ETH, 1509.45 ETH -> 0.000612 BTC | 0 ETH |
+----------+-----------+-------------------------------------------------------+--------------+
+----------+-----------+-----------------------------+--------------+
| Exchange | Account | Trades | Final Amount |
+----------+-----------+-----------------------------+--------------+
| binance | testuser1 | 98.47 ETH -> 0.00005 BTC | 0 ETH |
| huobi | testuser1 | | 1.56 ETH |
|~~~~~~~~~~|~~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~|
| huobi | testuser2 | 0.45609 EUR -> 98.47 ETH | 0 ETH |
| | | 1509.45 ETH -> 0.000612 BTC | |
+----------+-----------+-----------------------------+--------------+
)";
expectStr(kExpected);
}
Expand Down
115 changes: 95 additions & 20 deletions src/tech/include/simpletable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,23 @@ namespace cct {
/// Simple, lightweight and fast table with dynamic number of columns.
/// No checks are made about the number of columns for each Row, it's up to client's responsibility to make sure they
/// match.
/// Multi line rows are *not* supported.
/// The SimpleTable is made up of 'Row's, themselves made up of 'Cell's, themselves made of 'CellLine's.
/// The first Row is constructed like any other, but it will print an additional line separator to appear like a header.
/// No line separator will be placed between two single line only cells.
/// However, multi line Rows ('Row' containing at least a 'Cell' with several 'CellLine's) will have line separators
/// before and after them.
/// It is also possible to force a line separator between any row in the print. For that, you can insert the special Row
/// Row::kDivider at the desired place in the SimpleTable.
/// See the unit test to have an overview of its usage and the look and feel of the print.
class SimpleTable {
public:
class Row;
class Cell;

/// Cell in a SimpleTable.
/// Cell in a SimpleTable on a single line.
/// Can currently hold only 4 types of values: a string, a string_view, a int64_t and a bool.
class Cell {
class CellLine {
public:
using IntegralType = int64_t;
#ifdef CCT_MSVC
// folly::string does not support operator<< correctly with alignments with MSVC. Hence we use std::string
// in SimpleTable to guarantee correct alignment of formatted table. Referenced in this issue:
Expand All @@ -40,39 +47,103 @@ class SimpleTable {
#else
using string_type = string;
#endif
using value_type = std::variant<string_type, std::string_view, IntegralType, bool>;
using value_type = std::variant<string_type, std::string_view, int64_t, uint64_t, bool>;
using size_type = uint32_t;

explicit Cell(std::string_view sv = std::string_view()) : _data(sv) {}
explicit CellLine(std::string_view sv = std::string_view()) : _data(sv) {}

explicit Cell(const char *cstr) : _data(std::string_view(cstr)) {}
explicit CellLine(const char *cstr) : _data(std::string_view(cstr)) {}

#ifdef CCT_MSVC
explicit Cell(const string &v) : _data(std::string(v.data(), v.size())) {}
#else
explicit Cell(const string_type &str) : _data(str) {}
explicit CellLine(const string_type &str) : _data(str) {}

explicit Cell(string_type &&str) : _data(std::move(str)) {}
explicit CellLine(string_type &&str) : _data(std::move(str)) {}
#endif

explicit Cell(std::integral auto val) : _data(val) {}
explicit CellLine(std::integral auto val) : _data(val) {}

// Number of chars of this single line cell value.
size_type size() const noexcept;

void swap(Cell &rhs) noexcept { _data.swap(rhs._data); }
size_type length() const noexcept { return size(); }

size_type width() const noexcept { return size(); }

void swap(CellLine &rhs) noexcept { _data.swap(rhs._data); }

using trivially_relocatable = is_trivially_relocatable<string_type>::type;

bool operator==(const CellLine &) const noexcept = default;

std::strong_ordering operator<=>(const CellLine &) const noexcept = default;

friend std::ostream &operator<<(std::ostream &os, const CellLine &singleLineCell);

private:
value_type _data;
};

class Cell {
public:
using value_type = CellLine;
using size_type = uint32_t;
using iterator = SmallVector<value_type, 1>::iterator;
using const_iterator = SmallVector<value_type, 1>::const_iterator;

/// Implicit constructor of a Cell from a CellLine.
Cell(CellLine singleLineCell) { _singleLineCells.push_back(std::move(singleLineCell)); }

/// Creates a new Row with given list of cells.
template <class... Args>
explicit Cell(Args &&...singleLineCells) {
([&](auto &&input) { _singleLineCells.emplace_back(std::forward<decltype(input)>(input)); }(
std::forward<Args>(singleLineCells)),
...);
}

iterator begin() noexcept { return _singleLineCells.begin(); }
const_iterator begin() const noexcept { return _singleLineCells.begin(); }

iterator end() noexcept { return _singleLineCells.end(); }
const_iterator end() const noexcept { return _singleLineCells.end(); }

value_type &front() { return _singleLineCells.front(); }
const value_type &front() const { return _singleLineCells.front(); }

value_type &back() { return _singleLineCells.back(); }
const value_type &back() const { return _singleLineCells.back(); }

void push_back(const value_type &cell) { _singleLineCells.push_back(cell); }
void push_back(value_type &&cell) { _singleLineCells.push_back(std::move(cell)); }

template <class... Args>
value_type &emplace_back(Args &&...args) {
return _singleLineCells.emplace_back(std::forward<Args &&>(args)...);
}

size_type size() const noexcept { return _singleLineCells.size(); }

size_type width() const noexcept;

value_type &operator[](size_type cellPos) { return _singleLineCells[cellPos]; }
const value_type &operator[](size_type cellPos) const { return _singleLineCells[cellPos]; }

void swap(Cell &rhs) noexcept { _singleLineCells.swap(rhs._singleLineCells); }

using trivially_relocatable = is_trivially_relocatable<CellLine>::type;

bool operator==(const Cell &) const noexcept = default;

std::strong_ordering operator<=>(const Cell &) const noexcept = default;

private:
friend class Row;

void print(std::ostream &os, size_type maxCellWidth) const;
void print(std::ostream &os, size_type linePos, size_type maxCellWidth) const;

value_type _data;
SmallVector<value_type, 1> _singleLineCells;
};

/// Row in a SimpleTable.
Expand All @@ -85,10 +156,11 @@ class SimpleTable {

static const Row kDivider;

/// Creates a new Row with given list of cells.
template <class... Args>
explicit Row(Args &&...args) {
// Usage of C++17 fold expressions to make it possible to set a Row directly from a variadic input arguments
([&](auto &&input) { _cells.emplace_back(std::forward<decltype(input)>(input)); }(std::forward<Args>(args)), ...);
explicit Row(Args &&...cells) {
([&](auto &&input) { _cells.emplace_back(std::forward<decltype(input)>(input)); }(std::forward<Args>(cells)),
...);
}

iterator begin() noexcept { return _cells.begin(); }
Expand All @@ -103,8 +175,8 @@ class SimpleTable {
value_type &back() { return _cells.back(); }
const value_type &back() const { return _cells.back(); }

void push_back(const Cell &cell) { _cells.push_back(cell); }
void push_back(Cell &&cell) { _cells.push_back(std::move(cell)); }
void push_back(const value_type &cell) { _cells.push_back(cell); }
void push_back(value_type &&cell) { _cells.push_back(std::move(cell)); }

template <class... Args>
value_type &emplace_back(Args &&...args) {
Expand All @@ -113,11 +185,15 @@ class SimpleTable {

size_type size() const noexcept { return _cells.size(); }

bool isMultiLine() const noexcept;

bool isDivider() const noexcept { return _cells.empty(); }

value_type &operator[](size_type cellPos) { return _cells[cellPos]; }
const value_type &operator[](size_type cellPos) const { return _cells[cellPos]; }

void swap(Row &rhs) noexcept { _cells.swap(rhs._cells); }

using trivially_relocatable = is_trivially_relocatable<vector<value_type>>::type;

bool operator==(const Row &) const noexcept = default;
Expand Down Expand Up @@ -165,6 +241,7 @@ class SimpleTable {
size_type size() const noexcept { return _rows.size(); }
bool empty() const noexcept { return _rows.empty(); }

value_type &operator[](size_type rowPos) { return _rows[rowPos]; }
const value_type &operator[](size_type rowPos) const { return _rows[rowPos]; }

void reserve(size_type sz) { _rows.reserve(sz); }
Expand All @@ -176,8 +253,6 @@ class SimpleTable {
private:
using MaxWidthPerColumnVector = SmallVector<uint16_t, 8>;

static Cell::string_type ComputeLineSep(std::span<const uint16_t> maxWidthPerColumnVector);

MaxWidthPerColumnVector computeMaxWidthPerColumn() const;

vector<Row> _rows;
Expand Down
Loading

0 comments on commit e145ef3

Please sign in to comment.