// 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; namespace EpicGames.Horde.Jobs { /// /// Base class for converting to and from types containing a . Useful pattern for reducing boilerplate with strongly typed records. /// /// public abstract class SubResourceIdConverter where T : struct { /// /// Converts a type to a /// public abstract SubResourceId ToSubResourceId(T value); /// /// Constructs a type from a /// public abstract T FromSubResourceId(SubResourceId id); } /// /// Attribute declaring a for a particular type /// [AttributeUsage(AttributeTargets.Struct)] public sealed class SubResourceIdConverterAttribute : Attribute { /// /// The converter type /// public Type ConverterType { get; } /// /// Constructor /// public SubResourceIdConverterAttribute(Type converterType) => ConverterType = converterType; } /// /// Class which serializes types with a to Json /// public sealed class SubResourceIdTypeConverter : TypeConverter where TValue : struct where TConverter : SubResourceIdConverter, new() { readonly TConverter _converter = new TConverter(); /// public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string) || sourceType == typeof(SubResourceId); } /// public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string str) { return _converter.FromSubResourceId(SubResourceId.Parse(str)); } if (value is SubResourceId stringId) { return _converter.FromSubResourceId(stringId); } return null; } /// public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) => destinationType == typeof(string) || destinationType == typeof(SubResourceId); /// public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType == typeof(string)) { return _converter.ToSubResourceId((TValue)value!).ToString(); } if (destinationType == typeof(SubResourceId)) { return _converter.ToSubResourceId((TValue)value!); } return null; } } /// /// Class which serializes types with a to Json /// public sealed class SubResourceIdJsonConverter : JsonConverter where TValue : struct where TConverter : SubResourceIdConverter, new() { readonly TConverter _converter = new TConverter(); /// public override TValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => _converter.FromSubResourceId(SubResourceId.Parse(reader.GetString() ?? String.Empty)); /// public override void Write(Utf8JsonWriter writer, TValue value, JsonSerializerOptions options) => writer.WriteStringValue(_converter.ToSubResourceId(value).ToString()); } /// /// Creates constructors for types with a to Json /// public sealed class SubResourceIdJsonConverterFactory : JsonConverterFactory { /// public override bool CanConvert(Type typeToConvert) => typeToConvert.GetCustomAttribute() != null; /// public override JsonConverter? CreateConverter(Type type, JsonSerializerOptions options) { SubResourceIdConverterAttribute? attribute = type.GetCustomAttribute(); if (attribute == null) { return null; } return (JsonConverter?)Activator.CreateInstance(typeof(SubResourceIdJsonConverter<,>).MakeGenericType(type, attribute.ConverterType)); } } }