Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-14 09:39:51

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 
0011 #include <algorithm>
0012 #include <cctype>
0013 #include <format>
0014 #include <ostream>
0015 #include <stdexcept>
0016 #include <string>
0017 #include <vector>
0018 
0019 namespace Acts {
0020 
0021 /// A utility class for creating formatted markdown tables with automatic
0022 /// column sizing and alignment.
0023 ///
0024 /// Usage:
0025 /// ```cpp
0026 /// Table table;
0027 /// table.addColumn("Name", "{}", "left");        // String alignment
0028 /// table.addColumn("Value", "{:.2f}", "right");  // or "r" for short
0029 /// table.addColumn("Count", "{}", Table::Alignment::Center);  // Enum also
0030 /// supported table.addRow("Item1", 1.23, 42); table.addRow("Item2", 4.56, 7);
0031 /// std::cout << table.toString();
0032 /// ```
0033 class Table {
0034  public:
0035   /// Text alignment options for table columns.
0036   enum class Alignment { Left, Right, Center };
0037 
0038   struct Column {
0039     std::string header;
0040     std::string format;
0041     Alignment alignment;
0042     std::size_t width = 0;
0043   };
0044 
0045   /// Add a column with header, format string, and alignment
0046   /// @param header Column header text
0047   /// @param format Format string for the column values
0048   /// @param alignment Column alignment (default: Left)
0049   void addColumn(const std::string& header, const std::string& format,
0050                  Alignment alignment = Alignment::Left) {
0051     if (!m_rows.empty()) {
0052       throw std::runtime_error("Cannot add columns after rows have been added");
0053     }
0054     m_columns.push_back({header, format, alignment, header.length()});
0055   }
0056 
0057   /// Add a column with header, format string, and alignment as string
0058   /// @param header Column header text
0059   /// @param format Format string for the column values
0060   /// @param alignment String alignment: "left"/"l", "right"/"r", "center"/"c" (case insensitive)
0061   /// @throws std::invalid_argument if alignment string is not recognized
0062   void addColumn(const std::string& header, const std::string& format,
0063                  const std::string& alignment = "left") {
0064     addColumn(header, format, parseAlignment(alignment));
0065   }
0066 
0067   /// Set whether to include markdown alignment markers in output
0068   /// @param includeMarkers If true (default), includes markdown alignment markers
0069   void setMarkdownMode(bool includeMarkers) {
0070     m_includeMarkdownMarkers = includeMarkers;
0071   }
0072 
0073   /// Add a row with variable arguments matching the number of columns
0074   /// @param args Arguments to add as row values
0075   /// @throws std::runtime_error if argument count doesn't match column count
0076   template <typename... Args>
0077   void addRow(Args&&... args) {
0078     if (sizeof...(Args) != m_columns.size()) {
0079       throw std::runtime_error("Number of arguments (" +
0080                                std::to_string(sizeof...(Args)) +
0081                                ") does not match number of columns (" +
0082                                std::to_string(m_columns.size()) + ")");
0083     }
0084 
0085     std::vector<std::string> row;
0086     std::size_t colIndex = 0;
0087 
0088     auto addCell = [&](auto&& arg) {
0089       std::string formatted =
0090           std::vformat(m_columns[colIndex].format, std::make_format_args(arg));
0091       row.push_back(formatted);
0092       m_columns[colIndex].width =
0093           std::max(m_columns[colIndex].width, formatted.length());
0094       ++colIndex;
0095     };
0096 
0097     (addCell(args), ...);
0098 
0099     m_rows.push_back(std::move(row));
0100   }
0101 
0102   /// Generate the formatted table as a markdown string
0103   /// @return Markdown formatted string representation of the table
0104   std::string toString() const {
0105     if (m_columns.empty()) {
0106       return "";
0107     }
0108 
0109     std::string result;
0110 
0111     // Build header row
0112     result += "|";
0113     for (std::size_t i = 0; i < m_columns.size(); ++i) {
0114       result += std::format(
0115           " {} |", formatAligned(m_columns[i].header, m_columns[i].width,
0116                                  m_columns[i].alignment));
0117     }
0118     result += "\n";
0119 
0120     // Build separator row
0121     result += "|";
0122     for (std::size_t i = 0; i < m_columns.size(); ++i) {
0123       std::size_t contentWidth = m_columns[i].width;
0124 
0125       if (m_includeMarkdownMarkers) {
0126         switch (m_columns[i].alignment) {
0127           case Alignment::Left:
0128             result += std::format(":{:-<{}}", "", contentWidth + 1);
0129             break;
0130           case Alignment::Right:
0131             result += std::format("{:-<{}}:", "", contentWidth + 1);
0132             break;
0133           case Alignment::Center:
0134             result += std::format(":{:-<{}}:", "", contentWidth);
0135             break;
0136         }
0137       } else {
0138         result += std::format("{:-<{}}", "", contentWidth + 2);
0139       }
0140       result += "|";
0141     }
0142     result += "\n";
0143 
0144     // Build data rows
0145     for (const auto& row : m_rows) {
0146       result += "|";
0147       for (std::size_t i = 0; i < row.size(); ++i) {
0148         result += std::format(" {} |", formatAligned(row[i], m_columns[i].width,
0149                                                      m_columns[i].alignment));
0150       }
0151       result += "\n";
0152     }
0153 
0154     return result;
0155   }
0156 
0157   /// Stream output operator for Table
0158   friend std::ostream& operator<<(std::ostream& os, const Table& table) {
0159     return os << table.toString();
0160   }
0161 
0162  private:
0163   /// Parse alignment string to enum
0164   /// @throws std::invalid_argument if alignment string is not recognized
0165   static Alignment parseAlignment(const std::string& alignment) {
0166     std::string lower = alignment;
0167     std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
0168 
0169     if (lower == "left" || lower == "l") {
0170       return Alignment::Left;
0171     } else if (lower == "right" || lower == "r") {
0172       return Alignment::Right;
0173     } else if (lower == "center" || lower == "c") {
0174       return Alignment::Center;
0175     } else {
0176       throw std::invalid_argument(
0177           "Invalid alignment string: '" + alignment +
0178           "'. Valid options: 'left'/'l', 'right'/'r', 'center'/'c'");
0179     }
0180   }
0181 
0182   std::string formatAligned(const std::string& text, std::size_t width,
0183                             Alignment alignment) const {
0184     switch (alignment) {
0185       case Alignment::Left:
0186         return std::format("{:<{}}", text, width);
0187       case Alignment::Right:
0188         return std::format("{:>{}}", text, width);
0189       case Alignment::Center:
0190         return std::format("{:^{}}", text, width);
0191     }
0192     return text;
0193   }
0194 
0195   std::vector<Column> m_columns;
0196   std::vector<std::vector<std::string>> m_rows;
0197   bool m_includeMarkdownMarkers = true;
0198 };
0199 
0200 }  // namespace Acts