// 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 { /// /// Base class for converting to and from types containing a . Useful pattern for reducing boilerplate with strongly typed records. /// /// public abstract class StringIdConverter where T : struct { /// /// Converts a type to a /// public abstract StringId ToStringId(T value); /// /// Constructs a type from a /// public abstract T FromStringId(StringId id); } /// /// Attribute declaring a for a particular type /// [AttributeUsage(AttributeTargets.Struct)] public sealed class StringIdConverterAttribute : Attribute { /// /// The converter type /// public Type ConverterType { get; } /// /// Constructor /// public StringIdConverterAttribute(Type converterType) => ConverterType = converterType; } /// /// Converter to compact binary objects /// public sealed class StringIdCbConverter : CbConverter where TValue : struct where TConverter : StringIdConverter, new() { readonly TConverter _converter = new TConverter(); /// public override TValue Read(CbField field) { return _converter.FromStringId(new StringId(new Utf8String(field.AsString()))); } /// public override void Write(CbWriter writer, TValue value) { writer.WriteStringValue(_converter.ToStringId(value).ToString()); } /// public override void WriteNamed(CbWriter writer, CbFieldName name, TValue value) { writer.WriteString(name, _converter.ToStringId(value).ToString()); } } /// /// Class which serializes types with a to Json /// public sealed class StringIdTypeConverter : TypeConverter where TValue : struct where TConverter : StringIdConverter, new() { readonly TConverter _converter = new TConverter(); /// public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || sourceType == typeof(StringId); } /// 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; } /// public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) => destinationType == typeof(string) || destinationType == typeof(StringId); /// 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; } } /// /// Class which serializes types with a to Json /// public sealed class StringIdJsonConverter : JsonConverter where TValue : struct where TConverter : StringIdConverter, new() { readonly TConverter _converter = new TConverter(); /// public override TValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => _converter.FromStringId(new StringId(new Utf8String(reader.GetUtf8String().ToArray()))); /// public override void Write(Utf8JsonWriter writer, TValue value, JsonSerializerOptions options) => writer.WriteStringValue(_converter.ToStringId(value).Span); /// public override TValue ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => Read(ref reader, typeToConvert, options); /// public override void WriteAsPropertyName(Utf8JsonWriter writer, TValue value, JsonSerializerOptions options) => writer.WritePropertyName(_converter.ToStringId(value).Span); } /// /// Creates constructors for types with a to Json /// public sealed class StringIdJsonConverterFactory : JsonConverterFactory { /// public override bool CanConvert(Type typeToConvert) => typeToConvert.GetCustomAttribute() != null; /// public override JsonConverter? CreateConverter(Type type, JsonSerializerOptions options) { StringIdConverterAttribute? attribute = type.GetCustomAttribute(); if (attribute == null) { return null; } return (JsonConverter?)Activator.CreateInstance(typeof(StringIdJsonConverter<,>).MakeGenericType(type, attribute.ConverterType)); } } }