Skip to content

Commit

Permalink
Merge pull request #2743 from jnyrup/type_dictionary
Browse files Browse the repository at this point in the history
Apply the Type-Dictionary Trick
  • Loading branch information
kblok authored Aug 19, 2024
2 parents c4df9a1 + 2723ae2 commit 9aa23f3
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public async Task AllEnumsdAreValid()
await Page.GoToAsync(TestConstants.EmptyPage);
await Context.OverridePermissionsAsync(
TestConstants.EmptyPage,
Enum.GetValues(typeof(OverridePermission)).Cast<OverridePermission>().ToArray());
Enum.GetValues<OverridePermission>());
Assert.That(await GetPermissionAsync(Page, "geolocation"), Is.EqualTo("granted"));
await Context.ClearPermissionOverridesAsync();
Assert.That(await GetPermissionAsync(Page, "geolocation"), Is.EqualTo("prompt"));
Expand Down
7 changes: 4 additions & 3 deletions lib/PuppeteerSharp/BrowserData/Cache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using PuppeteerSharp.Helpers;

namespace PuppeteerSharp.BrowserData
{
Expand All @@ -27,12 +28,12 @@ public IEnumerable<InstalledBrowser> GetInstalledBrowsers()
return Array.Empty<InstalledBrowser>();
}

var browserNames = Enum.GetNames(typeof(SupportedBrowser)).Select(browser => browser.ToUpperInvariant());
var browserNames = EnumHelper.GetNames<SupportedBrowser>().Select(browser => browser.ToUpperInvariant());
var browsers = rootInfo.GetDirectories().Where(browser => browserNames.Contains(browser.Name.ToUpperInvariant()));

return browsers.SelectMany(browser =>
{
var browserEnum = (SupportedBrowser)Enum.Parse(typeof(SupportedBrowser), browser.Name, ignoreCase: true);
var browserEnum = EnumHelper.Parse<SupportedBrowser>(browser.Name, ignoreCase: true);
var dirInfo = new DirectoryInfo(GetBrowserRoot(browserEnum));
var dirs = dirInfo.GetDirectories();

Expand All @@ -45,7 +46,7 @@ public IEnumerable<InstalledBrowser> GetInstalledBrowsers()
return null;
}

var platformEnum = (Platform)Enum.Parse(typeof(Platform), result.Value.Platform, ignoreCase: true);
var platformEnum = EnumHelper.Parse<Platform>(result.Value.Platform, ignoreCase: true);
return new InstalledBrowser(this, browserEnum, result.Value.BuildId, platformEnum);
})
.Where(item => item != null);
Expand Down
2 changes: 1 addition & 1 deletion lib/PuppeteerSharp/BrowserData/Firefox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ internal static void CreateProfile(string tempUserDataDirectory, Dictionary<stri
private static (FirefoxChannel Channel, string BuildId) ParseBuildId(string buildId)
{
// Iterate through all the FirefoxChannel enum values as string
foreach (var value in Enum.GetValues(typeof(FirefoxChannel)).Cast<FirefoxChannel>().Select(v => v.ToValueString()))
foreach (var value in EnumHelper.GetValues<FirefoxChannel>().Select(v => v.ToValueString()))
{
if (buildId.StartsWith(value, StringComparison.OrdinalIgnoreCase))
{
Expand Down
120 changes: 65 additions & 55 deletions lib/PuppeteerSharp/Helpers/EnumHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
// * SOFTWARE.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
Expand All @@ -31,86 +30,97 @@ namespace PuppeteerSharp.Helpers;

internal static class EnumHelper
{
private static readonly ConcurrentDictionary<Type, IReadOnlyDictionary<string, Enum>> _stringToEnumCache = new();
public static string[] GetNames<TEnum>()
where TEnum : struct, Enum =>
#if NET8_0_OR_GREATER
Enum.GetNames<TEnum>();
#else
Enum.GetNames(typeof(TEnum));
#endif

private static readonly ConcurrentDictionary<Type, IReadOnlyDictionary<Enum, string>> _enumToStringCache = new();
public static TEnum[] GetValues<TEnum>()
where TEnum : struct, Enum =>
#if NET8_0_OR_GREATER
Enum.GetValues<TEnum>();
#else
(TEnum[])Enum.GetValues(typeof(TEnum));
#endif

public static TEnum ToEnum<TEnum>(this string value)
where TEnum : Enum
{
var enumValues = _stringToEnumCache.GetOrAdd(typeof(TEnum), type =>
{
var names = Enum.GetNames(type);
var values = (TEnum[])Enum.GetValues(type);
var dictionary = new Dictionary<string, Enum>(StringComparer.OrdinalIgnoreCase);
for (var i = 0; i < names.Length; i++)
{
dictionary.Add(names[i], values[i]);
var memberValue = type.GetField(names[i]).GetCustomAttribute<EnumMemberAttribute>()?.Value;
if (memberValue != null)
{
dictionary[memberValue] = values[i];
}
}
public static TEnum Parse<TEnum>(string value, bool ignoreCase)
where TEnum : struct, Enum =>
#if NET8_0_OR_GREATER
Enum.Parse<TEnum>(value, ignoreCase);
#else
(TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase);
#endif

return dictionary;
});
return (TEnum)enumValues[value];
}
public static TEnum ToEnum<TEnum>(this string value)
where TEnum : struct, Enum
=> StringToEnum<TEnum>.Cache[value];

public static string ToValueString<TEnum>(this TEnum value)
where TEnum : Enum
where TEnum : struct, Enum
=> EnumToString<TEnum>.Cache[value];

public static TEnum FromValueString<TEnum>(string value)
where TEnum : struct, Enum
{
var enumValues = _enumToStringCache.GetOrAdd(typeof(TEnum), type =>
if (StringToEnum<TEnum>.Cache.TryGetValue(value, out var enumValue))
{
var names = Enum.GetNames(type);
var dictionary = new Dictionary<Enum, string>();
foreach (var t in names)
{
var field = type.GetField(t);
var valueName = field.GetCustomAttribute<EnumMemberAttribute>()?.Value ?? t;
var fieldValue = (TEnum)field.GetValue(null);
dictionary[fieldValue] = valueName;
}
return enumValue;
}

return dictionary;
});
var defaultEnumAttribute = typeof(TEnum).GetCustomAttribute<DefaultEnumValueAttribute>();
if (defaultEnumAttribute != null)
{
return (TEnum)(object)defaultEnumAttribute.Value;
}

return enumValues[value];
throw new ArgumentException($"Unknown value '{value}' for enum {typeof(TEnum).Name}");
}

public static TEnum FromValueString<TEnum>(string value)
private static class StringToEnum<TEnum>
where TEnum : struct, Enum
{
var enumValues = _stringToEnumCache.GetOrAdd(typeof(TEnum), type =>
public static readonly IReadOnlyDictionary<string, TEnum> Cache = Compute();

private static Dictionary<string, TEnum> Compute()
{
var names = Enum.GetNames(type);
var dictionary = new Dictionary<string, Enum>(StringComparer.OrdinalIgnoreCase);
foreach (var valueName in names)
var names = GetNames<TEnum>();
var dictionary = new Dictionary<string, TEnum>(StringComparer.OrdinalIgnoreCase);
foreach (var name in names)
{
var field = type.GetField(valueName);
var field = typeof(TEnum).GetField(name);
var fieldValue = (TEnum)field.GetValue(null);
dictionary[valueName] = fieldValue;
dictionary[name] = fieldValue;
if (field.GetCustomAttribute<EnumMemberAttribute>()?.Value is { } enumMember)
{
dictionary[enumMember] = fieldValue;
}
}

return dictionary;
});

if (enumValues.TryGetValue(value, out var enumValue))
{
return (TEnum)enumValue;
}
}

var defaultEnumAttribute = typeof(TEnum).GetCustomAttribute<DefaultEnumValueAttribute>();
if (defaultEnumAttribute != null)
private static class EnumToString<TEnum>
where TEnum : struct, Enum
{
public static readonly IReadOnlyDictionary<TEnum, string> Cache = Compute();

private static Dictionary<TEnum, string> Compute()
{
return (TEnum)(object)defaultEnumAttribute.Value;
}
var names = GetNames<TEnum>();
var dictionary = new Dictionary<TEnum, string>();
foreach (var name in names)
{
var field = typeof(TEnum).GetField(name);
var valueName = field.GetCustomAttribute<EnumMemberAttribute>()?.Value ?? name;
var fieldValue = (TEnum)field.GetValue(null);
dictionary[fieldValue] = valueName;
}

throw new ArgumentException($"Unknown value '{value}' for enum {typeof(TEnum).Name}");
return dictionary;
}
}
}

0 comments on commit 9aa23f3

Please sign in to comment.