From: Joel Rosdahl Date: Fri, 23 Jul 2021 11:52:21 +0000 (+0200) Subject: Add TextTable class X-Git-Tag: v4.4~94 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b04bce82de6e726ccd2a6af59677a5a28bb93198;p=thirdparty%2Fccache.git Add TextTable class --- diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 6912ad654..48e96ae62 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,5 +1,6 @@ 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 diff --git a/src/util/TextTable.cpp b/src/util/TextTable.cpp new file mode 100644 index 000000000..809b1f854 --- /dev/null +++ b/src/util/TextTable.cpp @@ -0,0 +1,83 @@ +// 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 + +#include + +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 cells) +{ + m_rows.emplace_back(cells); +} + +std::string +TextTable::render() const +{ + std::vector 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 diff --git a/src/util/TextTable.hpp b/src/util/TextTable.hpp new file mode 100644 index 000000000..a6d19407b --- /dev/null +++ b/src/util/TextTable.hpp @@ -0,0 +1,53 @@ +// 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 +#include +#include + +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 cells); + std::string render() const; + +private: + std::vector> m_rows; +}; + +} // namespace util diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 6c2b3d2cc..d30902084 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -24,6 +24,7 @@ set( 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 diff --git a/unittest/test_util_TextTable.cpp b/unittest/test_util_TextTable.cpp new file mode 100644 index 000000000..143509594 --- /dev/null +++ b/unittest/test_util_TextTable.cpp @@ -0,0 +1,86 @@ +// 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 + +#include + +#include // 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")); + } +}