diff --git a/src/Refitter.Core/InterfaceGenerator.cs b/src/Refitter.Core/InterfaceGenerator.cs index 32c80be72..155291736 100644 --- a/src/Refitter.Core/InterfaceGenerator.cs +++ b/src/Refitter.Core/InterfaceGenerator.cs @@ -217,13 +217,18 @@ private string GetBaseOperationName(OpenApiOperationInfo op) .GetOperationName(document, op.Path, op.Verb, op.Operation); } + private static string TrimImportedNamespaces(string returnTypeParameter) => + returnTypeParameter.StartsWith("System.Collections.Generic.", StringComparison.OrdinalIgnoreCase) + ? returnTypeParameter.Replace("System.Collections.Generic.", string.Empty) + : returnTypeParameter; + private string GetTypeName(OpenApiOperation operation) { if (settings.ResponseTypeOverride.TryGetValue(operation.OperationId, out var type)) { return type is null or "void" ? GetAsyncOperationType(true) - : $"{GetAsyncOperationType(false)}<{WellKnownNamespaces.TrimImportedNamespaces(type)}>"; + : $"{GetAsyncOperationType(false)}<{TrimImportedNamespaces(type)}>"; } if (IsFileStreamResponse(operation)) @@ -328,8 +333,8 @@ private string GetConfiguredReturnType(string returnTypeParameter) { var asyncType = GetAsyncOperationType(false); return settings.ReturnIApiResponse - ? $"{asyncType}>" - : $"{asyncType}<{WellKnownNamespaces.TrimImportedNamespaces(returnTypeParameter)}>"; + ? $"{asyncType}>" + : $"{asyncType}<{TrimImportedNamespaces(returnTypeParameter)}>"; } private string GetAsyncOperationType(bool withVoidReturnType) diff --git a/src/Refitter.Core/ParameterShared.cs b/src/Refitter.Core/ParameterShared.cs index 7272af07a..529d8bccc 100644 --- a/src/Refitter.Core/ParameterShared.cs +++ b/src/Refitter.Core/ParameterShared.cs @@ -165,12 +165,16 @@ public static string FindSupportedType(string typeName) return typeName; } + private static string TrimImportedNamespaces(string returnTypeParameter) => + returnTypeParameter.StartsWith("System.Collections.Generic.", StringComparison.OrdinalIgnoreCase) + ? returnTypeParameter.Replace("System.Collections.Generic.", string.Empty) + : returnTypeParameter; + public static string GetParameterType( ParameterModelBase parameterModel, RefitGeneratorSettings settings) { - var type = WellKnownNamespaces - .TrimImportedNamespaces( + var type = TrimImportedNamespaces( FindSupportedType( parameterModel.Type)); @@ -198,7 +202,7 @@ public static string GetQueryParameterType( public static string GetBodyAttribute(CSharpParameterModel parameter, RefitGeneratorSettings settings) { var anyType = settings.CodeGeneratorSettings?.AnyType ?? "object"; - var parameterType = WellKnownNamespaces.TrimImportedNamespaces(FindSupportedType(parameter.Type)); + var parameterType = TrimImportedNamespaces(FindSupportedType(parameter.Type)); if (parameterType.Equals(anyType, StringComparison.OrdinalIgnoreCase) || parameterType.Contains("JsonElement", StringComparison.OrdinalIgnoreCase)) diff --git a/src/Refitter.Core/WellKnownNamespaces.cs b/src/Refitter.Core/WellKnownNamespaces.cs deleted file mode 100644 index 0147cbad9..000000000 --- a/src/Refitter.Core/WellKnownNamespaces.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Refitter.Core; - -internal static class WellKnownNamespaces -{ - private static readonly string[] ImportedNamespaces = - { - "System.Collections.Generic" - }; - - public static string TrimImportedNamespaces(string returnTypeParameter) => - ImportedNamespaces - .Where(s => returnTypeParameter.StartsWith(s, StringComparison.OrdinalIgnoreCase)) - .Select(s => returnTypeParameter.Replace(s + ".", string.Empty)) - .FirstOrDefault() ?? - returnTypeParameter; -} diff --git a/src/Refitter.Tests/FileWriterTests.cs b/src/Refitter.Tests/FileWriterTests.cs deleted file mode 100644 index 11d5aa008..000000000 --- a/src/Refitter.Tests/FileWriterTests.cs +++ /dev/null @@ -1,92 +0,0 @@ -using FluentAssertions; -using TUnit.Core; - -namespace Refitter.Tests; - -public class FileWriterTests -{ - private static readonly string TestRoot = - Path.Combine(Path.GetTempPath(), "refitter-filewriter-tests"); - - [Test] - public async Task WriteAsync_Writes_Content_To_Existing_Directory() - { - var tempFile = Path.Combine(Path.GetTempPath(), $"refitter-fw-{Guid.NewGuid()}.cs"); - const string content = "// generated code"; - - try - { - await FileWriter.WriteAsync(tempFile, content); - - File.Exists(tempFile).Should().BeTrue(); - (await File.ReadAllTextAsync(tempFile)).Should().Be(content); - } - finally - { - if (File.Exists(tempFile)) - File.Delete(tempFile); - } - } - - [Test] - public async Task WriteAsync_Creates_Missing_Directory() - { - var subDir = Path.Combine(TestRoot, Guid.NewGuid().ToString(), "nested"); - var filePath = Path.Combine(subDir, "Output.cs"); - const string content = "// code in new directory"; - - try - { - await FileWriter.WriteAsync(filePath, content); - - File.Exists(filePath).Should().BeTrue(); - (await File.ReadAllTextAsync(filePath)).Should().Be(content); - } - finally - { - if (Directory.Exists(subDir)) - Directory.Delete(subDir, recursive: true); - } - } - - [Test] - public async Task WriteAsync_PlannedFile_Writes_Content() - { - var tempFile = Path.Combine(Path.GetTempPath(), $"refitter-fw-planned-{Guid.NewGuid()}.cs"); - const string content = "// planned content"; - - try - { - await FileWriter.WriteAsync(new PlannedFile(tempFile, content)); - - File.Exists(tempFile).Should().BeTrue(); - (await File.ReadAllTextAsync(tempFile)).Should().Be(content); - } - finally - { - if (File.Exists(tempFile)) - File.Delete(tempFile); - } - } - - [Test] - public async Task WriteAsync_PlannedFile_Creates_Missing_Directory() - { - var subDir = Path.Combine(TestRoot, Guid.NewGuid().ToString(), "planned"); - var filePath = Path.Combine(subDir, "Planned.cs"); - const string content = "// planned in new dir"; - - try - { - await FileWriter.WriteAsync(new PlannedFile(filePath, content)); - - File.Exists(filePath).Should().BeTrue(); - (await File.ReadAllTextAsync(filePath)).Should().Be(content); - } - finally - { - if (Directory.Exists(subDir)) - Directory.Delete(subDir, recursive: true); - } - } -} diff --git a/src/Refitter/FileWriter.cs b/src/Refitter/FileWriter.cs deleted file mode 100644 index 8f43e2a7e..000000000 --- a/src/Refitter/FileWriter.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Refitter; - -/// -/// Thin filesystem sink for planned output. Owns the "ensure directory exists, -/// then write the file" mechanics so the command code no longer interleaves -/// directory creation with reporting. The GeneratedFile: marker stays -/// with the caller (see ) -/// because it is observable stdout, not a filesystem concern. -/// -internal static class FileWriter -{ - public static async Task WriteAsync(string path, string content) - { - var directory = Path.GetDirectoryName(path); - if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory)) - Directory.CreateDirectory(directory); - - await File.WriteAllTextAsync(path, content); - } - - public static Task WriteAsync(PlannedFile file) => WriteAsync(file.Path, file.Content); -} diff --git a/src/Refitter/GenerationOrchestrator.cs b/src/Refitter/GenerationOrchestrator.cs index e9bec20f6..81891c6b6 100644 --- a/src/Refitter/GenerationOrchestrator.cs +++ b/src/Refitter/GenerationOrchestrator.cs @@ -100,7 +100,10 @@ private static async Task WriteSingleFile( reporter.ReportSingleFileOutput(fileName, directory, sizeFormatted, lines); - await FileWriter.WriteAsync(planned); + var dir = Path.GetDirectoryName(planned.Path); + if (!string.IsNullOrWhiteSpace(dir) && !Directory.Exists(dir)) + Directory.CreateDirectory(dir); + await File.WriteAllTextAsync(planned.Path, planned.Content); reporter.ReportFileWritten(planned.Path); } @@ -136,7 +139,10 @@ private static async Task WriteMultipleFiles( totalSize += size; totalLines += lines; - await FileWriter.WriteAsync(plannedFile); + var plannedDir = Path.GetDirectoryName(plannedFile.Path); + if (!string.IsNullOrWhiteSpace(plannedDir) && !Directory.Exists(plannedDir)) + Directory.CreateDirectory(plannedDir); + await File.WriteAllTextAsync(plannedFile.Path, plannedFile.Content); reporter.ReportFileWritten(plannedFile.Path); }