Multi-Platform Package Manager for Stable Diffusion
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

132 lines
4.3 KiB

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace StabilityMatrix.Core.Converters.Json;
public class DefaultUnknownEnumConverter<
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T
> : JsonConverter<T>
where T : Enum
{
/// <summary>
/// Lazy initialization for <see cref="EnumMemberValues"/>.
/// </summary>
private readonly Lazy<Dictionary<string, T>> _enumMemberValuesLazy =
new(
() =>
typeof(T)
.GetFields()
.Where(field => field.IsStatic)
.Select(
field =>
new
{
FieldName = field.Name,
FieldValue = (T)field.GetValue(null)!,
EnumMemberValue = field
.GetCustomAttributes<EnumMemberAttribute>(false)
.FirstOrDefault()
?.Value?.ToString()
}
)
.ToDictionary(x => x.EnumMemberValue ?? x.FieldName, x => x.FieldValue)
);
/// <summary>
/// Gets a dictionary of enum member values, keyed by the EnumMember attribute value, or the field name if no EnumMember attribute is present.
/// </summary>
private Dictionary<string, T> EnumMemberValues => _enumMemberValuesLazy.Value;
/// <summary>
/// Lazy initialization for <see cref="EnumMemberNames"/>.
/// </summary>
private readonly Lazy<Dictionary<T, string>> _enumMemberNamesLazy;
/// <summary>
/// Gets a dictionary of enum member names, keyed by the enum member value.
/// </summary>
private Dictionary<T, string> EnumMemberNames => _enumMemberNamesLazy.Value;
/// <summary>
/// Gets the value of the "Unknown" enum member, or the 0 value if no "Unknown" member is present.
/// </summary>
private T UnknownValue =>
EnumMemberValues.TryGetValue("Unknown", out var res) ? res : (T)Enum.ToObject(typeof(T), 0);
/// <inheritdoc />
public override bool HandleNull => true;
public DefaultUnknownEnumConverter()
{
_enumMemberNamesLazy = new Lazy<Dictionary<T, string>>(
() => EnumMemberValues.ToDictionary(x => x.Value, x => x.Key)
);
}
/// <inheritdoc />
public override T Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options
)
{
if (reader.TokenType is not (JsonTokenType.String or JsonTokenType.PropertyName))
{
throw new JsonException("Expected String or PropertyName token");
}
if (reader.GetString() is { } readerString)
{
// First try get exact match
if (EnumMemberValues.TryGetValue(readerString, out var enumMemberValue))
{
return enumMemberValue;
}
// Otherwise try get case-insensitive match
if (
EnumMemberValues.Keys.FirstOrDefault(
key => key.Equals(readerString, StringComparison.OrdinalIgnoreCase)
) is
{ } enumMemberName
)
{
return EnumMemberValues[enumMemberName];
}
Debug.WriteLine($"Unknown enum member value for {typeToConvert}: {readerString}");
}
return UnknownValue;
}
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
{
if (value == null)
{
writer.WriteNullValue();
return;
}
writer.WriteStringValue(EnumMemberNames[value]);
}
/// <inheritdoc />
public override T ReadAsPropertyName(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options
) => Read(ref reader, typeToConvert, options);
/// <inheritdoc />
public override void WriteAsPropertyName(
Utf8JsonWriter writer,
T? value,
JsonSerializerOptions options
) => Write(writer, value, options);
}