diff --git a/CHANGELOG.md b/CHANGELOG.md index c717546586..d29ee7d31b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Suppress CS1591 when generating CSharp code and documentation is not available +- Added file name suffix escaping in Go to avoid generating files with reserved suffixes. [#4407](https://github.com/microsoft/kiota/issues/4407) ### Changed diff --git a/src/Kiota.Builder/Extensions/StringExtensions.cs b/src/Kiota.Builder/Extensions/StringExtensions.cs index 46a9258f6c..44738cec93 100644 --- a/src/Kiota.Builder/Extensions/StringExtensions.cs +++ b/src/Kiota.Builder/Extensions/StringExtensions.cs @@ -57,6 +57,15 @@ public static string ShortenFileName(this string name, int length = 251) => (!string.IsNullOrEmpty(name) && name.Length > length) ? HashString(name).ToLowerInvariant() : name; #pragma warning restore CA1308 + public static string EscapeSuffix(this string? name, HashSet specialFileNameSuffixes, char separator = '_') + { + ArgumentNullException.ThrowIfNull(specialFileNameSuffixes); + if (string.IsNullOrEmpty(name)) return string.Empty; + + var last = name.Split(separator)[^1]; + return specialFileNameSuffixes.Contains(last) ? $"{name}_escaped" : name; + } + public static string ToSnakeCase(this string? name, char separator = '_') { if (string.IsNullOrEmpty(name)) return string.Empty; diff --git a/src/Kiota.Builder/PathSegmenters/GoPathSegmenter.cs b/src/Kiota.Builder/PathSegmenters/GoPathSegmenter.cs index 4a1a29e8e0..90306fab87 100644 --- a/src/Kiota.Builder/PathSegmenters/GoPathSegmenter.cs +++ b/src/Kiota.Builder/PathSegmenters/GoPathSegmenter.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Kiota.Builder.CodeDOM; @@ -7,6 +8,67 @@ namespace Kiota.Builder.PathSegmenters; public class GoPathSegmenter : CommonPathSegmenter { + private static readonly HashSet specialFileNameSuffixes = new(StringComparer.OrdinalIgnoreCase) { + "test" , + + "aix" , + "android" , + "darwin" , + "dragonfly" , + "freebsd" , + "hurd" , + "illumos" , + "ios" , + "js" , + "linux" , + "nacl" , + "netbsd" , + "openbsd" , + "plan9" , + "solaris" , + "wasip1" , + "windows" , + "zos" , + + "aix" , + "android" , + "darwin" , + "dragonfly" , + "freebsd" , + "hurd" , + "illumos" , + "ios" , + "linux" , + "netbsd" , + "openbsd" , + "solaris" , + + "386" , + "amd64" , + "amd64p32" , + "arm" , + "armbe" , + "arm64" , + "arm64be" , + "loong64" , + "mips" , + "mipsle" , + "mips64" , + "mips64le" , + "mips64p32" , + "mips64p32le" , + "ppc" , + "ppc64" , + "ppc64le" , + "riscv" , + "riscv64" , + "s390" , + "s390x" , + "sparc" , + "sparc64" , + "wasm" , + }; + public GoPathSegmenter(string rootPath, string clientNamespaceName) : base(rootPath, clientNamespaceName) { } public override string FileSuffix => ".go"; public override IEnumerable GetAdditionalSegment(CodeElement currentElement, string fileName) @@ -17,12 +79,13 @@ public override IEnumerable GetAdditionalSegment(CodeElement currentElem _ => Enumerable.Empty(), }; } + public override string NormalizeFileName(CodeElement currentElement) { return currentElement switch { CodeNamespace => "go", - _ => GetLastFileNameSegment(currentElement).ToSnakeCase().ShortenFileName(), + _ => GetLastFileNameSegment(currentElement).ToSnakeCase().EscapeSuffix(specialFileNameSuffixes).ShortenFileName(), }; } public override string NormalizeNamespaceSegment(string segmentName) => segmentName?.ToLowerInvariant() ?? string.Empty; diff --git a/tests/Kiota.Builder.Tests/PathSegmenters/GoPathSegmenterTests.cs b/tests/Kiota.Builder.Tests/PathSegmenters/GoPathSegmenterTests.cs new file mode 100644 index 0000000000..7251d54cf6 --- /dev/null +++ b/tests/Kiota.Builder.Tests/PathSegmenters/GoPathSegmenterTests.cs @@ -0,0 +1,42 @@ +using Kiota.Builder.CodeDOM; +using Kiota.Builder.PathSegmenters; +using Xunit; + +namespace Kiota.Builder.Tests.PathSegmenters; + +public class GoPathSegmenterTests +{ + private readonly GoPathSegmenter segmenter; + public GoPathSegmenterTests() + { + segmenter = new GoPathSegmenter("D:\\source\\repos\\kiota-sample", "client"); + } + + [Fact] + public void GoPathSegmenterGeneratesCorrectFileName() + { + var fileName = segmenter.NormalizeFileName(new CodeClass + { + Name = "testClass" + }); + Assert.Equal("test_class", fileName); // file name should be snake case + } + + [Fact] + public void GoPathSegmenterGeneratesEscapedSpecialClassName() + { + var fileName = segmenter.NormalizeFileName(new CodeClass + { + Name = "adminWindows" + }); + Assert.Equal("admin_windows_escaped", fileName); // file name should be snake case and escaped + } + + [Fact] + public void GoPathSegmenterGeneratesNamespaceFolderName() + { + var namespaceName = "Microsoft.Graph"; + var normalizedNamespace = segmenter.NormalizeNamespaceSegment(namespaceName); + Assert.Equal("microsoft.graph", normalizedNamespace);// the file name should be lower case + } +}