set(
sources
+ ${CMAKE_CURRENT_SOURCE_DIR}/TextTable.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Tokenizer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/file.cpp
${CMAKE_CURRENT_SOURCE_DIR}/path.cpp
--- /dev/null
+// Copyright (C) 2021 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "TextTable.hpp"
+
+#include <third_party/fmt/core.h>
+
+#include <algorithm>
+
+namespace util {
+
+void
+TextTable::add_heading(const std::string& text)
+{
+ Cell cell(text);
+ cell.m_heading = true;
+ m_rows.push_back({cell});
+}
+
+void
+TextTable::add_row(const std::initializer_list<Cell> cells)
+{
+ m_rows.emplace_back(cells);
+}
+
+std::string
+TextTable::render() const
+{
+ std::vector<size_t> column_widths;
+
+ for (const auto& row : m_rows) {
+ column_widths.resize(std::max(column_widths.size(), row.size()));
+ for (size_t i = 0; i < row.size(); ++i) {
+ if (!row[i].m_heading) {
+ column_widths[i] = std::max(column_widths[i], row[i].m_text.size());
+ }
+ }
+ }
+
+ std::string result;
+ for (const auto& row : m_rows) {
+ std::string r;
+ for (size_t i = 0; i < row.size(); ++i) {
+ if (i > 0) {
+ r += ' ';
+ }
+ r += fmt::format((row[i].m_right_align ? "{:>{}}" : "{:<{}}"),
+ row[i].m_text,
+ column_widths[i]);
+ }
+ result.append(r, 0, r.find_last_not_of(' ') + 1);
+ result += '\n';
+ }
+ return result;
+}
+
+TextTable::Cell::Cell(const std::string& text) : m_text(text)
+{
+}
+
+TextTable::Cell&
+TextTable::Cell::right_align()
+{
+ m_right_align = true;
+ return *this;
+}
+
+} // namespace util
--- /dev/null
+// Copyright (C) 2021 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#pragma once
+
+#include <initializer_list>
+#include <string>
+#include <vector>
+
+namespace util {
+
+class TextTable
+{
+public:
+ class Cell
+ {
+ public:
+ Cell(const std::string& text);
+
+ Cell& right_align();
+
+ private:
+ friend TextTable;
+
+ const std::string m_text;
+ bool m_right_align = false;
+ bool m_heading = false;
+ };
+
+ void add_heading(const std::string& text);
+ void add_row(std::initializer_list<Cell> cells);
+ std::string render() const;
+
+private:
+ std::vector<std::vector<Cell>> m_rows;
+};
+
+} // namespace util
test_hashutil.cpp
test_storage_primary_StatsFile.cpp
test_storage_primary_util.cpp
+ test_util_TextTable.cpp
test_util_Tokenizer.cpp
test_util_expected.cpp
test_util_path.cpp
--- /dev/null
+// Copyright (C) 2021 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include <util/TextTable.hpp>
+
+#include <third_party/doctest.h>
+
+#include <iostream> // macOS bug: https://github.com/onqtam/doctest/issues/126
+
+TEST_CASE("TextTable")
+{
+ using C = util::TextTable::Cell;
+
+ util::TextTable table;
+
+ SUBCASE("empty")
+ {
+ CHECK(table.render() == "");
+ }
+
+ SUBCASE("1x1")
+ {
+ table.add_row({{"a"}});
+ CHECK(table.render() == "a\n");
+ }
+
+ SUBCASE("2x1 with space prefix/suffix")
+ {
+ table.add_row({{" a "}, C(" b ")});
+ CHECK(table.render() == " a b\n");
+ }
+
+ SUBCASE("1x2")
+ {
+ table.add_row({{"a"}});
+ table.add_row({{"b"}});
+ CHECK(table.render() == "a\nb\n");
+ }
+
+ SUBCASE("3 + 2")
+ {
+ table.add_row({{"a"}, {"b"}, {"c"}});
+ table.add_row({{"aa"}, {"bbb"}});
+ CHECK(table.render()
+ == ("a b c\n"
+ "aa bbb\n"));
+ }
+
+ SUBCASE("right align")
+ {
+ table.add_row({{"a"}, {"bbb"}, {"cc"}});
+ table.add_row(
+ {C("aa").right_align(), C("b").right_align(), C("ccc").right_align()});
+ table.add_row({{"aaa"}, {"bb"}, {"c"}});
+ CHECK(table.render()
+ == ("a bbb cc\n"
+ " aa b ccc\n"
+ "aaa bb c\n"));
+ }
+
+ SUBCASE("heading")
+ {
+ table.add_row({{"a"}, {"b"}, {"c"}});
+ table.add_heading("DDDDDD");
+ table.add_row({{"aaa"}, {"bbb"}, {"ccc"}});
+ CHECK(table.render()
+ == ("a b c\n"
+ "DDDDDD\n"
+ "aaa bbb ccc\n"));
+ }
+}