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 : }