Icarus
Vehicle Simulation as a Transformable Computational Graph, built on Vulcan and Janus
Loading...
Searching...
No Matches
AsciiTable.hpp
Go to the documentation of this file.
1#pragma once
2
9
11
12#include <algorithm>
13#include <sstream>
14#include <string>
15#include <vector>
16
17namespace icarus {
18
31 public:
32 enum class Align { Left, Right, Center };
33
34 struct Column {
35 std::string header;
36 std::size_t width = 0;
38 };
39
41 void AddColumn(const std::string &header, std::size_t width = 0, Align align = Align::Left) {
42 Column col;
43 col.header = header;
44 col.width = width;
45 col.align = align;
46 columns_.push_back(col);
47 }
48
50 void AddRow(const std::vector<std::string> &cells) { rows_.push_back(cells); }
51
53 [[nodiscard]] std::string Render() const {
54 if (columns_.empty()) {
55 return "";
56 }
57
58 // Calculate column widths
59 std::vector<std::size_t> widths = CalculateWidths();
60
61 std::ostringstream oss;
62
63 // Top border
64 oss << RenderTopBorder(widths) << "\n";
65
66 // Header row (always left-aligned)
67 oss << RenderHeaderRow(GetHeaders(), widths) << "\n";
68
69 // Header separator
70 oss << RenderHeaderSeparator(widths) << "\n";
71
72 // Data rows
73 for (const auto &row : rows_) {
74 oss << RenderRow(row, widths) << "\n";
75 }
76
77 // Bottom border
78 oss << RenderBottomBorder(widths) << "\n";
79
80 return oss.str();
81 }
82
84 void ClearRows() { rows_.clear(); }
85
87 void Clear() {
88 columns_.clear();
89 rows_.clear();
90 }
91
92 private:
93 std::vector<Column> columns_;
94 std::vector<std::vector<std::string>> rows_;
95
96 [[nodiscard]] std::vector<std::string> GetHeaders() const {
97 std::vector<std::string> headers;
98 headers.reserve(columns_.size());
99 for (const auto &col : columns_) {
100 headers.push_back(col.header);
101 }
102 return headers;
103 }
104
105 [[nodiscard]] std::vector<std::size_t> CalculateWidths() const {
106 std::vector<std::size_t> widths;
107 widths.reserve(columns_.size());
108
109 for (std::size_t i = 0; i < columns_.size(); ++i) {
110 std::size_t width = columns_[i].width;
111 if (width == 0) {
112 // Auto-size: max of header and all data
113 width = columns_[i].header.size();
114 for (const auto &row : rows_) {
115 if (i < row.size()) {
116 width = std::max(width, row[i].size());
117 }
118 }
119 }
120 widths.push_back(width);
121 }
122 return widths;
123 }
124
125 [[nodiscard]] std::string RenderTopBorder(const std::vector<std::size_t> &widths) const {
126 std::ostringstream oss;
127 oss << BoxChars::TopLeft;
128 for (std::size_t i = 0; i < widths.size(); ++i) {
129 for (std::size_t j = 0; j < widths[i] + 2; ++j) {
131 }
132 if (i < widths.size() - 1) {
133 oss << BoxChars::TeeDown;
134 }
135 }
136 oss << BoxChars::TopRight;
137 return oss.str();
138 }
139
140 [[nodiscard]] std::string RenderHeaderSeparator(const std::vector<std::size_t> &widths) const {
141 std::ostringstream oss;
142 oss << BoxChars::TeeRight;
143 for (std::size_t i = 0; i < widths.size(); ++i) {
144 for (std::size_t j = 0; j < widths[i] + 2; ++j) {
146 }
147 if (i < widths.size() - 1) {
148 oss << BoxChars::Cross;
149 }
150 }
151 oss << BoxChars::TeeLeft;
152 return oss.str();
153 }
154
155 [[nodiscard]] std::string RenderBottomBorder(const std::vector<std::size_t> &widths) const {
156 std::ostringstream oss;
158 for (std::size_t i = 0; i < widths.size(); ++i) {
159 for (std::size_t j = 0; j < widths[i] + 2; ++j) {
161 }
162 if (i < widths.size() - 1) {
163 oss << BoxChars::TeeUp;
164 }
165 }
167 return oss.str();
168 }
169
170 [[nodiscard]] std::string RenderRow(const std::vector<std::string> &cells,
171 const std::vector<std::size_t> &widths) const {
172 std::ostringstream oss;
173 oss << BoxChars::Vertical;
174 for (std::size_t i = 0; i < widths.size(); ++i) {
175 std::string cell = (i < cells.size()) ? cells[i] : "";
176 Align align = (i < columns_.size()) ? columns_[i].align : Align::Left;
177 oss << " " << AlignCell(cell, widths[i], align) << " ";
178 oss << BoxChars::Vertical;
179 }
180 return oss.str();
181 }
182
184 [[nodiscard]] std::string RenderHeaderRow(const std::vector<std::string> &cells,
185 const std::vector<std::size_t> &widths) const {
186 std::ostringstream oss;
187 oss << BoxChars::Vertical;
188 for (std::size_t i = 0; i < widths.size(); ++i) {
189 std::string cell = (i < cells.size()) ? cells[i] : "";
190 oss << " " << AlignCell(cell, widths[i], Align::Left) << " ";
191 oss << BoxChars::Vertical;
192 }
193 return oss.str();
194 }
195
197 [[nodiscard]] static std::size_t DisplayWidth(const std::string &text) {
198 std::size_t width = 0;
199 for (std::size_t i = 0; i < text.size(); ++i) {
200 unsigned char c = static_cast<unsigned char>(text[i]);
201 // Count only lead bytes (not continuation bytes 10xxxxxx)
202 if ((c & 0xC0) != 0x80) {
203 ++width;
204 }
205 }
206 return width;
207 }
208
209 [[nodiscard]] static std::string AlignCell(const std::string &text, std::size_t width,
210 Align align) {
211 std::size_t display_width = DisplayWidth(text);
212 if (display_width >= width) {
213 return text.substr(0, width);
214 }
215
216 std::size_t padding = width - display_width;
217 switch (align) {
218 case Align::Left:
219 return text + std::string(padding, ' ');
220 case Align::Right:
221 return std::string(padding, ' ') + text;
222 case Align::Center: {
223 std::size_t left = padding / 2;
224 std::size_t right = padding - left;
225 return std::string(left, ' ') + text + std::string(right, ' ');
226 }
227 }
228 return text;
229 }
230};
231
232} // namespace icarus
Console abstraction with ANSI color support.
ASCII table generator with box-drawing characters.
Definition AsciiTable.hpp:30
void ClearRows()
Clear all rows (keep columns).
Definition AsciiTable.hpp:84
void AddRow(const std::vector< std::string > &cells)
Add a row of data.
Definition AsciiTable.hpp:50
void Clear()
Clear everything.
Definition AsciiTable.hpp:87
Align
Definition AsciiTable.hpp:32
@ Center
Definition AsciiTable.hpp:32
@ Right
Definition AsciiTable.hpp:32
@ Left
Definition AsciiTable.hpp:32
std::string Render() const
Generate the formatted table string.
Definition AsciiTable.hpp:53
void AddColumn(const std::string &header, std::size_t width=0, Align align=Align::Left)
Add a column definition.
Definition AsciiTable.hpp:41
Definition AggregationTypes.hpp:13
Definition AsciiTable.hpp:34
std::string header
Definition AsciiTable.hpp:35
Align align
Definition AsciiTable.hpp:37
std::size_t width
0 = auto-size
Definition AsciiTable.hpp:36
static constexpr const char * TeeRight
Definition Console.hpp:87
static constexpr const char * TeeLeft
Definition Console.hpp:88
static constexpr const char * TeeUp
Definition Console.hpp:90
static constexpr const char * TeeDown
Definition Console.hpp:89
static constexpr const char * TopLeft
Definition Console.hpp:81
static constexpr const char * BottomLeft
Definition Console.hpp:83
static constexpr const char * Horizontal
Definition Console.hpp:85
static constexpr const char * BottomRight
Definition Console.hpp:84
static constexpr const char * Vertical
Definition Console.hpp:86
static constexpr const char * TopRight
Definition Console.hpp:82
static constexpr const char * Cross
Definition Console.hpp:91