Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-08-28 08:11:46

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   enum class Alignment { Left, Right, Center };
0036 
0037   struct Column {
0038     std::string header;
0039     std::string format;
0040     Alignment alignment;
0041     std::size_t width = 0;
0042   };
0043 
0044   /// Add a column with header, format string, and alignment
0045   /// @param header Column header text
0046   /// @param format Format string for the column values
0047   /// @param alignment Column alignment (default: Left)
0048   void addColumn(const std::string& header, const std::string& format,
0049                  Alignment alignment = Alignment::Left) {
0050     if (!m_rows.empty()) {
0051       throw std::runtime_error("Cannot add columns after rows have been added");
0052     }
0053     m_columns.push_back({header, format, alignment, header.length()});
0054   }
0055 
0056   /// Add a column with header, format string, and alignment as string
0057   /// @param header Column header text
0058   /// @param format Format string for the column values
0059   /// @param alignment String alignment: "left"/"l", "right"/"r", "center"/"c" (case insensitive)
0060   /// @throws std::invalid_argument if alignment string is not recognized
0061   void addColumn(const std::string& header, const std::string& format,
0062                  const std::string& alignment = "left") {
0063     addColumn(header, format, parseAlignment(alignment));
0064   }
0065 
0066   /// Set whether to include markdown alignment markers in output
0067   /// @param includeMarkers If true (default), includes markdown alignment markers
0068   void setMarkdownMode(bool includeMarkers) {
0069     m_includeMarkdownMarkers = includeMarkers;
0070   }
0071 
0072   /// Add a row with variable arguments matching the number of columns
0073   /// @throws std::runtime_error if argument count doesn't match column count
0074   template <typename... Args>
0075   void addRow(Args&&... args) {
0076     if (sizeof...(Args) != m_columns.size()) {
0077       throw std::runtime_error("Number of arguments (" +
0078                                std::to_string(sizeof...(Args)) +
0079                                ") does not match number of columns (" +
0080                                std::to_string(m_columns.size()) + ")");
0081     }
0082 
0083     std::vector<std::string> row;
0084     std::size_t colIndex = 0;
0085 
0086     auto addCell = [&](auto&& arg) {
0087       std::string formatted =
0088           std::vformat(m_columns[colIndex].format, std::make_format_args(arg));
0089       row.push_back(formatted);
0090       m_columns[colIndex].width =
0091           std::max(m_columns[colIndex].width, formatted.length());
0092       ++colIndex;
0093     };
0094 
0095     (addCell(args), ...);
0096 
0097     m_rows.push_back(std::move(row));
0098   }
0099 
0100   /// Generate the formatted table as a markdown string
0101   std::string toString() const {
0102     if (m_columns.empty()) {
0103       return "";
0104     }
0105 
0106     std::string result;
0107 
0108     // Build header row
0109     result += "|";
0110     for (std::size_t i = 0; i < m_columns.size(); ++i) {
0111       result += std::format(
0112           " {} |", formatAligned(m_columns[i].header, m_columns[i].width,
0113                                  m_columns[i].alignment));
0114     }
0115     result += "\n";
0116 
0117     // Build separator row
0118     result += "|";
0119     for (std::size_t i = 0; i < m_columns.size(); ++i) {
0120       std::size_t contentWidth = m_columns[i].width;
0121 
0122       if (m_includeMarkdownMarkers) {
0123         switch (m_columns[i].alignment) {
0124           case Alignment::Left:
0125             result += std::format(":{:-<{}}", "", contentWidth + 1);
0126             break;
0127           case Alignment::Right:
0128             result += std::format("{:-<{}}:", "", contentWidth + 1);
0129             break;
0130           case Alignment::Center:
0131             result += std::format(":{:-<{}}:", "", contentWidth);
0132             break;
0133         }
0134       } else {
0135         result += std::format("{:-<{}}", "", contentWidth + 2);
0136       }
0137       result += "|";
0138     }
0139     result += "\n";
0140 
0141     // Build data rows
0142     for (const auto& row : m_rows) {
0143       result += "|";
0144       for (std::size_t i = 0; i < row.size(); ++i) {
0145         result += std::format(" {} |", formatAligned(row[i], m_columns[i].width,
0146                                                      m_columns[i].alignment));
0147       }
0148       result += "\n";
0149     }
0150 
0151     return result;
0152   }
0153 
0154   /// Stream output operator for Table
0155   friend std::ostream& operator<<(std::ostream& os, const Table& table) {
0156     return os << table.toString();
0157   }
0158 
0159  private:
0160   /// Parse alignment string to enum
0161   /// @throws std::invalid_argument if alignment string is not recognized
0162   static Alignment parseAlignment(const std::string& alignment) {
0163     std::string lower = alignment;
0164     std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
0165 
0166     if (lower == "left" || lower == "l") {
0167       return Alignment::Left;
0168     } else if (lower == "right" || lower == "r") {
0169       return Alignment::Right;
0170     } else if (lower == "center" || lower == "c") {
0171       return Alignment::Center;
0172     } else {
0173       throw std::invalid_argument(
0174           "Invalid alignment string: '" + alignment +
0175           "'. Valid options: 'left'/'l', 'right'/'r', 'center'/'c'");
0176     }
0177   }
0178 
0179   std::string formatAligned(const std::string& text, std::size_t width,
0180                             Alignment alignment) const {
0181     switch (alignment) {
0182       case Alignment::Left:
0183         return std::format("{:<{}}", text, width);
0184       case Alignment::Right:
0185         return std::format("{:>{}}", text, width);
0186       case Alignment::Center:
0187         return std::format("{:^{}}", text, width);
0188     }
0189     return text;
0190   }
0191 
0192   std::vector<Column> m_columns;
0193   std::vector<std::vector<std::string>> m_rows;
0194   bool m_includeMarkdownMarkers = true;
0195 };
0196 
0197 }  // namespace Acts