// 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 BinaryIdConverter where T : struct { /// /// Converts a type to a /// public abstract BinaryId ToBinaryId(T value); /// /// Constructs a type from a /// public abstract T FromBinaryId(BinaryId id); } /// /// Attribute declaring a for a particular type /// [AttributeUsage(AttributeTargets.Struct)] public sealed class BinaryIdConverterAttribute : Attribute { /// /// The converter type /// public Type ConverterType { get; } /// /// Constructor /// public BinaryIdConverterAttribute(Type converterType) => ConverterType = converterType; } /// /// Converter to compact binary objects /// public sealed class BinaryIdCbConverter : CbConverter where TValue : struct where TConverter : BinaryIdConverter, new() { readonly TConverter _converter = new TConverter(); /// public override TValue Read(CbField field) { return _converter.FromBinaryId(BinaryId.Parse(field.AsUtf8String())); } /// public override void Write(CbWriter writer, TValue value) { writer.WriteStringValue(_converter.ToBinaryId(value).ToString()); } /// public override void WriteNamed(CbWriter writer, CbFieldName name, TValue value) { writer.WriteString(name, _converter.ToBinaryId(value).ToString()); } } /// /// Class which serializes types with a to Json /// public sealed class BinaryIdTypeConverter : TypeConverter where TValue : struct where TConverter : BinaryIdConverter, new() { readonly TConverter _converter = new TConverter(); /// public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || sourceType == typeof(BinaryId); } /// public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string str) { return _converter.FromBinaryId(BinaryId.Parse(str)); } if (value is BinaryId stringId) { return _converter.FromBinaryId(stringId); } return null; } /// public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) => destinationType == typeof(string) || destinationType == typeof(BinaryId); /// public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(string)) { return _converter.ToBinaryId((TValue)value!).ToString(); } if (destinationType == typeof(BinaryId)) { return _converter.ToBinaryId((TValue)value!); } return null; } } /// /// Class which serializes types with a to Json /// public sealed class BinaryIdJsonConverter : JsonConverter where TValue : struct where TConverter : BinaryIdConverter, new() { readonly TConverter _converter = new TConverter(); /// public override TValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => _converter.FromBinaryId(BinaryId.Parse(reader.GetUtf8String())); /// public override void Write(Utf8JsonWriter writer, TValue value, JsonSerializerOptions options) { BinaryId binaryId = _converter.ToBinaryId(value); Span span = stackalloc byte[12 * 2]; binaryId.ToUtf8String(span); writer.WriteStringValue(span); } } /// /// Creates constructors for types with a to Json /// public sealed class BinaryIdJsonConverterFactory : JsonConverterFactory { /// public override bool CanConvert(Type typeToConvert) => typeToConvert.GetCustomAttribute() != null; /// public override JsonConverter? CreateConverter(Type type, JsonSerializerOptions options) { BinaryIdConverterAttribute? attribute = type.GetCustomAttribute(); if (attribute == null) { return null; } return (JsonConverter?)Activator.CreateInstance(typeof(BinaryIdJsonConverter<,>).MakeGenericType(type, attribute.ConverterType)); } } }