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!); } } }