|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text.Json;
using Google.Protobuf.Reflection;
using Type = System.Type;
namespace Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Json;
internal sealed class EnumConverter<TEnum> : SettingsConverterBase<TEnum> where TEnum : Enum
{
public EnumConverter(JsonContext context) : base(context)
{
}
public override TEnum? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
switch (reader.TokenType)
{
case JsonTokenType.String:
var enumDescriptor = (EnumDescriptor?)Context.DescriptorRegistry.FindDescriptorByType(typeToConvert)
?? throw new InvalidOperationException($"Unable to resolve descriptor for {typeToConvert}.");
var value = reader.GetString()!;
var valueDescriptor = JsonNamingHelpers.GetEnumFieldReadValue(enumDescriptor, value, Context.Settings)
?? throw new InvalidOperationException(@$"Error converting value ""{value}"" to enum type {typeToConvert}.");
return ConvertFromInteger(valueDescriptor.Number);
case JsonTokenType.Number:
return ConvertFromInteger(reader.GetInt32());
case JsonTokenType.Null:
return default;
default:
throw new InvalidOperationException($"Unexpected JSON token: {reader.TokenType}.");
}
}
public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
{
if (Context.Settings.WriteEnumsAsIntegers)
{
writer.WriteNumberValue(ConvertToInteger(value));
}
else
{
var enumDescriptor = (EnumDescriptor?)Context.DescriptorRegistry.FindDescriptorByType(value.GetType())
?? throw new InvalidOperationException($"Unable to resolve descriptor for {value.GetType()}.");
var name = JsonNamingHelpers.GetEnumFieldWriteName(enumDescriptor, value, Context.Settings);
if (name != null)
{
writer.WriteStringValue(name);
}
else
{
writer.WriteNumberValue(ConvertToInteger(value));
}
}
}
private static TEnum ConvertFromInteger(int integer)
{
if (!TryConvertToEnum(integer, out var value))
{
throw new InvalidOperationException($"Integer can't be converted to enum {typeof(TEnum).FullName}.");
}
return value;
}
private static int ConvertToInteger(TEnum value)
{
if (!TryConvertToInteger(value, out var integer))
{
throw new InvalidOperationException($"Enum {typeof(TEnum).FullName} can't be converted to integer.");
}
return integer;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryConvertToInteger(TEnum value, out int integer)
{
if (Unsafe.SizeOf<int>() == Unsafe.SizeOf<TEnum>())
{
integer = Unsafe.As<TEnum, int>(ref value);
return true;
}
integer = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryConvertToEnum(int integer, [NotNullWhen(true)] out TEnum? value)
{
if (Unsafe.SizeOf<int>() == Unsafe.SizeOf<TEnum>())
{
value = Unsafe.As<int, TEnum>(ref integer);
return true;
}
value = default;
return false;
}
}
|