< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Logging.Utils.Console.Table
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Logging/Utils/Console/Table.cs
Tag: Kestrun/Kestrun@9d3a582b2d63930269564a7591aa77ef297cadeb
Line coverage
78%
Covered lines: 51
Uncovered lines: 14
Coverable lines: 65
Total lines: 236
Line coverage: 78.4%
Branch coverage
69%
Covered branches: 32
Total branches: 46
Branch coverage: 69.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Rows()100%11100%
get_Columns()100%11100%
get_HeaderSet()100%210%
get_Padding()100%11100%
.ctor()100%210%
.ctor(...)100%11100%
SetHeader(...)100%210%
AddRow(...)14.28%971425%
CalculateColumns()87.5%88100%
Render()100%66100%
RenderWithoutGrid()100%44100%
RenderGrid(...)100%88100%
RenderGridLine(...)83.33%66100%
ToString()100%11100%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Logging/Utils/Console/Table.cs

#LineLine coverage
 1using System.Text;
 2
 3namespace Kestrun.Logging.Utils.Console;
 4
 5/// <summary>
 6/// Represents a console table for formatted output with optional grid and padding.
 7/// </summary>
 8public class Table
 9{
 10    /// <summary>
 11    /// Represents the top-left joint character for the table grid.
 12    /// </summary>
 13    public const string TOP_LEFT_JOINT = "┌";
 14    /// <summary>
 15    /// Represents the top-right joint character for the table grid.
 16    /// </summary>
 17    public const string TOP_RIGHT_JOINT = "┐";
 18    /// <summary>
 19    /// Represents the bottom-left joint character for the table grid.
 20    /// </summary>
 21    public const string BOTTOM_LEFT_JOINT = "└";
 22    /// <summary>
 23    /// Represents the bottom-right joint character for the table grid.
 24    /// </summary>
 25    public const string BOTTOM_RIGHT_JOINT = "┘";
 26    /// <summary>
 27    /// Represents the top joint character for the table grid.
 28    /// </summary>
 29    public const string TOP_JOINT = "┬";
 30    /// <summary>
 31    /// Represents the bottom joint character for the table grid.
 32    /// </summary>
 33    public const string BOTTOM_JOINT = "┴";
 34    /// <summary>
 35    /// Represents the left joint character for the table grid.
 36    /// </summary>
 37    public const string LEFT_JOINT = "├";
 38    /// <summary>
 39    /// Represents the middle joint character for the table grid.
 40    /// </summary>
 41    public const string MIDDLE_JOINT = "┼";
 42    /// <summary>
 43    /// Represents the right joint character for the table grid.
 44    /// </summary>
 45    public const string RIGHT_JOINT = "┤";
 46    /// <summary>
 47    /// Represents the horizontal line character for the table grid.
 48    /// </summary>
 49    public const char HORIZONTAL_LINE = '─';
 50    /// <summary>
 51    /// Represents the vertical line character for the table grid.
 52    /// </summary>
 53    public const string VERTICAL_LINE = "│";
 54
 4455    private List<Row> Rows { get; } = [];
 11956    private List<Column> Columns { get; set; } = [];
 57
 58    /// <summary>
 59    /// Gets or sets a value indicating whether the table header has been set.
 60    /// </summary>
 061    public bool HeaderSet { get; set; }
 62    /// <summary>
 63    /// Gets the padding configuration for the table cells.
 64    /// </summary>
 11165    public Padding Padding { get; } = new Padding(0);
 66
 67    private int _rowIndex;
 68
 69    /// <summary>
 70    /// Initializes a new instance of the <see cref="Table"/> class with default padding.
 71    /// </summary>
 072    public Table() => Columns = [];
 73
 74    /// <summary>
 75    /// Initializes a new instance of the <see cref="Table"/> class with the specified padding.
 76    /// </summary>
 77    /// <param name="padding">The padding configuration for the table cells.</param>
 378    public Table(Padding padding)
 79    {
 380        Padding = padding;
 381        Columns = [];
 382    }
 83
 84    /// <summary>
 85    /// Sets the header row for the table using the specified header values.
 86    /// </summary>
 87    /// <param name="headers">An array of header strings to be used as the table's header row.</param>
 88    public void SetHeader(params string[] headers)
 89    {
 090        HeaderSet = true;
 091        Rows.Add(new Row(_rowIndex++, true, headers));
 092    }
 93
 94    /// <summary>
 95    /// Adds a row to the table with the specified cell values.
 96    /// Handles multiline cell values by splitting them into multiple rows.
 97    /// </summary>
 98    /// <param name="values">An array of objects representing the cell values for the row.</param>
 99    public void AddRow(params object[] values)
 100    {
 101        // Only consider string values for multiline handling to avoid heavy ToString() on complex objects
 39102        if (values.Any(v => v is string s && s.Contains(Environment.NewLine)))
 103        {
 0104            var maxLines = values.Max(v => v is string s ? s.Split('\n').Length : 1);
 0105            for (var i = 0; i < maxLines; i++)
 106            {
 0107                var row = new List<object>();
 0108                foreach (var value in values)
 109                {
 0110                    if (value is string s)
 111                    {
 0112                        var valueLines = s.Split('\n');
 0113                        row.Add(i < valueLines.Length ? valueLines[i].Replace("\r", "") : string.Empty);
 114                    }
 115                    else
 116                    {
 117                        // Non-strings are treated as single-line values
 0118                        row.Add(i == 0 ? value : string.Empty);
 119                    }
 120                }
 121
 0122                Rows.Add(new Row(_rowIndex++, false, row.ToArray()) { DisableTopGrid = i > 0 });
 123            }
 124        }
 125        else
 126        {
 127            // Add a single row (fixes previous accidental recursion)
 13128            Rows.Add(new Row(_rowIndex++, false, values));
 129        }
 13130    }
 131
 132    private void CalculateColumns()
 133    {
 3134        Columns = [];
 32135        foreach (var row in Rows)
 136        {
 78137            foreach (var cell in row.Cells)
 138            {
 26139                if (row.Index == 0)
 140                {
 6141                    Columns.Add(new Column(cell));
 142                }
 143                else
 144                {
 60145                    Columns.SingleOrDefault(c => c.Index == cell.Index)?.AddCell(cell);
 146                }
 147            }
 148        }
 3149    }
 150
 151    /// <summary>
 152    /// Renders the table as a formatted string with grid lines.
 153    /// </summary>
 154    /// <returns>A string representing the formatted table with grid.</returns>
 155    public string Render()
 156    {
 2157        var sb = new StringBuilder();
 2158        CalculateColumns();
 159
 28160        foreach (var row in Rows)
 161        {
 162            // Don't render grid if row is multiline
 12163            if (!row.DisableTopGrid)
 164            {
 12165                RenderGrid(sb, row.Index);
 166            }
 167
 72168            foreach (var cell in row.Cells)
 169            {
 24170                _ = sb.Append($"{VERTICAL_LINE}{Padding.LeftString()}{cell}{Padding.RightString()}");
 171            }
 12172            _ = sb.AppendLine(VERTICAL_LINE);
 173
 12174            RenderGrid(sb, row.Index, false);
 175        }
 176
 2177        return sb.ToString();
 178    }
 179
 180    /// <summary>
 181    /// Renders the table as a formatted string without grid lines.
 182    /// </summary>
 183    /// <returns>A string representing the formatted table without grid.</returns>
 184    public string RenderWithoutGrid()
 185    {
 1186        var sb = new StringBuilder();
 1187        CalculateColumns();
 188
 4189        foreach (var row in Rows)
 190        {
 6191            foreach (var cell in row.Cells)
 192            {
 2193                _ = sb.Append($"{Padding.LeftString()}{cell}{Padding.RightString()}");
 194            }
 1195            _ = sb.AppendLine();
 196        }
 197
 1198        return sb.ToString();
 199    }
 200
 201    private void RenderGrid(StringBuilder sb, int rowIndex, bool preRender = true)
 202    {
 24203        if (rowIndex == 0 && preRender) // First line
 204        {
 2205            RenderGridLine(sb, TOP_LEFT_JOINT, TOP_JOINT, TOP_RIGHT_JOINT);
 206        }
 22207        else if (rowIndex == Rows.Count - 1 && !preRender)  // Last line
 208        {
 2209            RenderGridLine(sb, BOTTOM_LEFT_JOINT, BOTTOM_JOINT, BOTTOM_RIGHT_JOINT);
 210        }
 20211        else if (preRender) // Middle line
 212        {
 10213            RenderGridLine(sb, LEFT_JOINT, MIDDLE_JOINT, RIGHT_JOINT);
 214        }
 20215    }
 216
 217    private void RenderGridLine(StringBuilder sb, string leftJoint, string middleJoint, string rightJoint)
 218    {
 84219        for (var i = 0; i < Columns.Count; i++)
 220        {
 28221            var columnWidth = Columns[i].MaxWidth + Padding.Right + Padding.Left;
 28222            _ = i == 0
 28223                ? sb.Append(leftJoint + string.Empty.PadLeft(columnWidth, HORIZONTAL_LINE) + middleJoint)
 28224                : i == Columns.Count - 1
 28225                    ? sb.Append(string.Empty.PadLeft(columnWidth, HORIZONTAL_LINE) + rightJoint)
 28226                    : sb.Append(string.Empty.PadLeft(columnWidth, HORIZONTAL_LINE) + middleJoint);
 227        }
 14228        _ = sb.AppendLine();
 14229    }
 230
 231    /// <summary>
 232    /// Returns a string that represents the current table.
 233    /// </summary>
 234    /// <returns>A string representation of the table.</returns>
 2235    public override string ToString() => Render();
 236}