#nullable enable using IPA.Config.Data; using System; namespace IPA.Config.Stores.Converters { /// /// A for objects normally serialized to config via . /// /// the same type parameter that would be passed into /// public class CustomObjectConverter : ValueConverter where T : class { private interface IImpl { T? FromValue(Value? value, object parent); Value? ToValue(T? obj, object parent); } private class Impl : IImpl where U : class, GeneratedStoreImpl.IGeneratedStore, T { private static readonly GeneratedStoreImpl.GeneratedStoreCreator creator = GeneratedStoreImpl.GetCreator(typeof(T)); private static U Create(GeneratedStoreImpl.IGeneratedStore? parent) => (U)creator(parent); public T? FromValue(Value? value, object parent) { // lots of casting here, but it works i promise (probably) (parent can be a non-IGeneratedStore, however it won't necessarily behave then) if (value is null) return null; var obj = Create(parent as GeneratedStoreImpl.IGeneratedStore); obj.Deserialize(value); return obj; } public Value? ToValue(T? obj, object parent) { if (obj is null) return null; if (obj is GeneratedStoreImpl.IGeneratedStore store) return store.Serialize(); else { var newObj = Create(null); newObj.CopyFrom(obj, false); // don't use lock because it won't be used return newObj.Serialize(); } } } private static readonly IImpl impl = (IImpl)Activator.CreateInstance( typeof(Impl<>).MakeGenericType(typeof(T), GeneratedStoreImpl.GetGeneratedType(typeof(T)))); /// /// Deserializes into a with the given . /// /// the to deserialize /// the parent object that will own the deserialized value /// the deserialized value /// public static T? Deserialize(Value? value, object parent) => impl.FromValue(value, parent); /// /// Serializes into a structure, given . /// /// the object to serialize /// the parent object that owns /// the tree that represents /// public static Value? Serialize(T? obj, object parent) => impl.ToValue(obj, parent); /// /// Deserializes into a with the given . /// /// the to deserialize /// the parent object that will own the deserialized value /// the deserialized value /// public override T? FromValue(Value? value, object parent) => Deserialize(value, parent); /// /// Serializes into a structure, given . /// /// the object to serialize /// the parent object that owns /// the tree that represents /// public override Value? ToValue(T? obj, object parent) => Serialize(obj, parent); } /// /// A for custom value types, serialized identically to the reference types serialized with /// . /// /// the type of the value to convert public class CustomValueTypeConverter : ValueConverter where T : struct { private static readonly GeneratedStoreImpl.SerializeObject serialize = GeneratedStoreImpl.GetSerializerDelegate(); private static readonly GeneratedStoreImpl.DeserializeObject deserialize = GeneratedStoreImpl.GetDeserializerDelegate(); /// /// Deserializes into a with the given . /// /// the to deserialize /// the parent object that will own the deserialized value /// the deserialized value /// public static T Deserialize(Value? value, object parent) => deserialize(value, parent); /// /// Serializes into a corresponding structure. /// /// the object to serialize /// the tree that represents /// public static Value Serialize(T obj) => serialize(obj); /// /// Deserializes into a with the given . /// /// the to deserialize /// the parent object that will own the deserialized value /// the deserialized value /// public override T FromValue(Value? value, object parent) => Deserialize(value, parent); /// /// Serializes into a structure, given . /// /// the object to serialize /// the parent object that owns /// the tree that represents /// public override Value? ToValue(T obj, object parent) => Serialize(obj); } }