-
-
Notifications
You must be signed in to change notification settings - Fork 64
Extract RefitterRunner as shared generation workflow #1166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
12778c6
50b0e41
a857c75
2328d1c
fb39173
93eb7ee
c1f6dc6
5dc492c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Refitter.Core.Validation; | ||
|
|
||
| namespace Refitter.Core; | ||
|
|
||
| /// <summary> | ||
| /// Validates an OpenAPI specification file and returns validation diagnostics and statistics. | ||
| /// Each distribution form (CLI, MSBuild, Source Generator) can provide its own adapter. | ||
| /// </summary> | ||
| public interface IValidator | ||
| { | ||
| /// <summary> | ||
| /// Validates the specified OpenAPI specification file. | ||
| /// </summary> | ||
| /// <param name="openApiPath">The path to the OpenAPI specification file.</param> | ||
| /// <param name="cancellationToken">A cancellation token.</param> | ||
| /// <returns>An <see cref="OpenApiValidationResult"/> containing diagnostics and element counts.</returns> | ||
| Task<OpenApiValidationResult> ValidateAsync(string openApiPath, CancellationToken cancellationToken = default); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Refitter.Core.Validation; | ||
|
|
||
| namespace Refitter.Core; | ||
|
|
||
| /// <summary> | ||
| /// Encapsulates the shared generation workflow across all distribution forms (CLI, MSBuild, Source Generator). | ||
| /// Handles generator creation, code generation, output planning, validation, warning detection, and file writing. | ||
| /// </summary> | ||
| public class RefitterRunner | ||
| { | ||
| /// <summary> | ||
| /// Runs the complete generation workflow. | ||
| /// </summary> | ||
| /// <param name="settings">The generator settings.</param> | ||
| /// <param name="writer">Optional file writer for writing output files. When null, files are not written.</param> | ||
| /// <param name="validator">Optional validator for OpenAPI spec validation. When null, validation is skipped.</param> | ||
| /// <param name="settingsFilePath">Optional path to the .refitter settings file, used for resolving relative output paths.</param> | ||
| /// <param name="outputPath">Optional CLI output path override.</param> | ||
| /// <param name="cancellationToken">A cancellation token.</param> | ||
| /// <returns>A <see cref="RunResult"/> containing generated files, warnings, diagnostics, and the exit code.</returns> | ||
| public async Task<RunResult> RunAsync( | ||
|
Check warning on line 26 in src/Refitter.Core/RefitterRunner.cs
|
||
| RefitGeneratorSettings settings, | ||
| IFileWriter? writer = null, | ||
| IValidator? validator = null, | ||
| string? settingsFilePath = null, | ||
| string? outputPath = null, | ||
| CancellationToken cancellationToken = default) | ||
| { | ||
| var stopwatch = Stopwatch.StartNew(); | ||
| var warnings = new List<Warning>(); | ||
| var diagnostics = new List<RunnerDiagnostic>(); | ||
|
|
||
| try | ||
| { | ||
| var generator = await RefitGenerator.CreateAsync( | ||
| settings, | ||
| cancellationToken); | ||
|
|
||
| GeneratorOutput output; | ||
| if (settings.GenerateMultipleFiles) | ||
| { | ||
| output = generator.GenerateMultipleFiles(); | ||
| } | ||
| else | ||
| { | ||
| var code = generator.Generate() | ||
| .Replace("\r\n", "\n") | ||
| .Replace("\r", "\n"); | ||
| output = new GeneratorOutput( | ||
| new List<GeneratedCode> { new(string.Empty, code) }); | ||
| } | ||
|
|
||
| var planner = new OutputPlannerAdapter(); | ||
| var plannedFiles = planner.Plan( | ||
| output, | ||
| settings, | ||
| settingsFilePath, | ||
| outputPath); | ||
|
|
||
| if (validator != null) | ||
| { | ||
| var openApiPaths = GetOpenApiPaths(settings); | ||
| foreach (var specPath in openApiPaths) | ||
|
Check warning on line 68 in src/Refitter.Core/RefitterRunner.cs
|
||
| { | ||
| if (!string.IsNullOrWhiteSpace(specPath)) | ||
| { | ||
| var validationResult = await validator.ValidateAsync( | ||
| specPath, | ||
| cancellationToken); | ||
|
|
||
| foreach (var error in validationResult.Diagnostics.Errors) | ||
| diagnostics.Add(new RunnerDiagnostic(error.Message, IsError: true)); | ||
|
|
||
| foreach (var warning in validationResult.Diagnostics.Warnings) | ||
| diagnostics.Add(new RunnerDiagnostic(warning.Message, IsError: false)); | ||
|
|
||
| validationResult.ThrowIfInvalid(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (writer != null) | ||
| { | ||
| foreach (var file in plannedFiles) | ||
| { | ||
| await writer.WriteAsync(file, cancellationToken); | ||
| } | ||
| } | ||
|
|
||
| DetectWarnings(settings, warnings); | ||
|
|
||
| if (settings.IncludePathMatches.Length > 0 && | ||
| generator.OpenApiDocument.Paths.Count == 0) | ||
| { | ||
| diagnostics.Add(new RunnerDiagnostic( | ||
| $"All paths were filtered out by include path patterns: [{string.Join(", ", settings.IncludePathMatches)}]", | ||
| IsError: false)); | ||
| } | ||
|
|
||
| stopwatch.Stop(); | ||
|
|
||
| return new RunResult( | ||
| plannedFiles, | ||
| warnings.AsReadOnly(), | ||
| diagnostics.AsReadOnly(), | ||
| stopwatch.Elapsed, | ||
| ExitCode: 0); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| stopwatch.Stop(); | ||
|
|
||
| var errorDiagnostics = new List<RunnerDiagnostic>(diagnostics) | ||
| { | ||
| new RunnerDiagnostic(ex.Message, IsError: true) | ||
| }; | ||
|
|
||
| return new RunResult( | ||
| Array.Empty<PlannedFile>(), | ||
| warnings.AsReadOnly(), | ||
| errorDiagnostics.AsReadOnly(), | ||
| stopwatch.Elapsed, | ||
| ex.HResult, | ||
| ex); | ||
| } | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| private static string[] GetOpenApiPaths(RefitGeneratorSettings settings) | ||
| { | ||
| if (settings.OpenApiPaths is { Length: > 0 }) | ||
| return settings.OpenApiPaths; | ||
|
|
||
| if (settings.OpenApiPath is not null) | ||
| return new[] { settings.OpenApiPath }; | ||
|
|
||
| return Array.Empty<string>(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Detects configuration warnings in the settings and adds them to the provided list. | ||
| /// </summary> | ||
| internal static void DetectWarnings( | ||
| RefitGeneratorSettings settings, | ||
| List<Warning> warnings) | ||
| { | ||
| if (settings is { UseIsoDateFormat: true, CodeGeneratorSettings.DateFormat: not null }) | ||
| { | ||
| warnings.Add( | ||
| new Warning( | ||
| "Date Format Override", | ||
| "'codeGeneratorSettings.dateFormat' will be ignored due to 'useIsoDateFormat' set to true")); | ||
| } | ||
|
|
||
| #pragma warning disable CS0618 | ||
| if (settings.DependencyInjectionSettings?.UsePolly is true) | ||
| #pragma warning restore CS0618 | ||
| { | ||
| warnings.Add( | ||
| new Warning( | ||
| "Deprecated Setting", | ||
| "The 'usePolly' property is deprecated. Use 'transientErrorHandler: Polly' instead")); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
|
|
||
| namespace Refitter.Core; | ||
|
|
||
| /// <summary> | ||
| /// Represents the result of a <see cref="RefitterRunner.RunAsync"/> invocation. | ||
| /// </summary> | ||
| /// <param name="GeneratedFiles">The list of planned output files with their paths and content.</param> | ||
| /// <param name="Warnings">Configuration warnings detected during the run.</param> | ||
| /// <param name="Diagnostics">Validation and error diagnostics collected during the run.</param> | ||
| /// <param name="Elapsed">The elapsed time of the run.</param> | ||
| /// <param name="ExitCode">The exit code (0 for success, non-zero for failure).</param> | ||
| /// <param name="Exception">The original exception, if any, for form-specific error handling.</param> | ||
| public record RunResult( | ||
| IReadOnlyList<PlannedFile> GeneratedFiles, | ||
| IReadOnlyList<Warning> Warnings, | ||
| IReadOnlyList<RunnerDiagnostic> Diagnostics, | ||
| TimeSpan Elapsed, | ||
| int ExitCode, | ||
| Exception? Exception = null); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| namespace Refitter.Core; | ||
|
|
||
| /// <summary> | ||
| /// Represents a diagnostic message from a generation run, such as a validation error or warning. | ||
| /// </summary> | ||
| /// <param name="Message">The diagnostic message text.</param> | ||
| /// <param name="IsError">Whether this diagnostic represents an error (true) or a warning (false).</param> | ||
| public record RunnerDiagnostic(string Message, bool IsError); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace Refitter.Core.Validation; | ||
|
|
||
| /// <summary> | ||
| /// Adapts the static <see cref="OpenApiValidator"/> to the <see cref="IValidator"/> interface. | ||
| /// </summary> | ||
| public sealed class OpenApiValidatorAdapter : IValidator | ||
| { | ||
| /// <inheritdoc /> | ||
| public async Task<OpenApiValidationResult> ValidateAsync(string openApiPath, CancellationToken cancellationToken = default) | ||
| { | ||
| return await OpenApiValidator.Validate(openApiPath, cancellationToken); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| namespace Refitter.Core; | ||
|
|
||
| /// <summary> | ||
| /// Represents a user-facing configuration warning message. | ||
| /// </summary> | ||
| /// <param name="Title">The short title of the warning.</param> | ||
| /// <param name="Description">The extended description of the warning.</param> | ||
| public record Warning(string Title, string Description); |
Uh oh!
There was an error while loading. Please reload this page.