diff --git a/barrel/Utils/Table.cc b/barrel/Utils/Table.cc index a4df863..c93f7b2 100644 --- a/barrel/Utils/Table.cc +++ b/barrel/Utils/Table.cc @@ -33,16 +33,38 @@ namespace barrel using namespace std; + struct Table::OutputInfo + { + OutputInfo(const Table& table); + + void calculate_hidden(const Table& table, const Table::Row& row); + void calculate_widths(const Table& table, const Table::Row& row, unsigned indent); + size_t calculate_total_width(const Table& table) const; + void calculate_abbriviated_widths(const Table& table); + + struct ColumnVars + { + bool hidden = false; + size_t width = 0; + }; + + vector column_vars; + }; + + Table::OutputInfo::OutputInfo(const Table& table) { - // calculate hidden, default to false + if (table.column_params.size() != table.header.get_columns().size()) + throw runtime_error("column_params.size != header.size"); - hidden.resize(table.header.get_columns().size()); + column_vars.resize(table.header.get_columns().size()); - for (size_t i = 0; i < table.visibilities.size(); ++i) + // calculate hidden, default to false + + for (size_t idx = 0; idx < table.column_params.size(); ++idx) { - if (table.visibilities[i] == Visibility::AUTO || table.visibilities[i] == Visibility::OFF) - hidden[i] = true; + if (table.column_params[idx].visibility == Visibility::AUTO || table.column_params[idx].visibility == Visibility::OFF) + column_vars[idx].hidden = true; } for (const Table::Row& row : table.rows) @@ -50,7 +72,8 @@ namespace barrel // calculate widths - widths = table.min_widths; + for (size_t idx = 0; idx < table.column_params.size(); ++idx) + column_vars[idx].width = table.column_params[idx].min_width; if (table.show_header) calculate_widths(table, table.header, 0); @@ -67,10 +90,13 @@ namespace barrel { const vector& columns = row.get_columns(); - for (size_t i = 0; i < min(columns.size(), table.visibilities.size()); ++i) + if (columns.size() > table.column_params.size()) + throw runtime_error("too many columns"); + + for (size_t idx = 0; idx < columns.size(); ++idx) { - if (table.visibilities[i] == Visibility::AUTO && !columns[i].empty()) - hidden[i] = false; + if (table.column_params[idx].visibility == Visibility::AUTO && !columns[idx].empty()) + column_vars[idx].hidden = false; } for (const Table::Row& subrow : row.get_subrows()) @@ -83,20 +109,17 @@ namespace barrel { const vector& columns = row.get_columns(); - if (columns.size() > widths.size()) - widths.resize(columns.size()); - - for (size_t i = 0; i < columns.size(); ++i) + for (size_t idx = 0; idx < columns.size(); ++idx) { - if (hidden[i]) + if (column_vars[idx].hidden) continue; - size_t width = mbs_width(columns[i]); + size_t width = mbs_width(columns[idx]); - if (i == table.tree_index) + if (idx == table.tree_idx) width += 2 * indent; - widths[i] = max(widths[i], width); + column_vars[idx].width = max(column_vars[idx].width, width); } for (const Table::Row& subrow : row.get_subrows()) @@ -114,9 +137,9 @@ namespace barrel bool first = true; - for (size_t i = 0; i < widths.size(); ++i) + for (size_t idx = 0; idx < column_vars.size(); ++idx) { - if (hidden[i]) + if (column_vars[idx].hidden) continue; if (first) @@ -124,7 +147,7 @@ namespace barrel else total_width += 2 + (table.show_grid ? 1 : 0); - total_width += widths[i]; + total_width += column_vars[idx].width; } return total_width; @@ -144,11 +167,11 @@ namespace barrel size_t too_much = total_width - table.screen_width; - for (size_t i = 0; i < table.abbreviates.size(); ++i) + for (size_t idx = 0; idx < table.column_params.size(); ++idx) { - if (table.abbreviates[i]) + if (table.column_params[idx].abbreviate) { - widths[i] = max(widths[i] - too_much, (size_t) 5); + column_vars[idx].width = max(column_vars[idx].width - too_much, (size_t) 5); break; } } @@ -156,23 +179,23 @@ namespace barrel void - Table::output(std::ostream& s, const Table::Row& row, const OutputInfo& output_info, const vector& lasts) const + Table::output(std::ostream& s, const OutputInfo& output_info, const Table::Row& row, const vector& lasts) const { s << string(global_indent, ' '); const vector& columns = row.get_columns(); - for (size_t i = 0; i < output_info.widths.size(); ++i) + for (size_t idx = 0; idx < output_info.column_vars.size(); ++idx) { - if (output_info.hidden[i]) + if (output_info.column_vars[idx].hidden) continue; - string column = i < columns.size() ? columns[i] : ""; + string column = idx < columns.size() ? columns[idx] : ""; - bool first = i == 0; - bool last = i == output_info.widths.size() - 1; + bool first = idx == 0; + bool last = idx == output_info.column_vars.size() - 1; - size_t extra = (i == tree_index) ? 2 * lasts.size() : 0; + size_t extra = (idx == tree_idx) ? 2 * lasts.size() : 0; if (last && column.empty()) break; @@ -180,7 +203,7 @@ namespace barrel if (!first) s << " "; - if (i == tree_index) + if (idx == tree_idx) { for (size_t tl = 0; tl < lasts.size(); ++tl) { @@ -193,10 +216,10 @@ namespace barrel size_t width = mbs_width(column); - if (aligns[i] == Align::RIGHT) + if (column_params[idx].align == Align::RIGHT) { - if (width < output_info.widths[i] - extra) - s << string(output_info.widths[i] - width - extra, ' '); + if (width < output_info.column_vars[idx].width - extra) + s << string(output_info.column_vars[idx].width - width - extra, ' '); } s << column; @@ -204,10 +227,10 @@ namespace barrel if (last) break; - if (aligns[i] == Align::LEFT) + if (column_params[idx].align == Align::LEFT) { - if (width < output_info.widths[i] - extra) - s << string(output_info.widths[i] - width - extra, ' '); + if (width < output_info.column_vars[idx].width - extra) + s << string(output_info.column_vars[idx].width - width - extra, ' '); } s << " "; @@ -223,7 +246,7 @@ namespace barrel { vector sub_lasts = lasts; sub_lasts.push_back(i == subrows.size() - 1); - output(s, subrows[i], output_info, sub_lasts); + output(s, output_info, subrows[i], sub_lasts); } } @@ -236,15 +259,15 @@ namespace barrel { s << string(global_indent, ' '); - for (size_t i = 0; i < output_info.widths.size(); ++i) + for (size_t idx = 0; idx < output_info.column_vars.size(); ++idx) { - if (output_info.hidden[i]) + if (output_info.column_vars[idx].hidden) continue; - for (size_t j = 0; j < output_info.widths[i]; ++j) + for (size_t j = 0; j < output_info.column_vars[idx].width; ++j) s << glyph(1); - if (i == output_info.widths.size() - 1) + if (idx == output_info.column_vars.size() - 1) break; s << glyph(1) << glyph(2) << glyph(1); @@ -254,12 +277,23 @@ namespace barrel } + bool + Table::has_id(Id id) const + { + for (size_t idx = 0; idx < column_params.size(); ++idx) + if (column_params[idx].id == id) + return true; + + return false; + } + + size_t - Table::id_to_index(Id id) const + Table::id_to_idx(Id id) const { - for (size_t i = 0; i < ids.size(); ++i) - if (ids[i] == id) - return i; + for (size_t idx = 0; idx < column_params.size(); ++idx) + if (column_params[idx].id == id) + return idx; throw runtime_error("id not found"); } @@ -268,12 +302,12 @@ namespace barrel string& Table::Row::operator[](Id id) { - size_t i = table.id_to_index(id); + size_t idx = table.id_to_idx(id); - if (columns.size() < i + 1) - columns.resize(i + 1); + if (columns.size() < idx + 1) + columns.resize(idx + 1); - return columns[i]; + return columns[idx]; } @@ -296,8 +330,7 @@ namespace barrel for (const Cell& cell : init) { header.add(cell.name); - ids.push_back(cell.id); - aligns.push_back(cell.align); + column_params.emplace_back(cell.id, cell.align); } } @@ -308,8 +341,7 @@ namespace barrel for (const Cell& cell : init) { header.add(cell.name); - ids.push_back(cell.id); - aligns.push_back(cell.align); + column_params.emplace_back(cell.id, cell.align); } } @@ -317,43 +349,31 @@ namespace barrel void Table::set_min_width(Id id, size_t min_width) { - size_t i = id_to_index(id); - - if (min_widths.size() < i + 1) - min_widths.resize(i + 1); - - min_widths[i] = min_width; + size_t idx = id_to_idx(id); + column_params[idx].min_width = min_width; } void Table::set_visibility(Id id, Visibility visibility) { - size_t i = id_to_index(id); - - if (visibilities.size() < i + 1) - visibilities.resize(i + 1); - - visibilities[i] = visibility; + size_t idx = id_to_idx(id); + column_params[idx].visibility = visibility; } void Table::set_abbreviate(Id id, bool abbreviate) { - size_t i = id_to_index(id); - - if (abbreviates.size() < i + 1) - abbreviates.resize(i + 1); - - abbreviates[i] = abbreviate; + size_t idx = id_to_idx(id); + column_params[idx].abbreviate = abbreviate; } void Table::set_tree_id(Id id) { - tree_index = id_to_index(id); + tree_idx = id_to_idx(id); } @@ -367,13 +387,13 @@ namespace barrel // output header and rows if (table.show_header) - table.output(s, table.header, output_info, {}); + table.output(s, output_info, table.header, {}); if (table.show_header && table.show_grid) table.output(s, output_info); for (const Table::Row& row : table.rows) - table.output(s, row, output_info, {}); + table.output(s, output_info, row, {}); return s; } diff --git a/barrel/Utils/Table.h b/barrel/Utils/Table.h index 67ec5b5..356ff2b 100644 --- a/barrel/Utils/Table.h +++ b/barrel/Utils/Table.h @@ -142,6 +142,8 @@ namespace barrel const vector get_rows() const { return rows; } + bool has_id(Id id) const; + void set_show_header(bool show_header) { Table::show_header = show_header; } void set_show_grid(bool show_grid) { Table::show_grid = show_grid; } void set_style(Style style) { Table::style = style; } @@ -166,30 +168,26 @@ namespace barrel Style style = Style::ASCII; size_t global_indent = 0; size_t screen_width = -1; + size_t tree_idx = 0; - vector aligns; - vector ids; - vector min_widths; - vector visibilities; - vector abbreviates; - size_t tree_index = 0; + struct ColumnParams + { + ColumnParams(Id id, Align align) : id(id), align(align) {} - size_t id_to_index(Id id) const; + Id id = Id::NONE; + Align align = Align::LEFT; + size_t min_width = 0; + Visibility visibility = Visibility::ON; + bool abbreviate = false; + }; - struct OutputInfo - { - OutputInfo(const Table& table); + vector column_params; - void calculate_hidden(const Table& table, const Table::Row& row); - void calculate_widths(const Table& table, const Table::Row& row, unsigned indent); - size_t calculate_total_width(const Table& table) const; - void calculate_abbriviated_widths(const Table& table); + size_t id_to_idx(Id id) const; - vector hidden; - vector widths; - }; + struct OutputInfo; - void output(std::ostream& s, const Table::Row& row, const OutputInfo& output_info, + void output(std::ostream& s, const OutputInfo& output_info, const Table::Row& row, const vector& lasts) const; void output(std::ostream& s, const OutputInfo& output_info) const; diff --git a/testsuite/table.cc b/testsuite/table.cc index 399e4d6..20ce689 100644 --- a/testsuite/table.cc +++ b/testsuite/table.cc @@ -10,6 +10,44 @@ using namespace barrel; BOOST_AUTO_TEST_CASE(test1) +{ + Table table({ "A", "B" }); + + table.set_style(Style::LIGHT); + + Table::Row row1(table, { "a" }); + table.add(row1); + + Table::Row row2(table, { "a", "b" }); + table.add(row2); + + ostringstream o; + o << table; + + string s = + "A │ B\n" + "──┼──\n" + "a │\n" + "a │ b\n"; + + BOOST_CHECK_EQUAL(o.str(), s); +} + + +BOOST_AUTO_TEST_CASE(test2) +{ + Table table({ "A", "B" }); + + Table::Row row1(table, { "a", "b", "c" }); + table.add(row1); + + BOOST_CHECK_EXCEPTION({ ostringstream tmp; tmp << table; }, runtime_error, [](const exception& e) { + return strcmp(e.what(), "too many columns") == 0; + }); +} + + +BOOST_AUTO_TEST_CASE(test3) { Table table({ "Name", Cell("Size", Id::SIZE, Align::RIGHT), "Usage" }); table.set_style(Style::LIGHT); @@ -38,7 +76,7 @@ BOOST_AUTO_TEST_CASE(test1) } -BOOST_AUTO_TEST_CASE(test2) +BOOST_AUTO_TEST_CASE(test4) { Table table({ "Name", Cell("Size", Id::SIZE, Align::RIGHT) }); table.set_style(Style::LIGHT); @@ -85,7 +123,7 @@ BOOST_AUTO_TEST_CASE(test2) } -BOOST_AUTO_TEST_CASE(test3) +BOOST_AUTO_TEST_CASE(test5) { Table table({ "Name", "Level" }); table.set_style(Style::LIGHT); @@ -127,7 +165,7 @@ BOOST_AUTO_TEST_CASE(test3) } -BOOST_AUTO_TEST_CASE(test4) +BOOST_AUTO_TEST_CASE(test6) { Table table({ "Level", Cell("Name", Id::NAME) }); table.set_style(Style::LIGHT); @@ -151,7 +189,7 @@ BOOST_AUTO_TEST_CASE(test4) } -BOOST_AUTO_TEST_CASE(test5) +BOOST_AUTO_TEST_CASE(test7) { Table table({ "Level", Cell("Name", Id::NAME) }); table.set_style(Style::ASCII);