| | 1 | | // Copyright DotNet API Diff Project Contributors - SPDX Identifier: MIT |
| | 2 | | using DotNetApiDiff.Interfaces; |
| | 3 | | using DotNetApiDiff.Models; |
| | 4 | | using System.Text.Json; |
| | 5 | | using System.Text.Json.Serialization; |
| | 6 | |
|
| | 7 | | namespace DotNetApiDiff.Reporting; |
| | 8 | |
|
| | 9 | | /// <summary> |
| | 10 | | /// Formatter for JSON output with complete comparison details |
| | 11 | | /// </summary> |
| | 12 | | public class JsonFormatter : IReportFormatter |
| | 13 | | { |
| | 14 | | private readonly JsonSerializerOptions _jsonOptions; |
| | 15 | |
|
| | 16 | | /// <summary> |
| | 17 | | /// Initializes a new instance of the <see cref="JsonFormatter"/> class. |
| | 18 | | /// </summary> |
| | 19 | | /// <param name="indented">Whether to format the JSON with indentation</param> |
| 9 | 20 | | public JsonFormatter(bool indented = true) |
| 9 | 21 | | { |
| 9 | 22 | | _jsonOptions = new JsonSerializerOptions |
| 9 | 23 | | { |
| 9 | 24 | | WriteIndented = indented, |
| 9 | 25 | | DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, |
| 9 | 26 | | PropertyNamingPolicy = JsonNamingPolicy.CamelCase |
| 9 | 27 | | }; |
| 9 | 28 | | } |
| | 29 | |
|
| | 30 | | /// <summary> |
| | 31 | | /// Formats a comparison result as JSON |
| | 32 | | /// </summary> |
| | 33 | | /// <param name="result">The comparison result to format</param> |
| | 34 | | /// <returns>Formatted JSON output as a string</returns> |
| | 35 | | public string Format(ComparisonResult result) |
| 7 | 36 | | { |
| | 37 | | // Create a JSON-friendly representation of the comparison result |
| 7 | 38 | | var jsonModel = CreateJsonModel(result); |
| | 39 | |
|
| | 40 | | // Serialize to JSON |
| 7 | 41 | | return JsonSerializer.Serialize(jsonModel, _jsonOptions); |
| 7 | 42 | | } |
| | 43 | |
|
| | 44 | | private static JsonComparisonResult CreateJsonModel(ComparisonResult result) |
| 7 | 45 | | { |
| | 46 | | // Create a JSON-specific model that includes all necessary information |
| 7 | 47 | | var jsonResult = new JsonComparisonResult |
| 7 | 48 | | { |
| 7 | 49 | | OldAssemblyPath = result.OldAssemblyPath, |
| 7 | 50 | | NewAssemblyPath = result.NewAssemblyPath, |
| 7 | 51 | | ComparisonTimestamp = result.ComparisonTimestamp, |
| 7 | 52 | | HasBreakingChanges = result.HasBreakingChanges, |
| 7 | 53 | | TotalDifferences = result.TotalDifferences, |
| 7 | 54 | | Summary = new JsonComparisonSummary |
| 7 | 55 | | { |
| 7 | 56 | | AddedCount = result.Summary.AddedCount, |
| 7 | 57 | | RemovedCount = result.Summary.RemovedCount, |
| 7 | 58 | | ModifiedCount = result.Summary.ModifiedCount, |
| 7 | 59 | | BreakingChangesCount = result.Summary.BreakingChangesCount, |
| 7 | 60 | | TotalChanges = result.Summary.TotalChanges |
| 7 | 61 | | } |
| 7 | 62 | | }; |
| | 63 | |
|
| | 64 | | // Group differences by change type for better organization |
| 14 | 65 | | var addedItems = result.Differences.Where(d => d.ChangeType == ChangeType.Added).ToList(); |
| 14 | 66 | | var removedItems = result.Differences.Where(d => d.ChangeType == ChangeType.Removed).ToList(); |
| 14 | 67 | | var modifiedItems = result.Differences.Where(d => d.ChangeType == ChangeType.Modified).ToList(); |
| 14 | 68 | | var excludedItems = result.Differences.Where(d => d.ChangeType == ChangeType.Excluded).ToList(); |
| 14 | 69 | | var movedItems = result.Differences.Where(d => d.ChangeType == ChangeType.Moved).ToList(); |
| | 70 | |
|
| | 71 | | // Convert differences to JSON model |
| 7 | 72 | | jsonResult.Added = addedItems.Select(ConvertToJsonDifference).ToList(); |
| 7 | 73 | | jsonResult.Removed = removedItems.Select(ConvertToJsonDifference).ToList(); |
| 7 | 74 | | jsonResult.Modified = modifiedItems.Select(ConvertToJsonDifference).ToList(); |
| 7 | 75 | | jsonResult.Excluded = excludedItems.Select(ConvertToJsonDifference).ToList(); |
| 7 | 76 | | jsonResult.Moved = movedItems.Select(ConvertToJsonDifference).ToList(); |
| | 77 | |
|
| | 78 | | // Add breaking changes separately for easy access |
| 7 | 79 | | jsonResult.BreakingChanges = result.Differences |
| 7 | 80 | | .Where(d => d.IsBreakingChange) |
| 7 | 81 | | .Select(ConvertToJsonDifference) |
| 7 | 82 | | .ToList(); |
| | 83 | |
|
| 7 | 84 | | return jsonResult; |
| 7 | 85 | | } |
| | 86 | |
|
| | 87 | | private static JsonApiDifference ConvertToJsonDifference(ApiDifference difference) |
| 11 | 88 | | { |
| 11 | 89 | | return new JsonApiDifference |
| 11 | 90 | | { |
| 11 | 91 | | ChangeType = difference.ChangeType.ToString(), |
| 11 | 92 | | ElementType = difference.ElementType.ToString(), |
| 11 | 93 | | ElementName = difference.ElementName, |
| 11 | 94 | | Description = difference.Description, |
| 11 | 95 | | IsBreakingChange = difference.IsBreakingChange, |
| 11 | 96 | | Severity = difference.Severity.ToString(), |
| 11 | 97 | | OldSignature = difference.OldSignature, |
| 11 | 98 | | NewSignature = difference.NewSignature |
| 11 | 99 | | }; |
| 11 | 100 | | } |
| | 101 | |
|
| | 102 | | /// <summary> |
| | 103 | | /// JSON-specific representation of a comparison result |
| | 104 | | /// </summary> |
| | 105 | | private class JsonComparisonResult |
| | 106 | | { |
| 21 | 107 | | public string OldAssemblyPath { get; set; } = string.Empty; |
| | 108 | |
|
| 21 | 109 | | public string NewAssemblyPath { get; set; } = string.Empty; |
| | 110 | |
|
| 14 | 111 | | public DateTime ComparisonTimestamp { get; set; } |
| | 112 | |
|
| 14 | 113 | | public bool HasBreakingChanges { get; set; } |
| | 114 | |
|
| 14 | 115 | | public int TotalDifferences { get; set; } |
| | 116 | |
|
| 21 | 117 | | public JsonComparisonSummary Summary { get; set; } = new(); |
| | 118 | |
|
| 21 | 119 | | public List<JsonApiDifference> Added { get; set; } = new(); |
| | 120 | |
|
| 21 | 121 | | public List<JsonApiDifference> Removed { get; set; } = new(); |
| | 122 | |
|
| 21 | 123 | | public List<JsonApiDifference> Modified { get; set; } = new(); |
| | 124 | |
|
| 21 | 125 | | public List<JsonApiDifference> Excluded { get; set; } = new(); |
| | 126 | |
|
| 21 | 127 | | public List<JsonApiDifference> Moved { get; set; } = new(); |
| | 128 | |
|
| 21 | 129 | | public List<JsonApiDifference> BreakingChanges { get; set; } = new(); |
| | 130 | | } |
| | 131 | |
|
| | 132 | | /// <summary> |
| | 133 | | /// JSON-specific representation of comparison summary |
| | 134 | | /// </summary> |
| | 135 | | private class JsonComparisonSummary |
| | 136 | | { |
| 14 | 137 | | public int AddedCount { get; set; } |
| | 138 | |
|
| 14 | 139 | | public int RemovedCount { get; set; } |
| | 140 | |
|
| 14 | 141 | | public int ModifiedCount { get; set; } |
| | 142 | |
|
| 14 | 143 | | public int BreakingChangesCount { get; set; } |
| | 144 | |
|
| 14 | 145 | | public int TotalChanges { get; set; } |
| | 146 | | } |
| | 147 | |
|
| | 148 | | /// <summary> |
| | 149 | | /// JSON-specific representation of an API difference |
| | 150 | | /// </summary> |
| | 151 | | private class JsonApiDifference |
| | 152 | | { |
| 33 | 153 | | public string ChangeType { get; set; } = string.Empty; |
| | 154 | |
|
| 33 | 155 | | public string ElementType { get; set; } = string.Empty; |
| | 156 | |
|
| 33 | 157 | | public string ElementName { get; set; } = string.Empty; |
| | 158 | |
|
| 33 | 159 | | public string Description { get; set; } = string.Empty; |
| | 160 | |
|
| 22 | 161 | | public bool IsBreakingChange { get; set; } |
| | 162 | |
|
| 33 | 163 | | public string Severity { get; set; } = string.Empty; |
| | 164 | |
|
| 22 | 165 | | public string? OldSignature { get; set; } |
| | 166 | |
|
| 22 | 167 | | public string? NewSignature { get; set; } |
| | 168 | | } |
| | 169 | | } |