using System.Collections.Concurrent; using System.Reflection; using System.Text.RegularExpressions; namespace EnvelopeGenerator.Infrastructure.Notification; public static class ReplacePlaceholderExtensions { private sealed record PlaceholderProperty(string Placeholder, PropertyInfo Property); private static readonly ConcurrentDictionary PropertyCache = new(); public static string ReplacePlaceholders(this string template, object data) { var properties = PropertyCache.GetOrAdd(data.GetType(), CreatePlaceholderProperties); foreach (var item in properties) { var value = item.Property.GetValue(data)?.ToString() ?? string.Empty; template = template.Replace( item.Placeholder, value, StringComparison.Ordinal); } return template; } private static PlaceholderProperty[] CreatePlaceholderProperties( Type type) { return [.. type .GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.CanRead && p.GetIndexParameters().Length == 0) .Select(p => new PlaceholderProperty( $"[{ToCapitalSnakeCase(p.Name)}]", p))]; } private static string ToCapitalSnakeCase(string name) { return Regex.Replace( Regex.Replace(name, @"([A-Z]+)([A-Z][a-z])", "$1_$2"), @"([a-z0-9])([A-Z])", "$1_$2").ToUpperInvariant(); } }