| File: Helpers\ConsoleTable.cs | Web Access |
| Project: src\aspnetcore\src\Tools\dotnet-user-jwts\src\dotnet-user-jwts.csproj (dotnet-user-jwts) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Data; using System.Globalization; using System.Linq; using System.Text; using Microsoft.Extensions.Tools.Internal; namespace Microsoft.Extensions.CommandLineUtils; internal sealed class ConsoleTable { private readonly List<string> _columns = new(); private readonly List<string[]> _rows = new(); private readonly IReporter _reporter; public ConsoleTable(IReporter reporter) { _reporter = reporter; } public void AddColumns(params string[] names) { _columns.AddRange(names); } public void AddRow(params string[] values) { ArgumentNullException.ThrowIfNull(values); if (!_columns.Any()) { throw new InvalidOperationException("Columns must be set before rows can be added."); } if (_columns.Count != values.Length) { throw new InvalidOperationException( $"The number of columns in the table '{_columns.Count}' does not match the number of columns in the row '{values.Length}'."); } _rows.Add(values); } public void Write() { var builder = new StringBuilder(); var maxColumnLengths = _columns .Select((t, i) => _rows.Select(x => x[i]) .Concat(new[] { _columns[i] }) .Where(x => x != null) .Select(x => x!.ToString()!.Length).Max()) .ToList(); // The table borders constructed using "|" have whitespaces before and after. // This number accounts for those spaces to ensure that the table width is not longer than the console window's width. var EXCESS_LENGTH_CREATED_BY_BORDERS = 4; var equalColumnLengths = Math.Max((Console.WindowWidth / _columns.Count) - EXCESS_LENGTH_CREATED_BY_BORDERS, 5); var excessLength = 0; var numberOfColumnsThatNeedMoreLength = 0; // Keep track of the excess length left behind by narrow columns and the number of columns that could use the extra length. for (var i = 0; i < maxColumnLengths.Count; i++) { if (maxColumnLengths[i] < equalColumnLengths) { excessLength += equalColumnLengths - maxColumnLengths[i]; } else { numberOfColumnsThatNeedMoreLength += 1; maxColumnLengths[i] = equalColumnLengths; } } // Share the excess length amongst the columns that could use it. for (var i = 0; i < maxColumnLengths.Count; i++) { if (maxColumnLengths[i] == equalColumnLengths) { maxColumnLengths[i] += excessLength / numberOfColumnsThatNeedMoreLength; } } var formatRow = Enumerable.Range(0, _columns.Count) .Select(i => " | {" + i + ", " + maxColumnLengths[i] + "}") .Aggregate((previousRowColumn, nextRowColumn) => previousRowColumn + nextRowColumn) + " |"; var columnHeaders = string.Format(CultureInfo.InvariantCulture, formatRow, _columns.ToArray()); var rowDivider = $" {new string('-', columnHeaders.Length - 1)} "; builder.AppendLine(rowDivider); builder.AppendLine(columnHeaders); WriteTableContent(_rows, maxColumnLengths, rowDivider, builder); void WriteTableContent(List<string[]> rows, List<int> columnLengths, string divider, StringBuilder stringBuilder) { stringBuilder.AppendLine(divider); for (var i = 0; i < rows.Count; i++) { while (!rows[i].All(i => i == string.Empty)) { var outputRow = string.Empty; for (var j = 0; j < rows[i].Length; j++) { outputRow = string.Concat(outputRow, " | "); if (rows[i][j].Length <= columnLengths[j]) { outputRow = string.Concat(outputRow, rows[i][j], new string(' ', columnLengths[j] - rows[i][j].Length)); rows[i][j] = string.Empty; } else { outputRow = string.Concat(outputRow, rows[i][j].Substring(0, columnLengths[j])); rows[i][j] = rows[i][j].Substring(columnLengths[j]); } } outputRow = string.Concat(outputRow, " |"); stringBuilder.AppendLine(outputRow); } stringBuilder.AppendLine(divider); } _reporter.Output(builder.ToString()); } } }