From dcd8b9777908f841d05a27e263fc81df7881885e Mon Sep 17 00:00:00 2001 From: Maxwell Weru Date: Thu, 6 Jun 2024 13:09:52 +0300 Subject: [PATCH] Use RegEx source generators on .NET 7 or later --- Directory.Build.props | 1 + .../EventBusNamingOptions.cs | 43 ++++++++++++++++--- .../Serialization/AbstractEventSerializer.cs | 13 +++++- .../Transports/EventBusTransport.cs | 15 +++++-- .../EventBusTransportConfigureOptions.cs | 13 +++++- 5 files changed, 71 insertions(+), 14 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 89400186..7ff32f94 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,6 +9,7 @@ + $(WarningsAsErrors),SYSLIB1045 $(WarningsAsErrors),IL2026,IL2060,IL2091,IL2095,IL3050 diff --git a/src/Tingle.EventBus/DependencyInjection/EventBusNamingOptions.cs b/src/Tingle.EventBus/DependencyInjection/EventBusNamingOptions.cs index 37b4365f..1ee9f3a3 100644 --- a/src/Tingle.EventBus/DependencyInjection/EventBusNamingOptions.cs +++ b/src/Tingle.EventBus/DependencyInjection/EventBusNamingOptions.cs @@ -7,14 +7,21 @@ namespace Microsoft.Extensions.DependencyInjection; /// /// Specifies options for naming behaviour and requirements. /// -public class EventBusNamingOptions +public partial class EventBusNamingOptions { - private static readonly Regex trimPattern = new("(Event|Consumer|EventConsumer)$", RegexOptions.Compiled); - private static readonly Regex namePattern = new("(?<=[a-z0-9])[A-Z]", RegexOptions.Compiled); - private static readonly Regex replacePatternKebabCase = new("[^a-zA-Z0-9-]", RegexOptions.Compiled); - private static readonly Regex replacePatternSnakeCase = new("[^a-zA-Z0-9_]", RegexOptions.Compiled); - private static readonly Regex replacePatternDotCase = new("[^a-zA-Z0-9\\.]", RegexOptions.Compiled); - private static readonly Regex replacePatternDefault = new("[^a-zA-Z0-9-_\\.]", RegexOptions.Compiled); + private const string TrimPattern = "(Event|Consumer|EventConsumer)$"; + private const string NamePattern = "(?<=[a-z0-9])[A-Z]"; + private const string ReplacePatternKebabCase = "[^a-zA-Z0-9-]"; + private const string ReplacePatternSnakeCase = "[^a-zA-Z0-9_]"; + private const string ReplacePatternDotCase = "[^a-zA-Z0-9\\.]"; + private const string ReplacePatternDefault = "[^a-zA-Z0-9-_\\.]"; + + private static readonly Regex trimPattern = GetTrimPattern(); + private static readonly Regex namePattern = GetNamePattern(); + private static readonly Regex replacePatternKebabCase = GetReplacePatternKebabCase(); + private static readonly Regex replacePatternSnakeCase = GetReplacePatternSnakeCase(); + private static readonly Regex replacePatternDotCase = GetReplacePatternDotCase(); + private static readonly Regex replacePatternDefault = GetReplacePatternDefault(); /// /// The scope to use for queues and subscriptions. @@ -136,4 +143,26 @@ public string Join(params string[] values) _ => throw new InvalidOperationException($"'{nameof(NamingConvention)}.{Convention}' does not support joining"), }).ToLowerInvariant(); } + +#if NET7_0_OR_GREATER + [GeneratedRegex(TrimPattern, RegexOptions.Compiled)] + private static partial Regex GetTrimPattern(); + [GeneratedRegex(NamePattern, RegexOptions.Compiled)] + private static partial Regex GetNamePattern(); + [GeneratedRegex(ReplacePatternKebabCase, RegexOptions.Compiled)] + private static partial Regex GetReplacePatternKebabCase(); + [GeneratedRegex(ReplacePatternSnakeCase, RegexOptions.Compiled)] + private static partial Regex GetReplacePatternSnakeCase(); + [GeneratedRegex(ReplacePatternDotCase, RegexOptions.Compiled)] + private static partial Regex GetReplacePatternDotCase(); + [GeneratedRegex(ReplacePatternDefault, RegexOptions.Compiled)] + private static partial Regex GetReplacePatternDefault(); +#else + private static Regex GetTrimPattern() => new(TrimPattern, RegexOptions.Compiled); + private static Regex GetNamePattern() => new(NamePattern, RegexOptions.Compiled); + private static Regex GetReplacePatternKebabCase() => new(ReplacePatternKebabCase, RegexOptions.Compiled); + private static Regex GetReplacePatternSnakeCase() => new(ReplacePatternSnakeCase, RegexOptions.Compiled); + private static Regex GetReplacePatternDotCase() => new(ReplacePatternDotCase, RegexOptions.Compiled); + private static Regex GetReplacePatternDefault() => new(ReplacePatternDefault, RegexOptions.Compiled); +#endif } diff --git a/src/Tingle.EventBus/Serialization/AbstractEventSerializer.cs b/src/Tingle.EventBus/Serialization/AbstractEventSerializer.cs index bd5fd589..bf98860d 100644 --- a/src/Tingle.EventBus/Serialization/AbstractEventSerializer.cs +++ b/src/Tingle.EventBus/Serialization/AbstractEventSerializer.cs @@ -12,12 +12,14 @@ namespace Tingle.EventBus.Serialization; /// /// Abstract implementation for an event serializer. /// -public abstract class AbstractEventSerializer : IEventSerializer +public abstract partial class AbstractEventSerializer : IEventSerializer { + private const string TrimPattern = "(Serializer|EventSerializer)$"; + /// protected static readonly IList JsonContentTypes = new[] { "application/json", "text/json", }; - private static readonly Regex trimPattern = new("(Serializer|EventSerializer)$", RegexOptions.Compiled); + private static readonly Regex trimPattern = GetTrimPattern(); /// /// @@ -147,4 +149,11 @@ protected abstract Task SerializeEnvelopeAsync(Stream stream, EventEnvelope envelope, CancellationToken cancellationToken = default) where T : class; + +#if NET7_0_OR_GREATER + [GeneratedRegex(TrimPattern, RegexOptions.Compiled)] + private static partial Regex GetTrimPattern(); +#else + private static Regex GetTrimPattern() => new(TrimPattern, RegexOptions.Compiled); +#endif } diff --git a/src/Tingle.EventBus/Transports/EventBusTransport.cs b/src/Tingle.EventBus/Transports/EventBusTransport.cs index 035c9007..d9ac22f5 100644 --- a/src/Tingle.EventBus/Transports/EventBusTransport.cs +++ b/src/Tingle.EventBus/Transports/EventBusTransport.cs @@ -15,9 +15,11 @@ namespace Tingle.EventBus.Transports; /// Abstract implementation for an event bus transport. /// /// The type used for configuring options of the transport -public abstract class EventBusTransport : IEventBusTransport where TOptions : EventBusTransportOptions, new() +public abstract partial class EventBusTransport : IEventBusTransport where TOptions : EventBusTransportOptions, new() { - private static readonly Regex CategoryNamePattern = new(@"Transport$", RegexOptions.Compiled); + private const string CategoryNamePattern = "Transport$"; + + private static readonly Regex categoryNamePattern = GetCategoryNamePattern(); private readonly IServiceScopeFactory scopeFactory; private readonly IOptionsMonitor optionsMonitor; @@ -41,7 +43,7 @@ public EventBusTransport(IServiceScopeFactory scopeFactory, // Create a well-scoped logger var categoryName = $"{LogCategoryNames.Transports}.{GetType().Name}"; - categoryName = CategoryNamePattern.Replace(categoryName, string.Empty); // remove trailing "Transport" + categoryName = categoryNamePattern.Replace(categoryName, string.Empty); // remove trailing "Transport" Logger = loggerFactory?.CreateLogger(categoryName) ?? throw new ArgumentNullException(nameof(loggerFactory)); } @@ -382,5 +384,12 @@ protected async Task ConsumeAsync(IServiceScope scope, return Logger.BeginScope(state); } +#if NET7_0_OR_GREATER + [GeneratedRegex(CategoryNamePattern, RegexOptions.Compiled)] + private static partial Regex GetCategoryNamePattern(); +#else + private static Regex GetCategoryNamePattern() => new(CategoryNamePattern, RegexOptions.Compiled); +#endif + #endregion } diff --git a/src/Tingle.EventBus/Transports/EventBusTransportConfigureOptions.cs b/src/Tingle.EventBus/Transports/EventBusTransportConfigureOptions.cs index 2a66d416..80738995 100644 --- a/src/Tingle.EventBus/Transports/EventBusTransportConfigureOptions.cs +++ b/src/Tingle.EventBus/Transports/EventBusTransportConfigureOptions.cs @@ -14,16 +14,18 @@ namespace Microsoft.Extensions.DependencyInjection; /// An instance. /// A list of to use when configuring options. /// An for bus configuration. -public abstract class EventBusTransportConfigureOptions(IEventBusConfigurationProvider configurationProvider, +public abstract partial class EventBusTransportConfigureOptions(IEventBusConfigurationProvider configurationProvider, IEnumerable configurators, IOptions busOptionsAccessor) : IConfigureNamedOptions, IPostConfigureOptions, IValidateOptions where TOptions : EventBusTransportOptions { + private const string ReplacePatternSafeEnv = "[^a-zA-Z0-9_]"; + // Some hosts do not allow certain characters for ENV vars but we know they all support alphanumeric and underscore // For example, Azure Container Instances does not allow hyphens in ENV vars while Azure Container Apps does - private static readonly Regex replacePatternSafeEnv = new("[^a-zA-Z0-9_]", RegexOptions.Compiled); + private static readonly Regex replacePatternSafeEnv = GetReplacePatternSafeEnv(); private readonly IEventBusConfigurationProvider configurationProvider = configurationProvider ?? throw new ArgumentNullException(nameof(configurationProvider)); private readonly IEnumerable configurators = configurators ?? throw new ArgumentNullException(nameof(configurators)); @@ -98,4 +100,11 @@ public virtual ValidateOptionsResult Validate(string? name, TOptions options) return ValidateOptionsResult.Success; } + +#if NET7_0_OR_GREATER + [GeneratedRegex(ReplacePatternSafeEnv, RegexOptions.Compiled)] + private static partial Regex GetReplacePatternSafeEnv(); +#else + private static Regex GetReplacePatternSafeEnv() => new(ReplacePatternSafeEnv, RegexOptions.Compiled); +#endif }