diff --git a/src/Refitter.Tests/GenerationOrchestratorTests.cs b/src/Refitter.Tests/GenerationOrchestratorTests.cs new file mode 100644 index 000000000..6b680aaa5 --- /dev/null +++ b/src/Refitter.Tests/GenerationOrchestratorTests.cs @@ -0,0 +1,181 @@ +using FluentAssertions; +using Refitter.Core; +using TUnit.Core; + +namespace Refitter.Tests; + +public class GenerationOrchestratorTests +{ + [Test] + public async Task RunAsync_Should_Generate_Code_And_Return_Zero() + { + var workspace = Path.Combine( + AppContext.BaseDirectory, + "GenerationOrchestratorTests", + Guid.NewGuid().ToString("N")); + + try + { + var openApiPath = Path.Combine(workspace, "spec.json"); + var outputPath = Path.Combine(workspace, "Output.cs"); + Directory.CreateDirectory(workspace); + + File.WriteAllText( + openApiPath, + """ + { + "openapi": "3.0.0", + "info": { "title": "Test API", "version": "1.0.0" }, + "paths": { + "/pets": { + "get": { + "operationId": "GetPets", + "responses": { "200": { "description": "ok" } } + } + } + } + } + """); + + var settings = new RefitGeneratorSettings + { + OpenApiPath = openApiPath, + Namespace = "TestNamespace", + GenerateContracts = false, + GenerateClients = true, + }; + + var cliSettings = new Settings + { + OpenApiPath = openApiPath, + OutputPath = outputPath, + NoLogging = true, + NoBanner = true, + SkipValidation = true, + }; + + var reporter = new SimpleGenerationReporter(); + var orchestrator = new GenerationOrchestrator(); + var result = await orchestrator.RunAsync(settings, cliSettings, reporter, default); + + result.Should().Be(0); + File.Exists(outputPath).Should().BeTrue(); + var generatedCode = await File.ReadAllTextAsync(outputPath); + generatedCode.Should().Contain("TestNamespace"); + generatedCode.Should().Contain("GetPets"); + } + finally + { + if (Directory.Exists(workspace)) + Directory.Delete(workspace, recursive: true); + } + } + + [Test] + public async Task RunAsync_Should_Generate_Multiple_Files_And_Return_Zero() + { + var workspace = Path.Combine( + AppContext.BaseDirectory, + "GenerationOrchestratorTests", + Guid.NewGuid().ToString("N")); + + try + { + var openApiPath = Path.Combine(workspace, "spec.json"); + var outputDir = Path.Combine(workspace, "Generated"); + Directory.CreateDirectory(workspace); + + File.WriteAllText( + openApiPath, + """ + { + "openapi": "3.0.0", + "info": { "title": "Test API", "version": "1.0.0" }, + "paths": { + "/pets": { + "get": { + "operationId": "GetPets", + "tags": ["pets"], + "responses": { "200": { "description": "ok" } } + } + } + } + } + """); + + var settings = new RefitGeneratorSettings + { + OpenApiPath = openApiPath, + Namespace = "TestNamespace", + GenerateMultipleFiles = true, + OutputFolder = outputDir, + GenerateContracts = false, + }; + + var cliSettings = new Settings + { + OpenApiPath = openApiPath, + OutputPath = outputDir, + GenerateMultipleFiles = true, + NoLogging = true, + NoBanner = true, + SkipValidation = true, + }; + + var reporter = new SimpleGenerationReporter(); + var orchestrator = new GenerationOrchestrator(); + var result = await orchestrator.RunAsync(settings, cliSettings, reporter, default); + + result.Should().Be(0); + var generatedFiles = Directory.GetFiles(outputDir, "*.cs"); + generatedFiles.Should().NotBeEmpty(); + var generatedCode = await File.ReadAllTextAsync(generatedFiles[0]); + generatedCode.Should().Contain("TestNamespace"); + } + finally + { + if (Directory.Exists(workspace)) + Directory.Delete(workspace, recursive: true); + } + } + + [Test] + public async Task RunAsync_Should_Return_NonZero_On_Exception() + { + var workspace = Path.Combine( + AppContext.BaseDirectory, + "GenerationOrchestratorTests", + Guid.NewGuid().ToString("N")); + + try + { + var openApiPath = Path.Combine(workspace, "nonexistent.json"); + Directory.CreateDirectory(workspace); + + var settings = new RefitGeneratorSettings + { + OpenApiPath = openApiPath, + Namespace = "TestNamespace", + }; + + var cliSettings = new Settings + { + OpenApiPath = openApiPath, + NoLogging = true, + NoBanner = true, + SkipValidation = true, + }; + + var reporter = new SimpleGenerationReporter(); + var orchestrator = new GenerationOrchestrator(); + var result = await orchestrator.RunAsync(settings, cliSettings, reporter, default); + + result.Should().NotBe(0); + } + finally + { + if (Directory.Exists(workspace)) + Directory.Delete(workspace, recursive: true); + } + } +} diff --git a/src/Refitter/GenerateCommand.cs b/src/Refitter/GenerateCommand.cs index 6fcd65eb0..c014b279d 100644 --- a/src/Refitter/GenerateCommand.cs +++ b/src/Refitter/GenerateCommand.cs @@ -1,8 +1,5 @@ -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using Microsoft.OpenApi; using Refitter.Core; -using Refitter.Validation; using Spectre.Console; using Spectre.Console.Cli; @@ -14,7 +11,6 @@ public sealed class GenerateCommand : AsyncCommand internal const string GeneratedFileMarker = "GeneratedFile: "; private RefitGeneratorSettings? cachedSettings; - private IGenerationReporter reporter = new RichGenerationReporter(); private static IGenerationReporter CreateReporter(Settings settings) => settings.SimpleOutput @@ -30,14 +26,10 @@ protected override ValidationResult Validate(CommandContext context, Settings se context.Arguments.Any(a => a.Equals("-v", StringComparison.OrdinalIgnoreCase))) return ValidationResult.Success(); - // Cache settings if using settings file to avoid reading twice var validationResult = SettingsValidator.Validate(settings, out var refitSettings); if (refitSettings != null) - { cachedSettings = refitSettings; - } - // Detect conflict: both CLI and settings file specify non-default jsonLibraryVersion if (settings.JsonLibraryVersion is { } cliValue && cliValue != 8.0m && refitSettings?.CodeGeneratorSettings?.JsonLibraryVersion is { } fileValue && @@ -50,136 +42,47 @@ protected override ValidationResult Validate(CommandContext context, Settings se return validationResult; } - protected override async Task ExecuteAsync(CommandContext context, Settings settings, CancellationToken cancellationToken) + protected override async Task ExecuteAsync( + CommandContext context, + Settings settings, + CancellationToken cancellationToken) { - RefitGeneratorSettings refitGeneratorSettings; - - reporter = CreateReporter(settings); - - try - { - // Use cached settings from Validate() if available - if (cachedSettings != null) - { - refitGeneratorSettings = cachedSettings; - cachedSettings = null; // Clear cache after use - } - else if (!string.IsNullOrWhiteSpace(settings.SettingsFilePath)) - { - // Fallback: read settings file if not cached (shouldn't normally happen) - var json = await File.ReadAllTextAsync(settings.SettingsFilePath); - refitGeneratorSettings = Serializer.Deserialize(json); - - // Allow CLI to override OpenApiPath if explicitly provided - if (!string.IsNullOrWhiteSpace(settings.OpenApiPath)) - refitGeneratorSettings.OpenApiPath = settings.OpenApiPath; - - ApplySettingsFileDefaults(settings.SettingsFilePath, refitGeneratorSettings); - } - else - { - // No settings file - build from CLI arguments - refitGeneratorSettings = CreateRefitGeneratorSettings(settings); - } - var stopwatch = Stopwatch.StartNew(); - var version = GetType().Assembly.GetName().Version!.ToString(); - if (version == "1.0.0.0") - version += " (local build)"; - - // Header with branding - reporter.ReportHeader(version); - - // Support information - var supportKey = settings.NoLogging - ? "Unavailable when logging is disabled" - : SupportInformation.GetSupportKey(); - - reporter.ReportSupportKey(supportKey); - - if (context.Arguments.Any(a => a.Equals("--version", StringComparison.OrdinalIgnoreCase)) || - context.Arguments.Any(a => a.Equals("-v", StringComparison.OrdinalIgnoreCase))) - { - return 0; - } + var reporter = CreateReporter(settings); - // Resolve relative paths from settings file directory before creating generator - if (!string.IsNullOrWhiteSpace(settings.SettingsFilePath)) - { - ResolveRelativeSpecPaths(settings.SettingsFilePath, refitGeneratorSettings); - } - - var generator = await RefitGenerator.CreateAsync(refitGeneratorSettings); - if (!settings.SkipValidation) - { - if (refitGeneratorSettings.OpenApiPaths == null || refitGeneratorSettings.OpenApiPaths.Length == 0) - { - var specPath = refitGeneratorSettings.OpenApiPath!; - await ValidateOpenApiSpec(specPath, reporter); - } - else - { - foreach (var specPath in refitGeneratorSettings.OpenApiPaths) - { - await ValidateOpenApiSpec(specPath, reporter); - } - } - } - - await (refitGeneratorSettings.GenerateMultipleFiles - ? WriteMultipleFiles(generator, settings, refitGeneratorSettings) - : WriteSingleFile(generator, settings, refitGeneratorSettings)); - - Analytics.LogFeatureUsage(settings, refitGeneratorSettings); - - // Generate .refitter settings file if not using existing settings file - if (string.IsNullOrWhiteSpace(settings.SettingsFilePath)) - { - await WriteRefitterSettingsFile(settings, refitGeneratorSettings); - } - - if (refitGeneratorSettings.IncludePathMatches.Length > 0 && - generator.OpenApiDocument.Paths.Count == 0) - { - reporter.ReportAllPathsFilteredWarning(refitGeneratorSettings.IncludePathMatches); - } - - // Success summary with performance metrics - stopwatch.Stop(); - reporter.ReportSuccess(stopwatch.Elapsed, refitGeneratorSettings.GenerateMultipleFiles); - - if (!settings.NoBanner) - { - reporter.ReportDonationBanner(); - } + if (!settings.NoLogging) + Analytics.Configure(); - ShowWarnings(refitGeneratorSettings, settings); + if (context.Arguments.Any(a => a.Equals("--version", StringComparison.OrdinalIgnoreCase)) || + context.Arguments.Any(a => a.Equals("-v", StringComparison.OrdinalIgnoreCase))) return 0; + + RefitGeneratorSettings refitGeneratorSettings; + if (cachedSettings != null) + { + refitGeneratorSettings = cachedSettings; + cachedSettings = null; } - catch (Exception exception) + else if (!string.IsNullOrWhiteSpace(settings.SettingsFilePath)) { - // Error summary panel - reporter.ReportGenerationFailed(); + var json = await File.ReadAllTextAsync(settings.SettingsFilePath); + refitGeneratorSettings = Serializer.Deserialize(json); - if (exception is OpenApiUnsupportedSpecVersionException unsupportedSpecVersionException) - { - reporter.ReportUnsupportedVersion(unsupportedSpecVersionException.SpecificationVersion); - } + if (!string.IsNullOrWhiteSpace(settings.OpenApiPath)) + refitGeneratorSettings.OpenApiPath = settings.OpenApiPath; - if (exception is not OpenApiValidationException) - { - reporter.ReportExceptionDetails(exception); - } - - if (!settings.SkipValidation) - { - reporter.ReportSkipValidationSuggestion(); - } - - reporter.ReportSupportHelp(); - - await Analytics.LogError(exception, settings); - return exception.HResult; + ApplySettingsFileDefaults(settings.SettingsFilePath, refitGeneratorSettings); + } + else + { + refitGeneratorSettings = CreateRefitGeneratorSettings(settings); } + + var orchestrator = new GenerationOrchestrator(); + return await orchestrator.RunAsync( + refitGeneratorSettings, + settings, + reporter, + cancellationToken); } private static RefitGeneratorSettings CreateRefitGeneratorSettings(Settings settings) @@ -239,119 +142,24 @@ private static RefitGeneratorSettings CreateRefitGeneratorSettings(Settings sett GenerateJsonSerializerContext = settings.GenerateJsonSerializerContext, }; } - private async Task WriteSingleFile( - RefitGenerator generator, - Settings settings, - RefitGeneratorSettings refitGeneratorSettings) - { - await reporter.ReportSingleFileGenerationProgressAsync(); - - var code = generator.Generate().ReplaceLineEndings(); - var planned = OutputPlanner.PlanSingleFile(settings, refitGeneratorSettings, code); - - var fileName = Path.GetFileName(planned.Path); - var directory = Path.GetDirectoryName(planned.Path) ?? ""; - var sizeFormatted = FormatFileSize(code.Length); - var lines = code.Split('\n').Length; - - reporter.ReportSingleFileOutput(fileName, directory, sizeFormatted, lines); - - await FileWriter.WriteAsync(planned); - reporter.ReportFileWritten(planned.Path); - } - private static string FormatFileSize(long bytes) - { - string[] suffixes = { "B", "KB", "MB", "GB" }; - double size = bytes; - int suffixIndex = 0; - - while (size >= 1024 && suffixIndex < suffixes.Length - 1) - { - size /= 1024; - suffixIndex++; - } - - return $"{size:F1} {suffixes[suffixIndex]}"; - } - private async Task WriteMultipleFiles( - RefitGenerator generator, - Settings settings, - RefitGeneratorSettings refitGeneratorSettings) - { - var generatorOutput = await reporter.GenerateMultipleFilesWithProgressAsync(generator.GenerateMultipleFiles); - var planned = OutputPlanner.PlanMultipleFiles(settings, refitGeneratorSettings, generatorOutput); - - var totalSize = 0L; - var totalLines = 0; - - var report = reporter.BeginMultiFileOutput(); - - for (var i = 0; i < generatorOutput.Files.Count; i++) - { - var outputFile = generatorOutput.Files[i]; - var plannedFile = planned[i]; - - var size = outputFile.Content.Length; - var lines = outputFile.Content.Split('\n').Length; - var directory = Path.GetDirectoryName(plannedFile.Path) ?? ""; - - report.AddFile(outputFile.Filename, directory, FormatFileSize(size), lines); - - totalSize += size; - totalLines += lines; - - await FileWriter.WriteAsync(plannedFile); - reporter.ReportFileWritten(plannedFile.Path); - } - - report.Complete(generatorOutput.Files.Count, FormatFileSize(totalSize), totalLines); - } - - private void ShowWarnings(RefitGeneratorSettings refitGeneratorSettings, Settings settings) - { - var warnings = new List<(string title, string description)>(); - - if (refitGeneratorSettings.UseIsoDateFormat && - refitGeneratorSettings.CodeGeneratorSettings?.DateFormat is not null) - { - warnings.Add(( - "Date Format Override", - "'codeGeneratorSettings.dateFormat' will be ignored due to 'useIsoDateFormat' set to true" - )); - } - -#pragma warning disable CS0618 // Type or member is obsolete - if (refitGeneratorSettings.DependencyInjectionSettings?.UsePolly is true) -#pragma warning restore CS0618 // Type or member is obsolete - { - warnings.Add(( - "Deprecated Setting", - "The 'usePolly' property is deprecated. Use 'transientErrorHandler: Polly' instead" - )); - } - - if (warnings.Any()) - { - reporter.ReportConfigurationWarnings(warnings); - } - } private static string GetOutputPath(Settings settings, RefitGeneratorSettings refitGeneratorSettings) => OutputPlanner.GetSingleFileOutputPath(settings, refitGeneratorSettings); + private string GetOutputPath( + Settings settings, + RefitGeneratorSettings refitGeneratorSettings, + GeneratedCode outputFile) => + OutputPlanner.GetMultiFileOutputPath(settings, refitGeneratorSettings, outputFile); + internal static void ApplySettingsFileDefaults(string settingsFilePath, RefitGeneratorSettings refitGeneratorSettings) { - // Re-apply multi-file trigger logic if (!string.IsNullOrWhiteSpace(refitGeneratorSettings.ContractsOutputFolder)) refitGeneratorSettings.GenerateMultipleFiles = true; - // Default outputFolder to ./Generated if not specified (property initializer not invoked by JSON deserialization) if (string.IsNullOrWhiteSpace(refitGeneratorSettings.OutputFolder)) - { refitGeneratorSettings.OutputFolder = RefitGeneratorSettings.DefaultOutputFolder; - } - // Default outputFilename to .refitter filename if not specified if (string.IsNullOrWhiteSpace(refitGeneratorSettings.OutputFilename)) { var refitterFileName = Path.GetFileNameWithoutExtension(settingsFilePath); @@ -361,40 +169,9 @@ internal static void ApplySettingsFileDefaults(string settingsFilePath, RefitGen } } - private string GetOutputPath( - Settings settings, - RefitGeneratorSettings refitGeneratorSettings, - GeneratedCode outputFile) => - OutputPlanner.GetMultiFileOutputPath(settings, refitGeneratorSettings, outputFile); - internal static string FormatGeneratedFileMarker(string outputPath) => $"{GeneratedFileMarker}{Path.GetFullPath(outputPath)}"; - private static async Task ValidateOpenApiSpec(string openApiPath, IGenerationReporter reporter) - { - var validationResult = await reporter.ValidateWithProgressAsync( - () => Validation.OpenApiValidator.Validate(openApiPath)); - - if (!validationResult.IsValid) - { - reporter.ReportValidationFailed(); - - foreach (var error in validationResult.Diagnostics.Errors) - { - reporter.ReportValidationDiagnostic(error, isError: true); - } - - foreach (var warning in validationResult.Diagnostics.Warnings) - { - reporter.ReportValidationDiagnostic(warning, isError: false); - } - - validationResult.ThrowIfInvalid(); - } - - reporter.ReportValidationStatistics(validationResult); - } - internal static async Task WriteRefitterSettingsFile(Settings settings, RefitGeneratorSettings refitGeneratorSettings) { var settingsFilePath = DetermineSettingsFilePath(settings); @@ -411,7 +188,6 @@ internal static async Task WriteRefitterSettingsFile(Settings settings, RefitGen internal static string DetermineSettingsFilePath(Settings settings) { - // If output path is specified and is a directory, put .refitter file there if (!string.IsNullOrWhiteSpace(settings.OutputPath) && settings.OutputPath != Settings.DefaultOutputPath) { @@ -420,12 +196,9 @@ internal static string DetermineSettingsFilePath(Settings settings) : Path.GetDirectoryName(settings.OutputPath); if (!string.IsNullOrWhiteSpace(outputDir)) - { return Path.Combine(outputDir, FileExtensionConstants.Refitter); - } } - // Default: put .refitter file in current directory return FileExtensionConstants.Refitter; } diff --git a/src/Refitter/GenerationOrchestrator.cs b/src/Refitter/GenerationOrchestrator.cs new file mode 100644 index 000000000..e9bec20f6 --- /dev/null +++ b/src/Refitter/GenerationOrchestrator.cs @@ -0,0 +1,224 @@ +using System.Diagnostics; +using Microsoft.OpenApi; +using Refitter.Core; +using Refitter.Validation; + +namespace Refitter; + +public sealed class GenerationOrchestrator : IGenerationOrchestrator +{ + public async Task RunAsync( + RefitGeneratorSettings refitGeneratorSettings, + Settings cliSettings, + IGenerationReporter reporter, + CancellationToken cancellationToken) + { + try + { + var stopwatch = Stopwatch.StartNew(); + var version = GetType().Assembly.GetName().Version!.ToString(); + if (version == "1.0.0.0") + version += " (local build)"; + + reporter.ReportHeader(version); + + var supportKey = cliSettings.NoLogging + ? "Unavailable when logging is disabled" + : SupportInformation.GetSupportKey(); + + reporter.ReportSupportKey(supportKey); + + if (!string.IsNullOrWhiteSpace(cliSettings.SettingsFilePath)) + GenerateCommand.ResolveRelativeSpecPaths( + cliSettings.SettingsFilePath, + refitGeneratorSettings); + + var generator = await RefitGenerator.CreateAsync(refitGeneratorSettings); + + if (!cliSettings.SkipValidation) + await ValidateOpenApiSpecs(refitGeneratorSettings, reporter); + + await (refitGeneratorSettings.GenerateMultipleFiles + ? WriteMultipleFiles(generator, cliSettings, refitGeneratorSettings, reporter) + : WriteSingleFile(generator, cliSettings, refitGeneratorSettings, reporter)); + + Analytics.LogFeatureUsage(cliSettings, refitGeneratorSettings); + + if (string.IsNullOrWhiteSpace(cliSettings.SettingsFilePath)) + await GenerateCommand.WriteRefitterSettingsFile(cliSettings, refitGeneratorSettings); + + if (refitGeneratorSettings.IncludePathMatches.Length > 0 && + generator.OpenApiDocument.Paths.Count == 0) + { + reporter.ReportAllPathsFilteredWarning(refitGeneratorSettings.IncludePathMatches); + } + + stopwatch.Stop(); + reporter.ReportSuccess(stopwatch.Elapsed, refitGeneratorSettings.GenerateMultipleFiles); + + if (!cliSettings.NoBanner) + reporter.ReportDonationBanner(); + + ShowWarnings(refitGeneratorSettings, cliSettings, reporter); + return 0; + } + catch (Exception exception) + { + reporter.ReportGenerationFailed(); + + if (exception is OpenApiUnsupportedSpecVersionException unsupportedSpecVersionException) + reporter.ReportUnsupportedVersion(unsupportedSpecVersionException.SpecificationVersion); + + if (exception is not OpenApiValidationException) + reporter.ReportExceptionDetails(exception); + + if (!cliSettings.SkipValidation) + reporter.ReportSkipValidationSuggestion(); + + reporter.ReportSupportHelp(); + + await Analytics.LogError(exception, cliSettings); + return exception.HResult; + } + } + + private static async Task WriteSingleFile( + RefitGenerator generator, + Settings settings, + RefitGeneratorSettings refitGeneratorSettings, + IGenerationReporter reporter) + { + await reporter.ReportSingleFileGenerationProgressAsync(); + + var code = generator.Generate().ReplaceLineEndings(); + var planned = OutputPlanner.PlanSingleFile(settings, refitGeneratorSettings, code); + + var fileName = Path.GetFileName(planned.Path); + var directory = Path.GetDirectoryName(planned.Path) ?? ""; + var sizeFormatted = FormatFileSize(code.Length); + var lines = code.Split('\n').Length; + + reporter.ReportSingleFileOutput(fileName, directory, sizeFormatted, lines); + + await FileWriter.WriteAsync(planned); + reporter.ReportFileWritten(planned.Path); + } + + private static async Task WriteMultipleFiles( + RefitGenerator generator, + Settings settings, + RefitGeneratorSettings refitGeneratorSettings, + IGenerationReporter reporter) + { + var generatorOutput = await reporter.GenerateMultipleFilesWithProgressAsync( + generator.GenerateMultipleFiles); + + var planned = OutputPlanner.PlanMultipleFiles( + settings, + refitGeneratorSettings, + generatorOutput); + + var totalSize = 0L; + var totalLines = 0; + var report = reporter.BeginMultiFileOutput(); + + for (var i = 0; i < generatorOutput.Files.Count; i++) + { + var outputFile = generatorOutput.Files[i]; + var plannedFile = planned[i]; + + var size = outputFile.Content.Length; + var lines = outputFile.Content.Split('\n').Length; + var directory = Path.GetDirectoryName(plannedFile.Path) ?? ""; + + report.AddFile(outputFile.Filename, directory, FormatFileSize(size), lines); + + totalSize += size; + totalLines += lines; + + await FileWriter.WriteAsync(plannedFile); + reporter.ReportFileWritten(plannedFile.Path); + } + + report.Complete(generatorOutput.Files.Count, FormatFileSize(totalSize), totalLines); + } + + private static string FormatFileSize(long bytes) + { + var suffixes = new[] { "B", "KB", "MB", "GB" }; + var size = (double)bytes; + var suffixIndex = 0; + + while (size >= 1024 && suffixIndex < suffixes.Length - 1) + { + size /= 1024; + suffixIndex++; + } + + return $"{size:F1} {suffixes[suffixIndex]}"; + } + + private static async Task ValidateOpenApiSpecs( + RefitGeneratorSettings refitGeneratorSettings, + IGenerationReporter reporter) + { + if (refitGeneratorSettings.OpenApiPaths is { Length: > 0 }) + { + foreach (var specPath in refitGeneratorSettings.OpenApiPaths) + await ValidateOpenApiSpec(specPath, reporter); + } + else if (refitGeneratorSettings.OpenApiPath is not null) + { + await ValidateOpenApiSpec(refitGeneratorSettings.OpenApiPath, reporter); + } + } + + private static async Task ValidateOpenApiSpec(string openApiPath, IGenerationReporter reporter) + { + var validationResult = await reporter.ValidateWithProgressAsync( + () => Validation.OpenApiValidator.Validate(openApiPath)); + + if (!validationResult.IsValid) + { + reporter.ReportValidationFailed(); + + foreach (var error in validationResult.Diagnostics.Errors) + reporter.ReportValidationDiagnostic(error, isError: true); + + foreach (var warning in validationResult.Diagnostics.Warnings) + reporter.ReportValidationDiagnostic(warning, isError: false); + + validationResult.ThrowIfInvalid(); + } + + reporter.ReportValidationStatistics(validationResult); + } + + private static void ShowWarnings( + RefitGeneratorSettings refitGeneratorSettings, + Settings settings, + IGenerationReporter reporter) + { + var warnings = new List<(string title, string description)>(); + + if (refitGeneratorSettings.UseIsoDateFormat && + refitGeneratorSettings.CodeGeneratorSettings?.DateFormat is not null) + { + warnings.Add(( + "Date Format Override", + "'codeGeneratorSettings.dateFormat' will be ignored due to 'useIsoDateFormat' set to true")); + } + +#pragma warning disable CS0618 + if (refitGeneratorSettings.DependencyInjectionSettings?.UsePolly is true) +#pragma warning restore CS0618 + { + warnings.Add(( + "Deprecated Setting", + "The 'usePolly' property is deprecated. Use 'transientErrorHandler: Polly' instead")); + } + + if (warnings.Count > 0) + reporter.ReportConfigurationWarnings(warnings); + } +} diff --git a/src/Refitter/IGenerationOrchestrator.cs b/src/Refitter/IGenerationOrchestrator.cs new file mode 100644 index 000000000..f214999ec --- /dev/null +++ b/src/Refitter/IGenerationOrchestrator.cs @@ -0,0 +1,12 @@ +using Refitter.Core; + +namespace Refitter; + +public interface IGenerationOrchestrator +{ + Task RunAsync( + RefitGeneratorSettings settings, + Settings cliSettings, + IGenerationReporter reporter, + CancellationToken cancellationToken); +} diff --git a/src/Refitter/IGenerationReporter.cs b/src/Refitter/IGenerationReporter.cs index 324e4c120..81bc0424a 100644 --- a/src/Refitter/IGenerationReporter.cs +++ b/src/Refitter/IGenerationReporter.cs @@ -11,7 +11,7 @@ namespace Refitter; /// Two adapters implement it: (plain text, /// machine-friendly) and (Spectre.Console). /// -internal interface IGenerationReporter +public interface IGenerationReporter { void ReportHeader(string version); @@ -69,7 +69,7 @@ internal interface IGenerationReporter /// prints each row as it is added (interleaved with file writes); the rich /// reporter buffers rows into a table rendered on . /// -internal interface IMultiFileOutputReport +public interface IMultiFileOutputReport { void AddFile(string fileName, string directory, string sizeFormatted, int lines);