Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
304 changes: 176 additions & 128 deletions src/DotNetApiDiff/Commands/CompareCommand.cs

Large diffs are not rendered by default.

45 changes: 6 additions & 39 deletions src/DotNetApiDiff/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ private static string GetBuildTime()
}

/// <summary>
/// Configures dependency injection services
/// Configures dependency injection services for the root container
/// Only includes services needed for command instantiation
/// </summary>
/// <param name="services">Service collection to configure</param>
public static void ConfigureServices(IServiceCollection services)
Expand Down Expand Up @@ -150,45 +151,11 @@ public static void ConfigureServices(IServiceCollection services)
builder.SetMinimumLevel(logLevel);
});

// Register core services
services.AddScoped<IAssemblyLoader, AssemblyLoading.AssemblyLoader>();
services.AddScoped<IApiExtractor, ApiExtraction.ApiExtractor>();
services.AddScoped<IMemberSignatureBuilder, ApiExtraction.MemberSignatureBuilder>();
services.AddScoped<ITypeAnalyzer, ApiExtraction.TypeAnalyzer>();
services.AddScoped<IDifferenceCalculator, ApiExtraction.DifferenceCalculator>();

// Register the NameMapper
services.AddScoped<INameMapper>(provider =>
{
var logger = provider.GetRequiredService<ILogger<ApiExtraction.NameMapper>>();

// Create a default mapping configuration - in real usage this would be loaded from config
var mappingConfig = Models.Configuration.MappingConfiguration.CreateDefault();
return new ApiExtraction.NameMapper(mappingConfig, logger);
});

// Register the ChangeClassifier
services.AddScoped<IChangeClassifier>(provider =>
{
var logger = provider.GetRequiredService<ILogger<ApiExtraction.ChangeClassifier>>();

// Create default configurations - in real usage these would be loaded from config
var breakingChangeRules = Models.Configuration.BreakingChangeRules.CreateDefault();
var exclusionConfig = Models.Configuration.ExclusionConfiguration.CreateDefault();

return new ApiExtraction.ChangeClassifier(breakingChangeRules, exclusionConfig, logger);
});

// Register the ApiComparer with NameMapper
services.AddScoped<IApiComparer, ApiExtraction.ApiComparer>();

// Register the ReportGenerator
services.AddScoped<IReportGenerator, Reporting.ReportGenerator>();

// Register the ExitCodeManager
// Register only services needed for command instantiation
services.AddSingleton<IExitCodeManager, ExitCodeManager>();

// Register the GlobalExceptionHandler
services.AddSingleton<IGlobalExceptionHandler, GlobalExceptionHandler>();

// Note: Business logic services (AssemblyLoader, ApiExtractor, etc.) will be registered
// in the command-specific container to avoid configuration timing issues
}
}
42 changes: 29 additions & 13 deletions src/DotNetApiDiff/Reporting/ConsoleFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,9 @@
table.AddColumn("Breaking");
}

table.Title = new TableTitle($"[bold {color}]{title}[/] ({changes.Count})");
// Ensure color is not empty or null to avoid Spectre Console parsing errors
var safeColor = string.IsNullOrWhiteSpace(color) ? "default" : color;
table.Title = new TableTitle($"[bold {safeColor}]{title}[/] ({changes.Count})");
table.Border = TableBorder.Rounded;

// Group changes by element type for better organization
Expand All @@ -336,7 +338,7 @@
var row = new List<string>
{
change.ElementType.ToString(),
change.ElementName,
string.IsNullOrWhiteSpace(change.ElementName) ? "[dim]<unnamed>[/]" : change.ElementName,
FormatChangeDetails(change)
};

Expand Down Expand Up @@ -368,25 +370,39 @@

private string FormatChangeDetails(ApiDifference change)
{
var details = new StringBuilder(change.Description);
// Escape any markup in the description to prevent parsing errors
var safeDescription = string.IsNullOrWhiteSpace(change.Description)
? "No description available"
: EscapeMarkup(change.Description);

if (!string.IsNullOrEmpty(change.OldSignature) && !string.IsNullOrEmpty(change.NewSignature))
{
details.AppendLine();
details.AppendLine($"[red]- {change.OldSignature}[/]");
details.AppendLine($"[green]+ {change.NewSignature}[/]");
}
else if (!string.IsNullOrEmpty(change.OldSignature))
var details = new StringBuilder(safeDescription);

if (!string.IsNullOrWhiteSpace(change.OldSignature))
{
details.AppendLine();
details.AppendLine($"[red]- {change.OldSignature}[/]");
details.AppendLine($"[red]- {EscapeMarkup(change.OldSignature)}[/]");
}
else if (!string.IsNullOrEmpty(change.NewSignature))

if (!string.IsNullOrWhiteSpace(change.NewSignature))
{
details.AppendLine();
details.AppendLine($"[green]+ {change.NewSignature}[/]");
details.AppendLine($"[green]+ {EscapeMarkup(change.NewSignature)}[/]");
}

return details.ToString();
}

/// <summary>
/// Escapes markup characters to prevent Spectre Console parsing errors
/// </summary>
/// <param name="text">The text to escape</param>
/// <returns>Escaped text safe for Spectre Console</returns>
private string EscapeMarkup(string text)
{
if (string.IsNullOrEmpty(text))
return text;

Check failure on line 403 in src/DotNetApiDiff/Reporting/ConsoleFormatter.cs

View workflow job for this annotation

GitHub Actions / code-quality

Check failure on line 403 in src/DotNetApiDiff/Reporting/ConsoleFormatter.cs

View workflow job for this annotation

GitHub Actions / code-quality


// Escape square brackets which are used for markup
return text.Replace("[", "[[").Replace("]", "]]");
}
}
Loading
Loading