Files
UnrealEngine/Engine/Source/Programs/Shared/EpicGames.Horde/StringIdConverter.cs
2025-05-18 13:04:45 +08:00

163 lines
5.3 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using EpicGames.Core;
using EpicGames.Serialization;
namespace EpicGames.Horde
{
/// <summary>
/// Base class for converting to and from types containing a <see cref="StringId"/>. Useful pattern for reducing boilerplate with strongly typed records.
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class StringIdConverter<T> where T : struct
{
/// <summary>
/// Converts a type to a <see cref="StringId"/>
/// </summary>
public abstract StringId ToStringId(T value);
/// <summary>
/// Constructs a type from a <see cref="StringId"/>
/// </summary>
public abstract T FromStringId(StringId id);
}
/// <summary>
/// Attribute declaring a <see cref="StringIdConverter{T}"/> for a particular type
/// </summary>
[AttributeUsage(AttributeTargets.Struct)]
public sealed class StringIdConverterAttribute : Attribute
{
/// <summary>
/// The converter type
/// </summary>
public Type ConverterType { get; }
/// <summary>
/// Constructor
/// </summary>
public StringIdConverterAttribute(Type converterType) => ConverterType = converterType;
}
/// <summary>
/// Converter to compact binary objects
/// </summary>
public sealed class StringIdCbConverter<TValue, TConverter> : CbConverter<TValue> where TValue : struct where TConverter : StringIdConverter<TValue>, new()
{
readonly TConverter _converter = new TConverter();
/// <inheritdoc/>
public override TValue Read(CbField field)
{
return _converter.FromStringId(new StringId(new Utf8String(field.AsString())));
}
/// <inheritdoc/>
public override void Write(CbWriter writer, TValue value)
{
writer.WriteStringValue(_converter.ToStringId(value).ToString());
}
/// <inheritdoc/>
public override void WriteNamed(CbWriter writer, CbFieldName name, TValue value)
{
writer.WriteString(name, _converter.ToStringId(value).ToString());
}
}
/// <summary>
/// Class which serializes types with a <see cref="StringIdConverter{T}"/> to Json
/// </summary>
public sealed class StringIdTypeConverter<TValue, TConverter> : TypeConverter where TValue : struct where TConverter : StringIdConverter<TValue>, new()
{
readonly TConverter _converter = new TConverter();
/// <inheritdoc/>
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
{
return sourceType == typeof(string) || sourceType == typeof(StringId);
}
/// <inheritdoc/>
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
{
if (value is string str)
{
return _converter.FromStringId(new StringId(new Utf8String(str)));
}
if (value is StringId stringId)
{
return _converter.FromStringId(stringId);
}
return null;
}
/// <inheritdoc/>
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) => destinationType == typeof(string) || destinationType == typeof(StringId);
/// <inheritdoc/>
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
{
if (destinationType == typeof(string))
{
return _converter.ToStringId((TValue)value!).ToString();
}
if (destinationType == typeof(StringId))
{
return _converter.ToStringId((TValue)value!);
}
return null;
}
}
/// <summary>
/// Class which serializes types with a <see cref="StringIdConverter{T}"/> to Json
/// </summary>
public sealed class StringIdJsonConverter<TValue, TConverter> : JsonConverter<TValue> where TValue : struct where TConverter : StringIdConverter<TValue>, new()
{
readonly TConverter _converter = new TConverter();
/// <inheritdoc/>
public override TValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> _converter.FromStringId(new StringId(new Utf8String(reader.GetUtf8String().ToArray())));
/// <inheritdoc/>
public override void Write(Utf8JsonWriter writer, TValue value, JsonSerializerOptions options)
=> writer.WriteStringValue(_converter.ToStringId(value).Span);
/// <inheritdoc/>
public override TValue ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> Read(ref reader, typeToConvert, options);
/// <inheritdoc/>
public override void WriteAsPropertyName(Utf8JsonWriter writer, TValue value, JsonSerializerOptions options)
=> writer.WritePropertyName(_converter.ToStringId(value).Span);
}
/// <summary>
/// Creates constructors for types with a <see cref="StringIdConverter{T}"/> to Json
/// </summary>
public sealed class StringIdJsonConverterFactory : JsonConverterFactory
{
/// <inheritdoc/>
public override bool CanConvert(Type typeToConvert) => typeToConvert.GetCustomAttribute<StringIdConverterAttribute>() != null;
/// <inheritdoc/>
public override JsonConverter? CreateConverter(Type type, JsonSerializerOptions options)
{
StringIdConverterAttribute? attribute = type.GetCustomAttribute<StringIdConverterAttribute>();
if (attribute == null)
{
return null;
}
return (JsonConverter?)Activator.CreateInstance(typeof(StringIdJsonConverter<,>).MakeGenericType(type, attribute.ConverterType));
}
}
}