using System.Text.Json; using System.Text.Json.Serialization; using StellaOps.Bench.ScannerAnalyzers.Baseline; namespace StellaOps.Bench.ScannerAnalyzers.Reporting; internal static class BenchmarkJsonWriter { private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web) { WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; public static async Task WriteAsync( string path, BenchmarkJsonMetadata metadata, IReadOnlyList reports, CancellationToken cancellationToken) { ArgumentException.ThrowIfNullOrWhiteSpace(path); ArgumentNullException.ThrowIfNull(metadata); ArgumentNullException.ThrowIfNull(reports); var resolved = Path.GetFullPath(path); var directory = Path.GetDirectoryName(resolved); if (!string.IsNullOrEmpty(directory)) { Directory.CreateDirectory(directory); } var document = new BenchmarkJsonDocument( metadata.SchemaVersion, metadata.CapturedAtUtc, metadata.Commit, metadata.Environment, reports.Select(CreateScenario).ToArray()); await using var stream = new FileStream(resolved, FileMode.Create, FileAccess.Write, FileShare.None); await JsonSerializer.SerializeAsync(stream, document, SerializerOptions, cancellationToken).ConfigureAwait(false); await stream.FlushAsync(cancellationToken).ConfigureAwait(false); } private static BenchmarkJsonScenario CreateScenario(BenchmarkScenarioReport report) { var baseline = report.Baseline; return new BenchmarkJsonScenario( report.Result.Id, report.Result.Label, report.Result.Iterations, report.Result.SampleCount, report.Result.MeanMs, report.Result.P95Ms, report.Result.MaxMs, report.Result.ThresholdMs, baseline is null ? null : new BenchmarkJsonScenarioBaseline( baseline.Iterations, baseline.SampleCount, baseline.MeanMs, baseline.P95Ms, baseline.MaxMs), new BenchmarkJsonScenarioRegression( report.MaxRegressionRatio, report.MeanRegressionRatio, report.RegressionLimit, report.RegressionBreached)); } private sealed record BenchmarkJsonDocument( string SchemaVersion, DateTimeOffset CapturedAt, string? Commit, string? Environment, IReadOnlyList Scenarios); private sealed record BenchmarkJsonScenario( string Id, string Label, int Iterations, int SampleCount, double MeanMs, double P95Ms, double MaxMs, double ThresholdMs, BenchmarkJsonScenarioBaseline? Baseline, BenchmarkJsonScenarioRegression Regression); private sealed record BenchmarkJsonScenarioBaseline( int Iterations, int SampleCount, double MeanMs, double P95Ms, double MaxMs); private sealed record BenchmarkJsonScenarioRegression( double? MaxRatio, double? MeanRatio, double Limit, bool Breached); } internal sealed record BenchmarkJsonMetadata( string SchemaVersion, DateTimeOffset CapturedAtUtc, string? Commit, string? Environment);