diff --git a/src/servers.cpp b/src/servers.cpp index dbd6b27..fdd567e 100644 --- a/src/servers.cpp +++ b/src/servers.cpp @@ -65,7 +65,7 @@ void list_servers() { if (ss.init(server.name, service)) { if (ss.is_healthy()) - serviceticks += ":tick: "; + serviceticks += ":tick: :tick: :cross: "; else serviceticks += ":cross: "; } diff --git a/src/tableprint.cpp b/src/tableprint.cpp index 83c2c58..eb81f65 100644 --- a/src/tableprint.cpp +++ b/src/tableprint.cpp @@ -7,36 +7,81 @@ #include #include #include +#include -// Helper function to replace all occurrences of a substring in a regular string -std::wstring replace_all(const std::string& str, const std::string& from, const std::wstring& to) { - std::wstring result; - size_t start_pos = 0; - size_t find_pos; - - while ((find_pos = str.find(from, start_pos)) != std::string::npos) { - result += std::wstring(str.begin() + start_pos, str.begin() + find_pos); - result += to; - start_pos = find_pos + from.length(); +enum kTextColors { + kTextColor_Default, + kTextColor_Red, + kTextColor_Green, + kTextColor_Yellow, + kTextColor_Blue, + kTextColor_Magenta, + kTextColor_Cyan +}; + +struct coloredText { + std::string text; + kTextColors color; +}; + +const std::map kReplacements = { + {":tick:", {"✓", kTextColor_Green}}, + {":cross:", {"✗", kTextColor_Red}}, + {":warning:", {"⚠️", kTextColor_Yellow}}, + {":info:", {"ℹ️", kTextColor_Blue}}, + {":check:", {"✅", kTextColor_Green}}, + {":x:", {"❌", kTextColor_Red}} +}; + +// Helper function to get ANSI color code +std::string get_color_code(kTextColors color) { + switch (color) { + case kTextColor_Red: return "\033[1;31m"; + case kTextColor_Green: return "\033[1;32m"; + case kTextColor_Yellow: return "\033[1;33m"; + case kTextColor_Blue: return "\033[1;34m"; + case kTextColor_Magenta: return "\033[1;35m"; + case kTextColor_Cyan: return "\033[1;36m"; + default: return "\033[0m"; + } +} + +// Helper function to process a cell with replacements +std::string process_cell(const std::string& cell) { + std::string result; + size_t pos = 0; + while (pos < cell.length()) { + bool found_replacement = false; + for (const auto& [key, value] : kReplacements) { + if (cell.compare(pos, key.length(), key) == 0) { + result += get_color_code(value.color) + value.text + "\033[0m"; + pos += key.length(); + found_replacement = true; + break; + } + } + if (!found_replacement) { + result += cell[pos]; + pos++; + } } - result += std::wstring(str.begin() + start_pos, str.end()); return result; } -// Helper function to replace all occurrences of a substring in a wide string -std::wstring replace_all(const std::wstring& str, const std::string& from, const std::wstring& to) { - std::wstring result; - size_t start_pos = 0; - size_t find_pos; - std::wstring from_w(from.begin(), from.end()); - - while ((find_pos = str.find(from_w, start_pos)) != std::wstring::npos) { - result += str.substr(start_pos, find_pos - start_pos); - result += to; - start_pos = find_pos + from_w.length(); +// Helper function to get visible length (ignoring color codes) +size_t get_visible_length(const std::string& str) { + size_t length = 0; + bool in_color_code = false; + for (size_t i = 0; i < str.length(); ++i) { + if (str[i] == '\033') { + while (i < str.length() && str[i] != 'm') { + i++; + } + } else { + length++; + } } - result += str.substr(start_pos); - return result; + return length; } tableprint::tableprint(const std::string title) : title(title) { @@ -51,24 +96,48 @@ void tableprint::set_title(const std::string title) { } void tableprint::add_row(const std::vector& row) { - rows.push_back(row); + std::vector trimmed_row; + for (const auto& cell : row) { + // Trim whitespace from start and end + auto start = cell.find_first_not_of(" \t"); + auto end = cell.find_last_not_of(" \t"); + if (start == std::string::npos) { + trimmed_row.push_back(""); + } else { + trimmed_row.push_back(cell.substr(start, end - start + 1)); + } + } + rows.push_back(trimmed_row); } void tableprint::print() { if (rows.empty()) return; - // Calculate column widths - std::vector col_widths(rows[0].size(), 0); + // First, process all cells and store the processed versions + std::vector> processed_rows; for (const auto& row : rows) { + std::vector processed_row; + for (const auto& cell : row) { + processed_row.push_back(process_cell(cell)); + } + processed_rows.push_back(processed_row); + } + + // Calculate column widths based on processed cells (ignoring color codes) + std::vector col_widths(rows[0].size(), 0); + for (const auto& row : processed_rows) { for (size_t i = 0; i < row.size(); ++i) { - std::string cell = row[i]; - // Count occurrences of :tick: and :cross: - size_t tick_count = std::count(cell.begin(), cell.end(), ':') / 2; - // Adjust width: original length minus 5 for each :tick: or :cross: (since they become 1 char) - col_widths[i] = std::max(col_widths[i], cell.length() - (tick_count * 5)); + col_widths[i] = std::max(col_widths[i], get_visible_length(row[i])); } } + // Debug output + std::cerr << "Column widths: "; + for (size_t width : col_widths) { + std::cerr << width << " "; + } + std::cerr << std::endl; + // Calculate total table width size_t total_width = 0; for (size_t width : col_widths) { @@ -80,29 +149,27 @@ void tableprint::print() { if (!title.empty()) { std::cout << "\033[90m"; // Dark grey color for borders std::cout << "┌"; - for (size_t i = 0; i < total_width; ++i) { - std::cout << "-"; + for (size_t i = 0; i < rows[0].size(); ++i) { + std::cout << std::string(col_widths[i] + 2, '-'); + if (i < rows[0].size() - 1) std::cout << "┬"; } std::cout << "┐" << std::endl; std::cout << "│" << "\033[1;37m"; // White color for title - size_t padding = (total_width - title.length()) / 2; - for (size_t i = 0; i < padding; ++i) { - std::cout << " "; - } - std::cout << title; - for (size_t i = 0; i < total_width - title.length() - padding; ++i) { - std::cout << " "; + size_t title_width = 0; + for (size_t width : col_widths) { + title_width += width + 2; // +2 for padding } + title_width += col_widths.size() - 1; // Add space for vertical borders + size_t padding = (title_width - title.length()) / 2; + std::cout << std::string(padding, ' ') << title << std::string(title_width - title.length() - padding, ' '); std::cout << "\033[90m│" << std::endl; // Use └─┴─┘ for bottom of title box to connect with table std::cout << "├"; for (size_t i = 0; i < rows[0].size(); ++i) { - for (size_t j = 0; j < col_widths[i] + 2; ++j) { - std::cout << "-"; - } - if (i < rows[0].size() - 1) std::cout << "┬"; + std::cout << std::string(col_widths[i] + 2, '-'); + if (i < rows[0].size() - 1) std::cout << "┼"; } std::cout << "┤" << std::endl; } else { @@ -110,9 +177,7 @@ void tableprint::print() { std::cout << "\033[90m"; // Dark grey color for borders std::cout << "┌"; for (size_t i = 0; i < rows[0].size(); ++i) { - for (size_t j = 0; j < col_widths[i] + 2; ++j) { - std::cout << "-"; - } + std::cout << std::string(col_widths[i] + 2, '-'); if (i < rows[0].size() - 1) std::cout << "┬"; } std::cout << "┐" << std::endl; @@ -121,9 +186,9 @@ void tableprint::print() { // Print header std::cout << "│"; std::cout << "\033[1;36m"; // Cyan color for header - for (size_t i = 0; i < rows[0].size(); ++i) { - std::cout << " " << std::setw(col_widths[i]) << std::left << rows[0][i] << " "; - if (i < rows[0].size() - 1) { + for (size_t i = 0; i < processed_rows[0].size(); ++i) { + std::cout << " " << std::setw(col_widths[i]) << std::left << processed_rows[0][i] << " "; + if (i < processed_rows[0].size() - 1) { std::cout << "\033[90m│\033[1;36m"; // Border color then back to cyan } else { std::cout << "\033[90m│"; // Just border color for last column @@ -142,50 +207,21 @@ void tableprint::print() { std::cout << "┤" << std::endl; // Print rows - for (size_t row_idx = 1; row_idx < rows.size(); ++row_idx) { - const auto& row = rows[row_idx]; + for (size_t row_idx = 1; row_idx < processed_rows.size(); ++row_idx) { + const auto& row = processed_rows[row_idx]; std::cout << "│"; for (size_t i = 0; i < row.size(); ++i) { - std::string cell = row[i]; - // Replace :tick: and :cross: with colored symbols - std::string processed_cell; - size_t pos = 0; - while (pos < cell.length()) { - if (cell.compare(pos, 6, ":tick:") == 0) { - processed_cell += "\033[1;32m✓\033[0m"; - pos += 6; - } else if (cell.compare(pos, 7, ":cross:") == 0) { - processed_cell += "\033[1;31m✗\033[0m"; - pos += 7; - } else { - processed_cell += cell[pos]; - pos++; - } - } - // Set the appropriate color for the row if (row_idx % 2 == 1) { - std::cout << " " << "\033[38;5;142m"; - if (cell.find(":tick:") != std::string::npos || cell.find(":cross:") != std::string::npos) { - std::cout << " " << processed_cell << " "; - } else { - std::cout << std::setw(col_widths[i]) << std::left << processed_cell; - } - std::cout << "\033[90m" << " │"; + std::cout << " " << "\033[38;5;142m" << std::setw(col_widths[i]) << std::left << row[i] << "\033[90m" << " │"; } else { - std::cout << " " << "\033[38;5;250m"; - if (cell.find(":tick:") != std::string::npos || cell.find(":cross:") != std::string::npos) { - std::cout << " " << processed_cell << " "; - } else { - std::cout << std::setw(col_widths[i]) << std::left << processed_cell; - } - std::cout << "\033[90m" << " │"; + std::cout << " " << "\033[38;5;250m" << std::setw(col_widths[i]) << std::left << row[i] << "\033[90m" << " │"; } } std::cout << std::endl; // Print row separator if not the last row - if (row_idx < rows.size() - 1) { + if (row_idx < processed_rows.size() - 1) { std::cout << "├"; for (size_t i = 0; i < rows[0].size(); ++i) { for (size_t j = 0; j < col_widths[i] + 2; ++j) {