LCOV - code coverage report
Current view: top level - src/Tools/Display/Terminal/Standard - Terminal_std.cpp (source / functions) Hit Total Coverage
Test: streampu_clean.info Lines: 101 140 72.1 %
Date: 2024-07-31 15:48:41 Functions: 5 8 62.5 %

          Line data    Source code
       1             : #include <algorithm>
       2             : #include <cassert>
       3             : #include <cmath>
       4             : #include <iomanip>
       5             : #include <ios>
       6             : 
       7             : #include "Tools/Display/Terminal/Standard/Terminal_std.hpp"
       8             : #include "Tools/Display/rang_format/rang_format.h"
       9             : #include "Tools/Exception/exception.hpp"
      10             : 
      11             : using namespace spu;
      12             : using namespace spu::tools;
      13             : 
      14             : const char spu::tools::Terminal_std::line_separator = '-';
      15             : const std::string spu::tools::Terminal_std::col_separator = "|";
      16             : const std::string spu::tools::Terminal_std::group_separator = "||";
      17             : const std::string spu::tools::Terminal_std::spaced_scol_separator = " |";
      18             : const std::string spu::tools::Terminal_std::spaced_dcol_separator = " ||";
      19             : const std::string spu::tools::Terminal_std::data_tag = "  ";
      20             : const rang::style spu::tools::Terminal_std::legend_style = rang::style::bold;
      21             : const rang::style spu::tools::Terminal_std::report_style = rang::style::bold;
      22             : 
      23             : #ifdef _WIN32
      24             : const size_t spu::tools::Terminal_std::def_column_width = 11;
      25             : #else
      26             : const size_t spu::tools::Terminal_std::def_column_width = 10;
      27             : #endif
      28             : 
      29          14 : Terminal_std::Terminal_std(const std::vector<tools::Reporter*>& reporters)
      30             :   : Terminal()
      31          14 :   , reporters(reporters)
      32             : {
      33          14 : }
      34             : 
      35           0 : Terminal_std::Terminal_std(const std::vector<std::unique_ptr<tools::Reporter>>& reporters)
      36             :   : Terminal()
      37           0 :   , reporters()
      38             : {
      39           0 :     for (auto const& r : reporters)
      40           0 :         this->reporters.push_back(r.get());
      41           0 : }
      42             : 
      43             : std::vector<tools::Reporter*>&
      44           0 : Terminal_std::get_reporters()
      45             : {
      46           0 :     return this->reporters;
      47             : }
      48             : 
      49             : void
      50          14 : Terminal_std::legend(std::ostream& stream) const
      51             : {
      52          14 :     std::vector<const Reporter::group_t*> cols_groups;
      53             : 
      54          70 :     for (auto& r : this->reporters)
      55          56 :         if (r != nullptr)
      56         112 :             for (auto& g : r->get_groups())
      57          56 :                 cols_groups.push_back(&g);
      58             :         else
      59           0 :             throw tools::runtime_error(__FILE__, __LINE__, __func__, "'this->reporters' contains null pointer.");
      60             : 
      61          14 :     std::ios::fmtflags f(stream.flags());
      62             : 
      63             :     // clang-format off
      64             :     // stream << "# " << "----------------------------------------------||---------------------------------" << std::endl; // line 1
      65             :     // stream << "# " << "          cols_groups[0].first.first          ||   cols_groups[1].first.first    " << std::endl; // line 2
      66             :     // stream << "# " << "          cols_groups[0].first.second         ||   cols_groups[1].first.second   " << std::endl; // line 3
      67             :     // stream << "# " << "----------------------------------------------||---------------------------------" << std::endl; // line 4
      68             :     // stream << "# " << "----------|-----------|-----------|-----------||----------|----------|-----------" << std::endl; // line 5
      69             :     // stream << "# " << "   (1.1)  |    (2.1)  |    (3.1)  |    (4.1)  ||   (5.1)  |   (6.1)  |   (7.1)   " << std::endl; // line 6
      70             :     // stream << "# " << "   (1.2)  |    (2.2)  |    (3.2)  |    (4.2)  ||   (5.2)  |   (6.2)  |   (7.2)   " << std::endl; // line 7
      71             :     // stream << "# " << "----------|-----------|-----------|-----------||----------|----------|-----------" << std::endl; // line 8
      72             :     // note (1.1) is "cols_groups[0].second[0].first"
      73             :     // note (1.2) is "cols_groups[0].second[0].second"
      74             :     // note (2.1) is "cols_groups[0].second[1].first"
      75             :     // note (2.2) is "cols_groups[0].second[1].second"
      76             :     // note (3.1) is "cols_groups[0].second[2].first"
      77             :     // note (3.2) is "cols_groups[0].second[2].second"
      78             :     // note (4.1) is "cols_groups[0].second[3].first"
      79             :     // note (4.2) is "cols_groups[0].second[3].second"
      80             :     // note (5.1) is "cols_groups[1].second[0].first"
      81             :     // note (5.2) is "cols_groups[1].second[0].second"
      82             :     // note (6.1) is "cols_groups[1].second[1].first"
      83             :     // note (6.2) is "cols_groups[1].second[1].second"
      84             :     // note (7.1) is "cols_groups[1].second[2].first"
      85             :     // note (7.2) is "cols_groups[1].second[2].second"
      86             :     // clang-format on
      87             : 
      88          14 :     assert(!cols_groups.empty());
      89             : 
      90             :     // print line 1 of the table
      91          14 :     stream << rang::tag::comment;
      92          70 :     for (unsigned i = 0; i < cols_groups.size(); i++)
      93             :     {
      94          56 :         assert(!cols_groups[i]->second.empty());
      95             : 
      96          56 :         const unsigned group_width = get_group_width(*cols_groups[i]);
      97          56 :         const auto n_separators = group_width + extra_spaces(cols_groups[i]->first, group_width);
      98             : 
      99          56 :         stream << legend_style << std::string(n_separators, line_separator) << rang::style::reset;
     100             : 
     101          56 :         if (i < (cols_groups.size() - 1)) // print group separator except for last
     102          42 :             stream << legend_style << group_separator << rang::style::reset;
     103             :     }
     104          14 :     stream << std::endl;
     105             : 
     106             :     // print line 2 and 3 of the table (group title lines)
     107          42 :     for (auto l = 0; l < 2; l++)
     108             :     {
     109          28 :         stream << rang::tag::comment;
     110         140 :         for (unsigned i = 0; i < cols_groups.size(); i++)
     111             :         {
     112         112 :             const auto& text = l == 0 ? std::get<0>(cols_groups[i]->first) : std::get<1>(cols_groups[i]->first);
     113             : 
     114         112 :             const unsigned group_width = get_group_width(*cols_groups[i]);
     115         112 :             int n_spaces = (int)group_width - (int)text.size();
     116             : 
     117         112 :             if (text.size() !=
     118         112 :                 std::max(std::get<0>(cols_groups[i]->first).size(), std::get<1>(cols_groups[i]->first).size()))
     119          56 :                 n_spaces += extra_spaces(cols_groups[i]->first, group_width);
     120             : 
     121         112 :             const unsigned n_spaces_left = (n_spaces >= 0) ? (unsigned)n_spaces / 2 : 0;
     122         112 :             const unsigned n_spaces_right = (n_spaces >= 0) ? n_spaces - n_spaces_left : 0;
     123             : 
     124         112 :             stream << legend_style << std::string(n_spaces_left, ' ') << rang::style::reset;
     125         112 :             stream << legend_style << text << rang::style::reset;
     126         112 :             stream << legend_style << std::string(n_spaces_right, ' ') << rang::style::reset;
     127             : 
     128         112 :             if (i < (cols_groups.size() - 1)) // print group separator except for last
     129          84 :                 stream << legend_style << group_separator << rang::style::reset;
     130             :         }
     131          28 :         stream << std::endl;
     132             :     }
     133             : 
     134             :     // print line 4 of the table
     135          14 :     stream << rang::tag::comment;
     136          70 :     for (unsigned i = 0; i < cols_groups.size(); i++)
     137             :     {
     138          56 :         const unsigned group_width = get_group_width(*cols_groups[i]);
     139          56 :         const auto n_separators = group_width + extra_spaces(cols_groups[i]->first, group_width);
     140             : 
     141          56 :         stream << legend_style << std::string(n_separators, line_separator) << rang::style::reset;
     142             : 
     143          56 :         if (i < (cols_groups.size() - 1)) // print group separator except for last
     144          42 :             stream << legend_style << group_separator << rang::style::reset;
     145             :     }
     146          14 :     stream << std::endl;
     147             : 
     148             :     // print line 5 of the table
     149          14 :     stream << rang::tag::comment;
     150          70 :     for (unsigned i = 0; i < cols_groups.size(); i++)
     151             :     {
     152          56 :         const unsigned group_width = get_group_width(*cols_groups[i]);
     153          56 :         const auto n_extra = extra_spaces(cols_groups[i]->first, group_width);
     154             : 
     155         238 :         for (unsigned j = 0; j < cols_groups[i]->second.size(); j++)
     156             :         {
     157             :             auto n_separators =
     158         182 :               (std::get<2>(cols_groups[i]->second[j])) ? std::get<2>(cols_groups[i]->second[j]) : def_column_width;
     159         182 :             if (j == 0) n_separators += n_extra;
     160             : 
     161         182 :             stream << legend_style << std::string(n_separators, line_separator) << rang::style::reset;
     162         182 :             if (j < (cols_groups[i]->second.size() - 1)) // print column separator except for last
     163         126 :                 stream << legend_style << col_separator << rang::style::reset;
     164             :         }
     165             : 
     166          56 :         if (i < (cols_groups.size() - 1)) // print group separator except for last
     167          42 :             stream << legend_style << group_separator << rang::style::reset;
     168             :     }
     169          14 :     stream << std::endl;
     170             : 
     171             :     // print line 6 and 7 of the table (column title lines)
     172          42 :     for (auto l = 0; l < 2; l++)
     173             :     {
     174          28 :         stream << rang::tag::comment;
     175         140 :         for (unsigned i = 0; i < cols_groups.size(); i++)
     176             :         {
     177         112 :             const unsigned group_width = get_group_width(*cols_groups[i]);
     178         112 :             const auto n_extra = extra_spaces(cols_groups[i]->first, group_width);
     179             : 
     180         476 :             for (unsigned j = 0; j < cols_groups[i]->second.size(); j++)
     181             :             {
     182             :                 const auto& text =
     183         364 :                   l == 0 ? std::get<0>(cols_groups[i]->second[j]) : std::get<1>(cols_groups[i]->second[j]);
     184         672 :                 int n_spaces = (int)((std::get<2>(cols_groups[i]->second[j])) ? std::get<2>(cols_groups[i]->second[j])
     185         672 :                                                                               : def_column_width) -
     186         364 :                                (int)text.size() - 1;
     187             : 
     188         364 :                 if (j == 0) n_spaces += n_extra;
     189             : 
     190         364 :                 if (n_spaces > 0) stream << legend_style << std::string(n_spaces, ' ') << rang::style::reset;
     191             : 
     192         364 :                 stream << legend_style << text + " " << rang::style::reset;
     193             : 
     194         364 :                 if (j < (cols_groups[i]->second.size() - 1)) // print column separator except for last
     195         252 :                     stream << legend_style << col_separator << rang::style::reset;
     196             :             }
     197             : 
     198         112 :             if (i < (cols_groups.size() - 1)) // print group separator except for last
     199          84 :                 stream << legend_style << group_separator << rang::style::reset;
     200             :         }
     201          28 :         stream << std::endl;
     202             :     }
     203             : 
     204             :     // print line 8 of the table
     205          14 :     stream << rang::tag::comment;
     206          70 :     for (unsigned i = 0; i < cols_groups.size(); i++)
     207             :     {
     208          56 :         const unsigned group_width = get_group_width(*cols_groups[i]);
     209          56 :         const auto n_extra = extra_spaces(cols_groups[i]->first, group_width);
     210             : 
     211         238 :         for (unsigned j = 0; j < cols_groups[i]->second.size(); j++)
     212             :         {
     213             :             auto n_separators =
     214         182 :               (std::get<2>(cols_groups[i]->second[j])) ? std::get<2>(cols_groups[i]->second[j]) : def_column_width;
     215         182 :             if (j == 0) n_separators += n_extra;
     216             : 
     217         182 :             stream << legend_style << std::string(n_separators, line_separator) << rang::style::reset;
     218         182 :             if (j < (cols_groups[i]->second.size() - 1)) // print column separator except for last
     219         126 :                 stream << legend_style << col_separator << rang::style::reset;
     220             :         }
     221             : 
     222          56 :         if (i < (cols_groups.size() - 1)) // print group separator except for last
     223          42 :             stream << legend_style << group_separator << rang::style::reset;
     224             :     }
     225          14 :     stream << std::endl;
     226             : 
     227          14 :     stream.flags(f);
     228          14 : }
     229             : 
     230             : void
     231           0 : Terminal_std::report(std::ostream& stream, bool final)
     232             : {
     233           0 :     std::ios::fmtflags f(stream.flags());
     234             : 
     235           0 :     stream << data_tag;
     236             : 
     237           0 :     for (unsigned r = 0; r < this->reporters.size(); r++)
     238           0 :         if (this->reporters[r] != nullptr)
     239             :         {
     240           0 :             auto report = this->reporters[r]->report(final);
     241           0 :             auto& groups = this->reporters[r]->get_groups();
     242             : 
     243           0 :             assert(report.size() == groups.size());
     244             : 
     245           0 :             for (unsigned g = 0; g < groups.size(); g++)
     246             :             {
     247           0 :                 assert(report[g].size() == groups[g].second.size());
     248             : 
     249           0 :                 stream << report_style << std::string(extra_spaces(groups[g]), ' ') << rang::style::reset;
     250             : 
     251           0 :                 for (unsigned c = 0; c < report[g].size(); c++)
     252             :                 {
     253           0 :                     auto& text = report[g][c];
     254             : 
     255             :                     size_t column_with =
     256           0 :                       std::get<2>(groups[g].second[c]) ? std::get<2>(groups[g].second[c]) : def_column_width;
     257           0 :                     if (text.size() < (size_t)column_with)
     258             :                     {
     259           0 :                         text += " ";
     260           0 :                         text.insert(0, column_with - text.size(), ' ');
     261             :                     }
     262             : 
     263           0 :                     stream << text;
     264             : 
     265           0 :                     if (c != (report[g].size() - 1)) stream << report_style << col_separator << rang::style::reset;
     266             :                 }
     267             : 
     268           0 :                 if (g != (groups.size() - 1)) stream << report_style << group_separator << rang::style::reset;
     269             :             }
     270             : 
     271           0 :             if (r != (this->reporters.size() - 1)) stream << report_style << group_separator << rang::style::reset;
     272           0 :         }
     273             :         else
     274           0 :             throw tools::runtime_error(__FILE__, __LINE__, __func__, "'this->reporters' contains null pointer.");
     275             : 
     276           0 :     if (final)
     277             :     {
     278           0 :         stream << "  " << std::endl;
     279             :     }
     280             :     else
     281             :     {
     282           0 :         stream << rang::style::bold << rang::fg::green << (real_time_state++ < 2 ? " *" : "  ") << rang::style::reset;
     283           0 :         stream << "\r"; // put the carrier return character only on temporary reports
     284           0 :         real_time_state %= (uint8_t)4;
     285             :     }
     286             : 
     287           0 :     stream.flags(f);
     288           0 :     stream.flush();
     289           0 : }
     290             : 
     291             : // get extra spaces if text is too long for the given group width
     292             : unsigned
     293         980 : Terminal_std::extra_spaces(const Reporter::title_t& text, const unsigned group_width)
     294             : {
     295         980 :     const unsigned longest_text = (unsigned)std::max(std::get<0>(text).size(), std::get<1>(text).size());
     296         980 :     return (longest_text > group_width) ? longest_text - group_width : 0;
     297             : }
     298             : 
     299             : unsigned
     300         588 : Terminal_std::extra_spaces(const Reporter::group_t& group)
     301             : {
     302         588 :     return extra_spaces(group.first, get_group_width(group));
     303             : }
     304             : 
     305             : unsigned
     306        1036 : Terminal_std::get_group_width(const Reporter::group_t& group)
     307             : {
     308        1036 :     size_t group_width = 0;
     309        4403 :     for (size_t j = 0; j < group.second.size(); j++)
     310        3367 :         group_width += (std::get<2>(group.second[j]) ? std::get<2>(group.second[j]) : def_column_width) + 1;
     311        1036 :     return --group_width;
     312             : }

Generated by: LCOV version 1.14