using System.Text.Json;
using System.Text.Json.Serialization;
namespace ZB.MOM.WW.ScadaBridge.CLI;
public static class OutputFormatter
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
/// Serializes to indented JSON and writes it to standard output.
/// The object to serialize; null is serialized as JSON null.
public static void WriteJson(object? data)
{
Console.WriteLine(JsonSerializer.Serialize(data, JsonOptions));
}
/// Writes a JSON error envelope with the given message and code to standard error.
/// Human-readable error description.
/// Machine-readable error code.
public static void WriteError(string message, string code)
{
Console.Error.WriteLine(JsonSerializer.Serialize(new { error = message, code }, JsonOptions));
}
/// Writes a plain-text padded table to standard output with the given column headers and data rows.
/// Data rows; each inner array corresponds to a column in the same order as .
/// Column header labels.
public static void WriteTable(IEnumerable rows, string[] headers)
=> WriteTable(rows.Select(TableRow.Columns), headers);
///
/// One row for the overload of WriteTable: either padded
/// column cells like the plain overload, or an already-formatted
/// line printed verbatim with no column padding/alignment at all — used for a note/log line folded onto a step, which must render as a raw
/// indented line under that step's row rather than as a cell in a table column (every
/// other row's cell there would otherwise render blank, and the line's own leading
/// whitespace would be swallowed by column padding).
///
internal readonly struct TableRow
{
private TableRow(string[]? cells, string? rawLine)
{
Cells = cells;
RawLine = rawLine;
}
/// The column cells of a normal row, or null for a raw line.
internal string[]? Cells { get; }
/// The verbatim line of a raw row, or null for a column row.
internal string? RawLine { get; }
/// A normal row, rendered as padded columns aligned with the header set.
/// The column cell values.
/// A column row.
internal static TableRow Columns(string[] cells) => new(cells, null);
/// An already-formatted line, printed exactly as given with no column alignment.
/// The preformatted line.
/// A raw row.
internal static TableRow Raw(string line) => new(null, line);
}
///
/// Writes a plain-text padded table like ,
/// except a row is printed verbatim — unpadded, uninvolved in the
/// column-width computation — at its position in the row sequence, immediately after
/// whatever row precedes it.
///
/// Data rows, each either padded columns or a raw line.
/// Column header labels.
internal static void WriteTable(IEnumerable rows, string[] headers)
{
var rowList = rows.ToList();
var widths = new int[headers.Length];
void Consider(string[] row)
{
for (int i = 0; i < Math.Min(row.Length, widths.Length); i++)
widths[i] = Math.Max(widths[i], (row[i] ?? "").Length);
}
Consider(headers);
foreach (var row in rowList)
if (row.Cells != null)
Consider(row.Cells);
void PrintColumns(string[] row)
{
for (int i = 0; i < headers.Length; i++)
Console.Write((i < row.Length ? row[i] ?? "" : "").PadRight(widths[i] + 2));
Console.WriteLine();
}
PrintColumns(headers);
foreach (var row in rowList)
{
if (row.RawLine != null)
Console.WriteLine(row.RawLine);
else
PrintColumns(row.Cells!);
}
}
}