From 07b9edc70ff18803881ee6ea4499af2d85eafa1d Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 27 Nov 2023 13:12:56 -0500 Subject: [PATCH] - refactoring: all regexes use a generate attribute Signed-off-by: Vincent Biret --- .../StructuredMimeTypesCollection.cs | 7 ++- src/Kiota.Builder/Constants.cs | 6 +-- .../OpenApiServerComparer.cs | 5 +- .../Extensions/OpenApiOperationExtensions.cs | 7 +-- .../OpenApiUrlTreeNodeExtensions.cs | 48 +++++++++++-------- .../Extensions/StringExtensions.cs | 13 ++--- src/Kiota.Builder/KiotaBuilder.cs | 7 ++- src/Kiota.Builder/Refiners/RubyRefiner.cs | 2 +- .../Writers/CLI/CliCodeMethodWriter.cs | 21 ++++---- .../Writers/Java/CodeMethodWriter.cs | 7 +-- .../Writers/Java/JavaConventionService.cs | 7 +-- .../Writers/Php/CodeEnumWriter.cs | 7 +-- src/kiota/KiotaHost.cs | 12 +++-- src/kiota/Rpc/Server.cs | 7 +-- 14 files changed, 81 insertions(+), 75 deletions(-) diff --git a/src/Kiota.Builder/Configuration/StructuredMimeTypesCollection.cs b/src/Kiota.Builder/Configuration/StructuredMimeTypesCollection.cs index 6f26e18d12..8e36bb980f 100644 --- a/src/Kiota.Builder/Configuration/StructuredMimeTypesCollection.cs +++ b/src/Kiota.Builder/Configuration/StructuredMimeTypesCollection.cs @@ -127,9 +127,8 @@ public IEnumerable GetContentTypes(IEnumerable searchTypes) .ThenByDescending(static x => x.Key, StringComparer.OrdinalIgnoreCase) .Select(static x => x.Key); } - [GeneratedRegex(@"[^/+]+\+", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline, 2000)] + [GeneratedRegex(@"[^/+]+\+", RegexOptions.IgnoreCase | RegexOptions.Singleline, 2000)] private static partial Regex vendorStripRegex(); - private readonly static Regex vendorStripRegexInstance = vendorStripRegex(); private bool TryGetMimeType(string mimeType, out float result) { if (string.IsNullOrEmpty(mimeType)) @@ -140,10 +139,10 @@ private bool TryGetMimeType(string mimeType, out float result) return _mimeTypes.TryGetValue(mimeType, out result) || // vendor and parameters mimeType.Contains('+', StringComparison.OrdinalIgnoreCase) && - _mimeTypes.TryGetValue(vendorStripRegexInstance.Replace(mimeType, string.Empty), out result) || // no vendor with parameters + _mimeTypes.TryGetValue(vendorStripRegex().Replace(mimeType, string.Empty), out result) || // no vendor with parameters mimeType.Contains(';', StringComparison.OrdinalIgnoreCase) && mimeType.Split(';', StringSplitOptions.RemoveEmptyEntries)[0] is string noParametersMimeType && (_mimeTypes.TryGetValue(noParametersMimeType, out result) || // vendor without parameters - _mimeTypes.TryGetValue(vendorStripRegexInstance.Replace(noParametersMimeType, string.Empty), out result)); // no vendor without parameters + _mimeTypes.TryGetValue(vendorStripRegex().Replace(noParametersMimeType, string.Empty), out result)); // no vendor without parameters } } diff --git a/src/Kiota.Builder/Constants.cs b/src/Kiota.Builder/Constants.cs index a1a9400621..1968837aa6 100644 --- a/src/Kiota.Builder/Constants.cs +++ b/src/Kiota.Builder/Constants.cs @@ -1,10 +1,6 @@ -using System; - -namespace Kiota.Builder; +namespace Kiota.Builder; public static class Constants { public const string DefaultOpenApiLabel = "default"; - public const string RawUrlParameterName = "request-raw-url"; - public static readonly TimeSpan DefaultRegexTimeout = TimeSpan.FromMilliseconds(100); public const string TempDirectoryName = "kiota"; } diff --git a/src/Kiota.Builder/EqualityComparers/OpenApiServerComparer.cs b/src/Kiota.Builder/EqualityComparers/OpenApiServerComparer.cs index da206ac7c5..dca85c9a02 100644 --- a/src/Kiota.Builder/EqualityComparers/OpenApiServerComparer.cs +++ b/src/Kiota.Builder/EqualityComparers/OpenApiServerComparer.cs @@ -8,9 +8,8 @@ namespace Kiota.Builder.EqualityComparers; internal sealed partial class OpenApiServerComparer : IEqualityComparer { - private static readonly Regex _protocolCleanupRegex = GetCleanupRegex(); [GeneratedRegex("^https?://", RegexOptions.IgnoreCase | RegexOptions.Compiled, 200)] - private static partial Regex GetCleanupRegex(); + private static partial Regex protocolCleanupRegex(); public bool Equals(OpenApiServer? x, OpenApiServer? y) { return x != null && y != null && GetHashCode(x) == GetHashCode(y); @@ -19,6 +18,6 @@ public int GetHashCode([DisallowNull] OpenApiServer obj) { if (string.IsNullOrEmpty(obj?.Url)) return 0; - return _protocolCleanupRegex.Replace(obj.Url, string.Empty).GetHashCode(StringComparison.OrdinalIgnoreCase); + return protocolCleanupRegex().Replace(obj.Url, string.Empty).GetHashCode(StringComparison.OrdinalIgnoreCase); } } diff --git a/src/Kiota.Builder/Extensions/OpenApiOperationExtensions.cs b/src/Kiota.Builder/Extensions/OpenApiOperationExtensions.cs index 767ac3696a..7995564db5 100644 --- a/src/Kiota.Builder/Extensions/OpenApiOperationExtensions.cs +++ b/src/Kiota.Builder/Extensions/OpenApiOperationExtensions.cs @@ -6,13 +6,14 @@ using Microsoft.OpenApi.Models; namespace Kiota.Builder.Extensions; -public static class OpenApiOperationExtensions +public static partial class OpenApiOperationExtensions { internal static readonly HashSet SuccessCodes = new(StringComparer.OrdinalIgnoreCase) { "200", "201", "202", "203", "206", "2XX" }; //204 excluded as it won't have a schema + [GeneratedRegex(@"[^/]+\+", RegexOptions.IgnoreCase | RegexOptions.Singleline, 100)] + private static partial Regex vendorSpecificCleanup(); /// /// cleans application/vnd.github.mercy-preview+json to application/json /// - private static readonly Regex vendorSpecificCleanup = new(@"[^/]+\+", RegexOptions.Compiled, Constants.DefaultRegexTimeout); internal static OpenApiSchema? GetResponseSchema(this OpenApiOperation operation, StructuredMimeTypesCollection structuredMimeTypes) { ArgumentNullException.ThrowIfNull(operation); @@ -48,7 +49,7 @@ internal static IEnumerable GetValidSchemas(this IDictionary !string.IsNullOrEmpty(c.Key)) .Select(static c => (Key: c.Key.Split(';', StringSplitOptions.RemoveEmptyEntries)[0], c.Value)) - .Where(c => structuredMimeTypes.Contains(c.Key) || structuredMimeTypes.Contains(vendorSpecificCleanup.Replace(c.Key, string.Empty))) + .Where(c => structuredMimeTypes.Contains(c.Key) || structuredMimeTypes.Contains(vendorSpecificCleanup().Replace(c.Key, string.Empty))) .Select(static co => co.Value.Schema) .Where(static s => s is not null); } diff --git a/src/Kiota.Builder/Extensions/OpenApiUrlTreeNodeExtensions.cs b/src/Kiota.Builder/Extensions/OpenApiUrlTreeNodeExtensions.cs index 3282ccba7a..4f3b65ae21 100644 --- a/src/Kiota.Builder/Extensions/OpenApiUrlTreeNodeExtensions.cs +++ b/src/Kiota.Builder/Extensions/OpenApiUrlTreeNodeExtensions.cs @@ -8,7 +8,7 @@ using Microsoft.OpenApi.Services; namespace Kiota.Builder.Extensions; -public static class OpenApiUrlTreeNodeExtensions +public static partial class OpenApiUrlTreeNodeExtensions { private static string GetDotIfBothNotNullOfEmpty(string x, string y) => string.IsNullOrEmpty(x) || string.IsNullOrEmpty(y) ? string.Empty : "."; private static readonly Func replaceSingleParameterSegmentByItem = @@ -38,9 +38,11 @@ public static string GetNodeNamespaceFromPath(this OpenApiUrlTreeNode currentNod return currentNode.Path.GetNamespaceFromPath(prefix); } //{id}, name(idParam={id}), name(idParam='{id}'), name(idParam='{id}',idParam2='{id2}') - private static readonly Regex PathParametersRegex = new(@"(?:\w+)?=?'?\{(?\w+)\}'?,?", RegexOptions.Compiled, Constants.DefaultRegexTimeout); + [GeneratedRegex(@"(?:\w+)?=?'?\{(?\w+)\}'?,?", RegexOptions.Singleline, 100)] + private static partial Regex PathParametersRegex(); // microsoft.graph.getRoleScopeTagsByIds(ids=@ids) - private static readonly Regex AtSignPathParameterRegex = new(@"=@(\w+)", RegexOptions.Compiled, Constants.DefaultRegexTimeout); + [GeneratedRegex(@"=@(\w+)", RegexOptions.Singleline, 100)] + private static partial Regex AtSignPathParameterRegex(); private const char RequestParametersChar = '{'; private const char RequestParametersEndChar = '}'; private const string RequestParametersSectionChar = "("; @@ -52,8 +54,8 @@ private static string CleanupParametersFromPath(string pathSegment) { if (string.IsNullOrEmpty(pathSegment)) return pathSegment; - return PathParametersRegex.Replace( - AtSignPathParameterRegex.Replace(pathSegment, "={$1}"), + return PathParametersRegex().Replace( + AtSignPathParameterRegex().Replace(pathSegment, "={$1}"), requestParametersMatchEvaluator) .Replace(RequestParametersSectionEndChar, string.Empty, StringComparison.OrdinalIgnoreCase) .Replace(RequestParametersSectionChar, string.Empty, StringComparison.OrdinalIgnoreCase); @@ -79,7 +81,8 @@ public static IEnumerable GetPathParametersForCurrentSegment(t return Enumerable.Empty(); } private const char PathNameSeparator = '\\'; - private static readonly Regex idClassNameCleanup = new(@"-?id\d?}?$", RegexOptions.Compiled | RegexOptions.IgnoreCase, Constants.DefaultRegexTimeout); + [GeneratedRegex(@"-?id\d?}?$", RegexOptions.Singleline | RegexOptions.IgnoreCase, 100)] + private static partial Regex idClassNameCleanup(); /// /// Returns the class name for the node with more or less precision depending on the provided arguments /// @@ -109,11 +112,11 @@ private static string GetSegmentName(this OpenApiUrlTreeNode currentNode, Struct CleanupParametersFromPath(currentNode.Segment)?.ReplaceValueIdentifier())); if (!string.IsNullOrEmpty(rawClassName) && string.IsNullOrEmpty(referenceName)) { - if (stripExtensionForIndexersTestRegex.IsMatch(rawClassName)) - rawClassName = stripExtensionForIndexersRegex.Replace(rawClassName, string.Empty); - if ((currentNode?.DoesNodeBelongToItemSubnamespace() ?? false) && idClassNameCleanup.IsMatch(rawClassName)) + if (stripExtensionForIndexersTestRegex().IsMatch(rawClassName)) + rawClassName = stripExtensionForIndexersRegex().Replace(rawClassName, string.Empty); + if ((currentNode?.DoesNodeBelongToItemSubnamespace() ?? false) && idClassNameCleanup().IsMatch(rawClassName)) { - rawClassName = idClassNameCleanup.Replace(rawClassName, string.Empty); + rawClassName = idClassNameCleanup().Replace(rawClassName, string.Empty); if (WithKeyword.Equals(rawClassName, StringComparison.Ordinal)) // in case the single parameter doesn't follow {classname-id} we get the previous segment rawClassName = currentNode.Path .Split(PathNameSeparator, StringSplitOptions.RemoveEmptyEntries) @@ -139,8 +142,9 @@ private static string GetSegmentName(this OpenApiUrlTreeNode currentNode, Struct "yml", "txt", }; - private static readonly Regex descriptionCleanupRegex = new(@"[\r\n\t]", RegexOptions.Compiled, Constants.DefaultRegexTimeout); - public static string CleanupDescription(this string? description) => string.IsNullOrEmpty(description) ? string.Empty : descriptionCleanupRegex.Replace(description, string.Empty); + [GeneratedRegex(@"[\r\n\t]", RegexOptions.Singleline, 100)] + private static partial Regex descriptionCleanupRegex(); + public static string CleanupDescription(this string? description) => string.IsNullOrEmpty(description) ? string.Empty : descriptionCleanupRegex().Replace(description, string.Empty); public static string GetPathItemDescription(this OpenApiUrlTreeNode currentNode, string label, string? defaultValue = default) { if (currentNode != null && !string.IsNullOrEmpty(label) && currentNode.PathItems.TryGetValue(label, out var pathItem)) @@ -158,7 +162,7 @@ public static bool IsPathSegmentWithSingleSimpleParameter(this OpenApiUrlTreeNod private static bool IsPathSegmentWithSingleSimpleParameter(this string currentSegment) { if (string.IsNullOrEmpty(currentSegment)) return false; - var segmentWithoutExtension = stripExtensionForIndexersRegex.Replace(currentSegment, string.Empty); + var segmentWithoutExtension = stripExtensionForIndexersRegex().Replace(currentSegment, string.Empty); return segmentWithoutExtension.StartsWith(RequestParametersChar) && segmentWithoutExtension.EndsWith(RequestParametersEndChar) && @@ -170,8 +174,10 @@ private static bool IsPathSegmentWithNumberOfParameters(this string currentSegme return eval(currentSegment.Where(static x => x == RequestParametersChar)); } - private static readonly Regex stripExtensionForIndexersRegex = new(@"\.(?:json|yaml|yml|csv|txt)$", RegexOptions.Compiled, Constants.DefaultRegexTimeout); // so {param-name}.json is considered as indexer - private static readonly Regex stripExtensionForIndexersTestRegex = new(@"\{\w+\}\.(?:json|yaml|yml|csv|txt)$", RegexOptions.Compiled, Constants.DefaultRegexTimeout); // so {param-name}.json is considered as indexer + [GeneratedRegex(@"\.(?:json|yaml|yml|csv|txt)$", RegexOptions.Singleline, 100)] + private static partial Regex stripExtensionForIndexersRegex(); // so {param-name}.json is considered as indexer + [GeneratedRegex(@"\{\w+\}\.(?:json|yaml|yml|csv|txt)$", RegexOptions.Singleline, 100)] + private static partial Regex stripExtensionForIndexersTestRegex(); // so {param-name}.json is considered as indexer public static bool IsComplexPathMultipleParameters(this OpenApiUrlTreeNode currentNode) => (currentNode?.Segment?.IsPathSegmentWithNumberOfParameters(static x => x.Any()) ?? false) && !currentNode.IsPathSegmentWithSingleSimpleParameter(); public static string GetUrlTemplate(this OpenApiUrlTreeNode currentNode) @@ -209,11 +215,12 @@ public static string GetUrlTemplate(this OpenApiUrlTreeNode currentNode) SanitizePathParameterNamesForUrlTemplate(currentNode.Path.Replace('\\', '/'), pathReservedPathParametersIds) + queryStringParameters; } - private static readonly Regex pathParamMatcher = new(@"{(?[^}]+)}", RegexOptions.Compiled, Constants.DefaultRegexTimeout); + [GeneratedRegex(@"{(?[^}]+)}", RegexOptions.Singleline, 100)] + private static partial Regex pathParamMatcher(); private static string SanitizePathParameterNamesForUrlTemplate(string original, HashSet reservedParameterNames) { if (string.IsNullOrEmpty(original) || !original.Contains('{', StringComparison.OrdinalIgnoreCase)) return original; - var parameters = pathParamMatcher.Matches(original); + var parameters = pathParamMatcher().Matches(original); foreach (var value in parameters.Select(x => x.Groups["paramname"].Value)) original = original.Replace(value, (reservedParameterNames.Contains(value) ? "+" : string.Empty) + value.SanitizeParameterNameForUrlTemplate(), StringComparison.Ordinal); return original; @@ -221,7 +228,7 @@ private static string SanitizePathParameterNamesForUrlTemplate(string original, public static string SanitizeParameterNameForUrlTemplate(this string original) { if (string.IsNullOrEmpty(original)) return original; - return Uri.EscapeDataString(stripExtensionForIndexersRegex + return Uri.EscapeDataString(stripExtensionForIndexersRegex() .Replace(original, string.Empty) // {param-name}.json becomes {param-name} .TrimStart('{') .TrimEnd('}')) @@ -229,10 +236,11 @@ public static string SanitizeParameterNameForUrlTemplate(this string original) .Replace(".", "%2E", StringComparison.OrdinalIgnoreCase) .Replace("~", "%7E", StringComparison.OrdinalIgnoreCase);// - . ~ are invalid uri template character but don't get encoded by Uri.EscapeDataString } - private static readonly Regex removePctEncodedCharacters = new(@"%[0-9A-F]{2}", RegexOptions.Compiled, Constants.DefaultRegexTimeout); + [GeneratedRegex(@"%[0-9A-F]{2}", RegexOptions.Singleline, 100)] + private static partial Regex removePctEncodedCharacters(); public static string SanitizeParameterNameForCodeSymbols(this string original, string replaceEncodedCharactersWith = "") { if (string.IsNullOrEmpty(original)) return original; - return removePctEncodedCharacters.Replace(original.ToCamelCase('-', '.', '~').SanitizeParameterNameForUrlTemplate(), replaceEncodedCharactersWith); + return removePctEncodedCharacters().Replace(original.ToCamelCase('-', '.', '~').SanitizeParameterNameForUrlTemplate(), replaceEncodedCharactersWith); } } diff --git a/src/Kiota.Builder/Extensions/StringExtensions.cs b/src/Kiota.Builder/Extensions/StringExtensions.cs index 00388b3e2b..ad1c39f28d 100644 --- a/src/Kiota.Builder/Extensions/StringExtensions.cs +++ b/src/Kiota.Builder/Extensions/StringExtensions.cs @@ -9,7 +9,7 @@ using System.Threading; namespace Kiota.Builder.Extensions; -public static class StringExtensions +public static partial class StringExtensions { private const int MaxStackLimit = 1024; @@ -149,7 +149,8 @@ public static string ReplaceDotsWithSlashInNamespaces(this string? namespaced) /// /// Cleanup regex that removes all special characters from ASCII 0-127 /// - private static readonly Regex propertyCleanupRegex = new(@"[""\s!#$%&'()*,./:;<=>?@\[\]\\^`’{}|~-](?\w)?", RegexOptions.Compiled, Constants.DefaultRegexTimeout); + [GeneratedRegex(@"[""\s!#$%&'()*,./:;<=>?@\[\]\\^`’{}|~-](?\w)?", RegexOptions.Singleline, 100)] + private static partial Regex propertyCleanupRegex(); private const string CleanupGroupName = "followingLetter"; public static string CleanupSymbolName(this string? original) { @@ -157,13 +158,13 @@ public static string CleanupSymbolName(this string? original) string result = NormalizeSymbolsBeforeCleanup(original); - result = propertyCleanupRegex.Replace(result, + result = propertyCleanupRegex().Replace(result, static x => x.Groups.Keys.Contains(CleanupGroupName) ? x.Groups[CleanupGroupName].Value.ToFirstCharacterUpperCase() : string.Empty); //strip out any invalid characters, and replace any following one by its uppercase version if (result.Length != 0 && int.TryParse(result.AsSpan(0, 1), out var _)) // in most languages a number or starting with a number is not a valid symbol name - result = NumbersSpellingRegex.Replace(result, static x => x.Groups["number"] + result = NumbersSpellingRegex().Replace(result, static x => x.Groups["number"] .Value .Select(static x => SpelledOutNumbers[x]) .Aggregate(static (z, y) => z + y)); @@ -180,8 +181,8 @@ public static string CleanupSymbolName(this string? original) return result; } - - private static readonly Regex NumbersSpellingRegex = new(@"^(?\d+)", RegexOptions.Compiled, Constants.DefaultRegexTimeout); + [GeneratedRegex(@"^(?\d+)", RegexOptions.Singleline, 100)] + private static partial Regex NumbersSpellingRegex(); private static readonly Dictionary SpelledOutNumbers = new() { {'0', "Zero"}, {'1', "One"}, diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index cd4c41799a..e8fa656c1c 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -300,10 +300,9 @@ private async Task UpdateLockFile(CancellationToken cancellationToken) await lockManagementService.WriteLockFileAsync(config.OutputPath, configurationLock, cancellationToken).ConfigureAwait(false); } private static readonly GlobComparer globComparer = new(); - [GeneratedRegex(@"([\/\\])\{[\w\d-]+\}([\/\\])", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline, 2000)] - private static partial Regex MultiIndexSameLevelCleanupRegexTemplate(); - private static readonly Regex MultiIndexSameLevelCleanupRegex = MultiIndexSameLevelCleanupRegexTemplate(); - private static string ReplaceAllIndexesWithWildcard(string path, uint depth = 10) => depth == 0 ? path : ReplaceAllIndexesWithWildcard(MultiIndexSameLevelCleanupRegex.Replace(path, "$1{*}$2"), depth - 1); // the bound needs to be greedy to avoid replacing anything else than single path parameters + [GeneratedRegex(@"([\/\\])\{[\w\d-]+\}([\/\\])", RegexOptions.IgnoreCase | RegexOptions.Singleline, 2000)] + private static partial Regex MultiIndexSameLevelCleanupRegex(); + private static string ReplaceAllIndexesWithWildcard(string path, uint depth = 10) => depth == 0 ? path : ReplaceAllIndexesWithWildcard(MultiIndexSameLevelCleanupRegex().Replace(path, "$1{*}$2"), depth - 1); // the bound needs to be greedy to avoid replacing anything else than single path parameters private static Dictionary> GetFilterPatternsFromConfiguration(HashSet configPatterns) { return configPatterns.Select(static x => diff --git a/src/Kiota.Builder/Refiners/RubyRefiner.cs b/src/Kiota.Builder/Refiners/RubyRefiner.cs index 84286899cf..e0c5913ac4 100644 --- a/src/Kiota.Builder/Refiners/RubyRefiner.cs +++ b/src/Kiota.Builder/Refiners/RubyRefiner.cs @@ -196,7 +196,7 @@ parameterType.TypeDefinition is CodeClass parameterTypeClass && } CrawlTree(currentElement, x => UpdateReferencesToDisambiguatedClasses(x, classesToUpdate, suffix)); } - [GeneratedRegex(@"\\.(\\w)", RegexOptions.IgnoreCase)] + [GeneratedRegex(@"\\.(\\w)", RegexOptions.IgnoreCase | RegexOptions.Singleline, 100)] private static partial Regex CapitalizedFirstLetterAfterDot(); private static void FlattenModelsNamespaces(CodeElement currentElement, CodeNamespace modelsNS) { diff --git a/src/Kiota.Builder/Writers/CLI/CliCodeMethodWriter.cs b/src/Kiota.Builder/Writers/CLI/CliCodeMethodWriter.cs index 2d06e426d2..bf0026bda6 100644 --- a/src/Kiota.Builder/Writers/CLI/CliCodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/CLI/CliCodeMethodWriter.cs @@ -12,9 +12,6 @@ namespace Kiota.Builder.Writers.Cli; partial class CliCodeMethodWriter : CodeMethodWriter { - private static readonly Regex DelimitedRegex = CliDelimitedRegex(); - private static readonly Regex CamelCaseRegex = CliCamelCaseRegex(); - private static readonly Regex UppercaseRegex = CliUppercaseRegex(); private const string AllParamType = "bool"; private const string AllParamName = "all"; private const string CancellationTokenParamType = "CancellationToken"; @@ -49,7 +46,7 @@ protected override void WriteCommandBuilderBody(CodeMethod codeElement, CodeClas { var classMethods = parentClass.Methods; var name = codeElement.SimpleName; - name = UppercaseRegex.Replace(name, "-$1").TrimStart('-').ToLowerInvariant(); + name = UppercaseRegex().Replace(name, "-$1").TrimStart('-').ToLowerInvariant(); if (codeElement.HttpMethod == null) { @@ -877,17 +874,17 @@ private static string NormalizeToIdentifier(string input) /// private static string NormalizeToOption(string input) { - var result = CamelCaseRegex.Replace(input, "-$1"); + var result = CamelCaseRegex().Replace(input, "-$1"); // 2 passes for cases like "singleValueLegacyExtendedProperty_id" - result = DelimitedRegex.Replace(result, "-$1"); + result = DelimitedRegex().Replace(result, "-$1"); return result.ToLowerInvariant(); } - [GeneratedRegex("(?<=[a-z])[-_\\.]+([A-Za-z])", RegexOptions.Compiled)] - private static partial Regex CliDelimitedRegex(); - [GeneratedRegex("(?<=[a-z])([A-Z])", RegexOptions.Compiled)] - private static partial Regex CliCamelCaseRegex(); - [GeneratedRegex("([A-Z])", RegexOptions.Compiled)] - private static partial Regex CliUppercaseRegex(); + [GeneratedRegex("(?<=[a-z])[-_\\.]+([A-Za-z])", RegexOptions.Singleline, 100)] + private static partial Regex DelimitedRegex(); + [GeneratedRegex("(?<=[a-z])([A-Z])", RegexOptions.Singleline, 100)] + private static partial Regex CamelCaseRegex(); + [GeneratedRegex("([A-Z])", RegexOptions.Singleline, 100)] + private static partial Regex UppercaseRegex(); } diff --git a/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs index b1d10598d9..02693baf95 100644 --- a/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs @@ -7,7 +7,7 @@ using Kiota.Builder.OrderComparers; namespace Kiota.Builder.Writers.Java; -public class CodeMethodWriter : BaseElementWriter +public partial class CodeMethodWriter : BaseElementWriter { public CodeMethodWriter(JavaConventionService conventionService) : base(conventionService) { } public override void WriteCodeElement(CodeMethod codeElement, LanguageWriter writer) @@ -145,10 +145,11 @@ private void WriteFactoryMethodBody(CodeMethod codeElement, CodeClass parentClas else writer.WriteLine($"return new {parentClass.Name}();"); } - private static readonly Regex factoryMethodIndexParser = new(@"_(?\d+)", RegexOptions.Compiled, Constants.DefaultRegexTimeout); + [GeneratedRegex(@"_(?\d+)", RegexOptions.Singleline, 100)] + private static partial Regex factoryMethodIndexParser(); private static void WriteFactoryOverloadMethod(CodeMethod codeElement, CodeClass parentClass, LanguageWriter writer) { - if (int.TryParse(factoryMethodIndexParser.Match(codeElement.Name).Groups["idx"].Value, out var currentDiscriminatorPageIndex) && + if (int.TryParse(factoryMethodIndexParser().Match(codeElement.Name).Groups["idx"].Value, out var currentDiscriminatorPageIndex) && codeElement.Parameters.FirstOrDefault() is CodeParameter parameter) { var takeValue = Math.Min(MaxDiscriminatorsPerMethod, parentClass.DiscriminatorInformation.DiscriminatorMappings.Count() - currentDiscriminatorPageIndex * MaxDiscriminatorsPerMethod); diff --git a/src/Kiota.Builder/Writers/Java/JavaConventionService.cs b/src/Kiota.Builder/Writers/Java/JavaConventionService.cs index fb731ef008..8ce6ee9805 100644 --- a/src/Kiota.Builder/Writers/Java/JavaConventionService.cs +++ b/src/Kiota.Builder/Writers/Java/JavaConventionService.cs @@ -9,7 +9,7 @@ using Kiota.Builder.Refiners; namespace Kiota.Builder.Writers.Java; -public class JavaConventionService : CommonLanguageConventionService +public partial class JavaConventionService : CommonLanguageConventionService { internal static string AutoGenerationHeader => "@jakarta.annotation.Generated(\"com.microsoft.kiota\")"; private const string InternalStreamTypeName = "InputStream"; @@ -122,11 +122,12 @@ public void WriteLongDescription(CodeElement element, LanguageWriter writer, IEn writer.WriteLine(DocCommentEnd); } } - private static readonly Regex nonAsciiReplaceRegex = new(@"[^\u0000-\u007F]+", RegexOptions.Compiled, Constants.DefaultRegexTimeout); + [GeneratedRegex(@"[^\u0000-\u007F]+", RegexOptions.None, 100)] + private static partial Regex nonAsciiReplaceRegex(); internal static string RemoveInvalidDescriptionCharacters(string originalDescription) => string.IsNullOrEmpty(originalDescription) ? originalDescription : - nonAsciiReplaceRegex.Replace(originalDescription.Replace("\\", "/", StringComparison.OrdinalIgnoreCase).Replace("*/", string.Empty, StringComparison.OrdinalIgnoreCase), string.Empty); + nonAsciiReplaceRegex().Replace(originalDescription.Replace("\\", "/", StringComparison.OrdinalIgnoreCase).Replace("*/", string.Empty, StringComparison.OrdinalIgnoreCase), string.Empty); #pragma warning disable CA1822 // Method should be static internal void AddRequestBuilderBody(CodeClass parentClass, string returnType, LanguageWriter writer, string? urlTemplateVarName = default, IEnumerable? pathParameters = default) { diff --git a/src/Kiota.Builder/Writers/Php/CodeEnumWriter.cs b/src/Kiota.Builder/Writers/Php/CodeEnumWriter.cs index 66df608cc9..ff583687ab 100644 --- a/src/Kiota.Builder/Writers/Php/CodeEnumWriter.cs +++ b/src/Kiota.Builder/Writers/Php/CodeEnumWriter.cs @@ -6,7 +6,7 @@ using Kiota.Builder.Extensions; namespace Kiota.Builder.Writers.Php; -public class CodeEnumWriter : BaseElementWriter +public partial class CodeEnumWriter : BaseElementWriter { public CodeEnumWriter(PhpConventionService conventionService) : base(conventionService) { } @@ -50,10 +50,11 @@ public override void WriteCodeElement(CodeEnum codeElement, LanguageWriter write writer.WriteLine($"public const {GetEnumValueName(enumProperty.Name)} = '{enumProperty.WireName}';"); } } - private static readonly Regex _enumValueNameRegex = new("([A-Z]{1})", RegexOptions.Compiled, Constants.DefaultRegexTimeout); + [GeneratedRegex(@"([A-Z]{1})", RegexOptions.Singleline, 100)] + private static partial Regex _enumValueNameRegex(); private static string GetEnumValueName(string original) { - return _enumValueNameRegex.Replace(original, "_$1").Trim('_').ToUpperInvariant(); + return _enumValueNameRegex().Replace(original, "_$1").Trim('_').ToUpperInvariant(); } } diff --git a/src/kiota/KiotaHost.cs b/src/kiota/KiotaHost.cs index f627543291..7541d52f7f 100644 --- a/src/kiota/KiotaHost.cs +++ b/src/kiota/KiotaHost.cs @@ -12,7 +12,7 @@ using Microsoft.Extensions.Logging; namespace kiota; -public static class KiotaHost +public static partial class KiotaHost { public static RootCommand GetRootCommand() { @@ -315,8 +315,10 @@ private static Option GetManifestOption(string defaultValue) manifestOption.AddAlias("-a"); return manifestOption; } - private static readonly Regex classNameRegex = new(@"^[a-zA-Z_][\w_-]+", RegexOptions.Compiled, Constants.DefaultRegexTimeout); - private static readonly Regex namespaceNameRegex = new(@"^[\w][\w\._-]+", RegexOptions.Compiled, Constants.DefaultRegexTimeout); + [GeneratedRegex(@"^[a-zA-Z_][\w_-]+", RegexOptions.Singleline, 100)] + private static partial Regex classNameRegex(); + [GeneratedRegex(@"^[\w][\w\._-]+", RegexOptions.Singleline, 100)] + private static partial Regex namespaceNameRegex(); private static Command GetGenerateCommand() { var defaultConfiguration = new GenerationConfiguration(); @@ -333,12 +335,12 @@ private static Command GetGenerateCommand() var classOption = new Option("--class-name", () => defaultConfiguration.ClientClassName, "The class name to use for the core client class."); classOption.AddAlias("-c"); classOption.ArgumentHelpName = "name"; - AddStringRegexValidator(classOption, classNameRegex, "class name"); + AddStringRegexValidator(classOption, classNameRegex(), "class name"); var namespaceOption = new Option("--namespace-name", () => defaultConfiguration.ClientNamespaceName, "The namespace to use for the core client class specified with the --class-name option."); namespaceOption.AddAlias("-n"); namespaceOption.ArgumentHelpName = "name"; - AddStringRegexValidator(namespaceOption, namespaceNameRegex, "namespace name"); + AddStringRegexValidator(namespaceOption, namespaceNameRegex(), "namespace name"); var logLevelOption = GetLogLevelOption(); diff --git a/src/kiota/Rpc/Server.cs b/src/kiota/Rpc/Server.cs index 10a6c51aed..97a54d9ae5 100644 --- a/src/kiota/Rpc/Server.cs +++ b/src/kiota/Rpc/Server.cs @@ -17,7 +17,7 @@ using Microsoft.OpenApi.Services; namespace kiota.Rpc; -internal class Server : IServer +internal partial class Server : IServer { protected KiotaConfiguration Configuration { @@ -133,12 +133,13 @@ private static IEnumerable GetOperationsFromTreeNode(OpenApiUrlTreeNode Enumerable.Empty()) .Union(node.Children.SelectMany(static x => GetOperationsFromTreeNode(x.Value))); } - private static readonly Regex indexingNormalizationRegex = new(@"{\w+}", RegexOptions.Compiled, TimeSpan.FromMilliseconds(100)); + [GeneratedRegex(@"{\w+}", RegexOptions.Singleline, 100)] + private static partial Regex indexingNormalizationRegex(); private static string NormalizeOperationNodePath(OpenApiUrlTreeNode node, OperationType operationType, bool forIndexing = false) { var name = $"{node.Path}#{operationType.ToString().ToUpperInvariant()}"; if (forIndexing) - return indexingNormalizationRegex.Replace(name, "{}"); + return indexingNormalizationRegex().Replace(name, "{}"); return name; } public async Task> GenerateAsync(string openAPIFilePath, string outputPath, GenerationLanguage language, string[] includePatterns, string[] excludePatterns, string clientClassName, string clientNamespaceName, bool usesBackingStore, bool cleanOutput, bool clearCache, bool excludeBackwardCompatible, string[] disabledValidationRules, string[] serializers, string[] deserializers, string[] structuredMimeTypes, bool includeAdditionalData, CancellationToken cancellationToken)