diff --git a/src/DotNetApiDiff/ApiExtraction/ApiComparer.cs b/src/DotNetApiDiff/ApiExtraction/ApiComparer.cs index c460fc2..9eb1df5 100644 --- a/src/DotNetApiDiff/ApiExtraction/ApiComparer.cs +++ b/src/DotNetApiDiff/ApiExtraction/ApiComparer.cs @@ -2,6 +2,7 @@ using System.Reflection; using DotNetApiDiff.Interfaces; using DotNetApiDiff.Models; +using DotNetApiDiff.Models.Configuration; using Microsoft.Extensions.Logging; namespace DotNetApiDiff.ApiExtraction; @@ -15,6 +16,7 @@ public class ApiComparer : IApiComparer private readonly IDifferenceCalculator _differenceCalculator; private readonly INameMapper _nameMapper; private readonly IChangeClassifier _changeClassifier; + private readonly ComparisonConfiguration _configuration; private readonly ILogger _logger; /// @@ -24,18 +26,21 @@ public class ApiComparer : IApiComparer /// Calculator for detailed change analysis /// Mapper for namespace and type name transformations /// Classifier for breaking changes and exclusions + /// Configuration used for the comparison /// Logger for diagnostic information public ApiComparer( IApiExtractor apiExtractor, IDifferenceCalculator differenceCalculator, INameMapper nameMapper, IChangeClassifier changeClassifier, + ComparisonConfiguration configuration, ILogger logger) { _apiExtractor = apiExtractor ?? throw new ArgumentNullException(nameof(apiExtractor)); _differenceCalculator = differenceCalculator ?? throw new ArgumentNullException(nameof(differenceCalculator)); _nameMapper = nameMapper ?? throw new ArgumentNullException(nameof(nameMapper)); _changeClassifier = changeClassifier ?? throw new ArgumentNullException(nameof(changeClassifier)); + _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } @@ -66,7 +71,8 @@ public ComparisonResult CompareAssemblies(Assembly oldAssembly, Assembly newAsse { OldAssemblyPath = oldAssembly.Location, NewAssemblyPath = newAssembly.Location, - ComparisonTimestamp = DateTime.UtcNow + ComparisonTimestamp = DateTime.UtcNow, + Configuration = _configuration }; try diff --git a/src/DotNetApiDiff/Commands/CompareCommand.cs b/src/DotNetApiDiff/Commands/CompareCommand.cs index a78f2e8..8279d7a 100644 --- a/src/DotNetApiDiff/Commands/CompareCommand.cs +++ b/src/DotNetApiDiff/Commands/CompareCommand.cs @@ -275,7 +275,14 @@ public override int Execute([NotNull] CommandContext context, [NotNull] CompareC loggerFactory.CreateLogger())); // Add the main comparison service that depends on configured services - commandServices.AddScoped(); + commandServices.AddScoped(provider => + new ApiExtraction.ApiComparer( + provider.GetRequiredService(), + provider.GetRequiredService(), + provider.GetRequiredService(), + provider.GetRequiredService(), + config, + provider.GetRequiredService>())); // Execute the command with the configured services using (var commandProvider = commandServices.BuildServiceProvider()) diff --git a/src/DotNetApiDiff/DotNetApiDiff.csproj b/src/DotNetApiDiff/DotNetApiDiff.csproj index 4af61e3..6138a08 100644 --- a/src/DotNetApiDiff/DotNetApiDiff.csproj +++ b/src/DotNetApiDiff/DotNetApiDiff.csproj @@ -21,7 +21,7 @@ - + diff --git a/src/DotNetApiDiff/Reporting/HtmlFormatterScriban.cs b/src/DotNetApiDiff/Reporting/HtmlFormatterScriban.cs index adc6329..e53e9d4 100644 --- a/src/DotNetApiDiff/Reporting/HtmlFormatterScriban.cs +++ b/src/DotNetApiDiff/Reporting/HtmlFormatterScriban.cs @@ -4,6 +4,7 @@ using DotNetApiDiff.Models.Configuration; using Scriban; using Scriban.Runtime; +using System.Linq; namespace DotNetApiDiff.Reporting; @@ -65,42 +66,57 @@ public string Format(ComparisonResult result) private object PrepareConfigData(ComparisonConfiguration config) { + var namespaceMappings = config?.Mappings?.NamespaceMappings ?? new Dictionary>(); + + // Convert Dictionary to array of objects with key/value properties for Scriban + var namespaceMappingsArray = namespaceMappings.Select(kvp => new { key = kvp.Key, value = kvp.Value }).ToList(); + var typeMappingsArray = (config?.Mappings?.TypeMappings ?? new Dictionary()).Select(kvp => new { key = kvp.Key, value = kvp.Value }).ToList(); + + var mappingsResult = new + { + namespace_mappings = namespaceMappingsArray, + type_mappings = typeMappingsArray, + auto_map_same_name_types = config?.Mappings?.AutoMapSameNameTypes ?? false, + ignore_case = config?.Mappings?.IgnoreCase ?? false + }; + return new { filters = new { - include_internals = config.Filters.IncludeInternals, - include_compiler_generated = config.Filters.IncludeCompilerGenerated, - include_namespaces = config.Filters.IncludeNamespaces?.ToList() ?? new List(), - exclude_namespaces = config.Filters.ExcludeNamespaces?.ToList() ?? new List(), - include_types = config.Filters.IncludeTypes?.ToList() ?? new List(), - exclude_types = config.Filters.ExcludeTypes?.ToList() ?? new List() - }, - mappings = new - { - namespace_mappings = config.Mappings.NamespaceMappings ?? new Dictionary>(), - type_mappings = config.Mappings.TypeMappings ?? new Dictionary(), - auto_map_same_name_types = config.Mappings.AutoMapSameNameTypes, - ignore_case = config.Mappings.IgnoreCase + include_internals = config?.Filters?.IncludeInternals ?? false, + include_compiler_generated = config?.Filters?.IncludeCompilerGenerated ?? false, + include_namespaces = config?.Filters?.IncludeNamespaces?.ToList() ?? new List(), + exclude_namespaces = config?.Filters?.ExcludeNamespaces?.ToList() ?? new List(), + include_types = config?.Filters?.IncludeTypes?.ToList() ?? new List(), + exclude_types = config?.Filters?.ExcludeTypes?.ToList() ?? new List() }, + mappings = mappingsResult, exclusions = new { - excluded_types = config.Exclusions.ExcludedTypes?.ToList() ?? new List(), - excluded_members = config.Exclusions.ExcludedMembers?.ToList() ?? new List(), - excluded_type_patterns = config.Exclusions.ExcludedTypePatterns?.ToList() ?? new List(), - excluded_member_patterns = config.Exclusions.ExcludedMemberPatterns?.ToList() ?? new List(), - exclude_compiler_generated = config.Exclusions.ExcludeCompilerGenerated, - exclude_obsolete = config.Exclusions.ExcludeObsolete + excluded_types = config?.Exclusions?.ExcludedTypes?.ToList() ?? new List(), + excluded_members = config?.Exclusions?.ExcludedMembers?.ToList() ?? new List(), + excluded_type_patterns = config?.Exclusions?.ExcludedTypePatterns?.ToList() ?? new List(), + excluded_member_patterns = config?.Exclusions?.ExcludedMemberPatterns?.ToList() ?? new List(), + exclude_compiler_generated = config?.Exclusions?.ExcludeCompilerGenerated ?? false, + exclude_obsolete = config?.Exclusions?.ExcludeObsolete ?? false }, breaking_change_rules = new { - treat_type_removal_as_breaking = config.BreakingChangeRules.TreatTypeRemovalAsBreaking, - treat_member_removal_as_breaking = config.BreakingChangeRules.TreatMemberRemovalAsBreaking, - treat_signature_change_as_breaking = config.BreakingChangeRules.TreatSignatureChangeAsBreaking, - treat_reduced_accessibility_as_breaking = config.BreakingChangeRules.TreatReducedAccessibilityAsBreaking + treat_type_removal_as_breaking = config?.BreakingChangeRules?.TreatTypeRemovalAsBreaking ?? true, + treat_member_removal_as_breaking = config?.BreakingChangeRules?.TreatMemberRemovalAsBreaking ?? true, + treat_signature_change_as_breaking = config?.BreakingChangeRules?.TreatSignatureChangeAsBreaking ?? true, + treat_reduced_accessibility_as_breaking = config?.BreakingChangeRules?.TreatReducedAccessibilityAsBreaking ?? true, + treat_added_type_as_breaking = config?.BreakingChangeRules?.TreatAddedTypeAsBreaking ?? false, + treat_added_member_as_breaking = config?.BreakingChangeRules?.TreatAddedMemberAsBreaking ?? false, + treat_added_interface_as_breaking = config?.BreakingChangeRules?.TreatAddedInterfaceAsBreaking ?? true, + treat_removed_interface_as_breaking = config?.BreakingChangeRules?.TreatRemovedInterfaceAsBreaking ?? true, + treat_parameter_name_change_as_breaking = config?.BreakingChangeRules?.TreatParameterNameChangeAsBreaking ?? false, + treat_added_optional_parameter_as_breaking = config?.BreakingChangeRules?.TreatAddedOptionalParameterAsBreaking ?? false }, - output_format = config.OutputFormat.ToString(), - output_path = config.OutputPath ?? string.Empty + output_format = config?.OutputFormat.ToString() ?? "Console", + output_path = config?.OutputPath ?? string.Empty, + fail_on_breaking_changes = config?.FailOnBreakingChanges ?? false }; } diff --git a/src/DotNetApiDiff/Reporting/HtmlTemplates/config-boolean-item.scriban b/src/DotNetApiDiff/Reporting/HtmlTemplates/config-boolean-item.scriban new file mode 100644 index 0000000..7e90f1d --- /dev/null +++ b/src/DotNetApiDiff/Reporting/HtmlTemplates/config-boolean-item.scriban @@ -0,0 +1,4 @@ +
+ {{ $1 }}: + {{ $2 | format_boolean }} +
diff --git a/src/DotNetApiDiff/Reporting/HtmlTemplates/config-mappings.scriban b/src/DotNetApiDiff/Reporting/HtmlTemplates/config-mappings.scriban index 6152f59..4760b2a 100644 --- a/src/DotNetApiDiff/Reporting/HtmlTemplates/config-mappings.scriban +++ b/src/DotNetApiDiff/Reporting/HtmlTemplates/config-mappings.scriban @@ -1,8 +1,8 @@
- {{ label }}: - {{if mappings && mappings.size > 0}} + {{ $2 }}: + {{if $1 && $1.size > 0}}
- {{for mapping in mappings}} + {{for mapping in $1}}
{{ mapping.key }} diff --git a/src/DotNetApiDiff/Reporting/HtmlTemplates/config-namespace-mappings.scriban b/src/DotNetApiDiff/Reporting/HtmlTemplates/config-namespace-mappings.scriban index 230dd0d..afeb220 100644 --- a/src/DotNetApiDiff/Reporting/HtmlTemplates/config-namespace-mappings.scriban +++ b/src/DotNetApiDiff/Reporting/HtmlTemplates/config-namespace-mappings.scriban @@ -1,8 +1,8 @@
- {{ label }}: - {{if mappings && mappings.size > 0}} + {{ $2 }}: + {{if $1 && $1.size > 0}}
- {{for mapping in mappings}} + {{for mapping in $1}}
{{ mapping.key }} diff --git a/src/DotNetApiDiff/Reporting/HtmlTemplates/config-string-list.scriban b/src/DotNetApiDiff/Reporting/HtmlTemplates/config-string-list.scriban index 011f4d2..3aea070 100644 --- a/src/DotNetApiDiff/Reporting/HtmlTemplates/config-string-list.scriban +++ b/src/DotNetApiDiff/Reporting/HtmlTemplates/config-string-list.scriban @@ -1,8 +1,8 @@
- {{ label }}: - {{if items && items.size > 0}} + {{ $2 }}: + {{if $1 && $1.size > 0}}
- {{for item in items}} + {{for item in $1}} {{ item }} {{end}}
diff --git a/src/DotNetApiDiff/Reporting/HtmlTemplates/configuration.scriban b/src/DotNetApiDiff/Reporting/HtmlTemplates/configuration.scriban index 3a22300..10deed6 100644 --- a/src/DotNetApiDiff/Reporting/HtmlTemplates/configuration.scriban +++ b/src/DotNetApiDiff/Reporting/HtmlTemplates/configuration.scriban @@ -2,14 +2,8 @@

🔍 Filters

-
- Include Internals: - {{ config.filters.include_internals | format_boolean }} -
-
- Include Compiler Generated: - {{ config.filters.include_compiler_generated | format_boolean }} -
+ {{ include "config-boolean-item" "Include Internals" config.filters.include_internals }} + {{ include "config-boolean-item" "Include Compiler Generated" config.filters.include_compiler_generated }} {{ include "config-string-list" config.filters.include_namespaces "Include Namespaces" }} {{ include "config-string-list" config.filters.exclude_namespaces "Exclude Namespaces" }} {{ include "config-string-list" config.filters.include_types "Include Types" }} @@ -19,14 +13,8 @@

🔗 Mappings

-
- Auto Map Same Name Types: - {{ config.mappings.auto_map_same_name_types | format_boolean }} -
-
- Ignore Case: - {{ config.mappings.ignore_case | format_boolean }} -
+ {{ include "config-boolean-item" "Auto Map Same Name Types" config.mappings.auto_map_same_name_types }} + {{ include "config-boolean-item" "Ignore Case" config.mappings.ignore_case }} {{ include "config-mappings" config.mappings.type_mappings "Type Mappings" }} {{ include "config-namespace-mappings" config.mappings.namespace_mappings "Namespace Mappings" }}
@@ -38,6 +26,8 @@ {{ include "config-string-list" config.exclusions.excluded_members "Excluded Members" }} {{ include "config-string-list" config.exclusions.excluded_type_patterns "Excluded Type Patterns" }} {{ include "config-string-list" config.exclusions.excluded_member_patterns "Excluded Member Patterns" }} + {{ include "config-boolean-item" "Exclude Compiler Generated" config.exclusions.exclude_compiler_generated }} + {{ include "config-boolean-item" "Exclude Obsolete" config.exclusions.exclude_obsolete }}
@@ -45,24 +35,24 @@

⚠️ Breaking Change Rules

-
- Treat Type Removal as Breaking: - {{ config.breaking_change_rules.treat_type_removal_as_breaking | format_boolean }} -
-
- Treat Member Removal as Breaking: - {{ config.breaking_change_rules.treat_member_removal_as_breaking | format_boolean }} -
+

Removal Rules

+ {{ include "config-boolean-item" "Treat Type Removal as Breaking" config.breaking_change_rules.treat_type_removal_as_breaking }} + {{ include "config-boolean-item" "Treat Member Removal as Breaking" config.breaking_change_rules.treat_member_removal_as_breaking }} + +

Addition Rules

+ {{ include "config-boolean-item" "Treat Added Type as Breaking" config.breaking_change_rules.treat_added_type_as_breaking }} + {{ include "config-boolean-item" "Treat Added Member as Breaking" config.breaking_change_rules.treat_added_member_as_breaking }}
-
- Treat Signature Change as Breaking: - {{ config.breaking_change_rules.treat_signature_change_as_breaking | format_boolean }} -
-
- Treat Reduced Accessibility as Breaking: - {{ config.breaking_change_rules.treat_reduced_accessibility_as_breaking | format_boolean }} -
+

Modification Rules

+ {{ include "config-boolean-item" "Treat Signature Change as Breaking" config.breaking_change_rules.treat_signature_change_as_breaking }} + {{ include "config-boolean-item" "Treat Reduced Accessibility as Breaking" config.breaking_change_rules.treat_reduced_accessibility_as_breaking }} + {{ include "config-boolean-item" "Treat Parameter Name Change as Breaking" config.breaking_change_rules.treat_parameter_name_change_as_breaking }} + {{ include "config-boolean-item" "Treat Added Optional Parameter as Breaking" config.breaking_change_rules.treat_added_optional_parameter_as_breaking }} + +

Interface Rules

+ {{ include "config-boolean-item" "Treat Added Interface as Breaking" config.breaking_change_rules.treat_added_interface_as_breaking }} + {{ include "config-boolean-item" "Treat Removed Interface as Breaking" config.breaking_change_rules.treat_removed_interface_as_breaking }}
@@ -74,10 +64,7 @@ Output Format: {{ config.output_format }}
-
- Fail On Breaking Changes: - {{ config.fail_on_breaking_changes | format_boolean }} -
+ {{ include "config-boolean-item" "Fail On Breaking Changes" config.fail_on_breaking_changes }} {{if config.output_path}}
Output Path: diff --git a/src/DotNetApiDiff/Reporting/HtmlTemplates/main-layout.scriban b/src/DotNetApiDiff/Reporting/HtmlTemplates/main-layout.scriban index b93fc9a..fdbd2b1 100644 --- a/src/DotNetApiDiff/Reporting/HtmlTemplates/main-layout.scriban +++ b/src/DotNetApiDiff/Reporting/HtmlTemplates/main-layout.scriban @@ -85,7 +85,7 @@
diff --git a/src/DotNetApiDiff/Reporting/HtmlTemplates/styles.css b/src/DotNetApiDiff/Reporting/HtmlTemplates/styles.css index 52943c9..bd8bfb1 100644 --- a/src/DotNetApiDiff/Reporting/HtmlTemplates/styles.css +++ b/src/DotNetApiDiff/Reporting/HtmlTemplates/styles.css @@ -248,6 +248,15 @@ section h2 { font-size: 1.2rem; } +.config-section h4 { + margin: 15px 0 10px 0; + color: #6c757d; + font-size: 1rem; + font-weight: 600; + border-bottom: 1px solid #e9ecef; + padding-bottom: 5px; +} + .config-item { margin-bottom: 12px; } diff --git a/tests/DotNetApiDiff.Tests/ApiExtraction/ApiComparerTests.cs b/tests/DotNetApiDiff.Tests/ApiExtraction/ApiComparerTests.cs index 01da727..a50b511 100644 --- a/tests/DotNetApiDiff.Tests/ApiExtraction/ApiComparerTests.cs +++ b/tests/DotNetApiDiff.Tests/ApiExtraction/ApiComparerTests.cs @@ -3,6 +3,7 @@ using DotNetApiDiff.ApiExtraction; using DotNetApiDiff.Interfaces; using DotNetApiDiff.Models; +using DotNetApiDiff.Models.Configuration; using Microsoft.Extensions.Logging; using Moq; using Xunit; @@ -35,6 +36,7 @@ public ApiComparerTests() _mockDifferenceCalculator.Object, _mockNameMapper.Object, _mockChangeClassifier.Object, + ComparisonConfiguration.CreateDefault(), _mockLogger.Object); } diff --git a/tests/DotNetApiDiff.Tests/ApiExtraction/ApiComparerWithMappingTests.cs b/tests/DotNetApiDiff.Tests/ApiExtraction/ApiComparerWithMappingTests.cs index 6a65a04..dae2ef0 100644 --- a/tests/DotNetApiDiff.Tests/ApiExtraction/ApiComparerWithMappingTests.cs +++ b/tests/DotNetApiDiff.Tests/ApiExtraction/ApiComparerWithMappingTests.cs @@ -157,6 +157,7 @@ public void CompareTypes_WithNoMapping_FindsDifferences() _differenceCalculatorMock.Object, nameMapper, _changeClassifierMock.Object, + ComparisonConfiguration.CreateDefault(), _loggerMock.Object); var addedDifference = new ApiDifference { ChangeType = ChangeType.Added };