using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; namespace Capnp { /// /// Provides functionality to construct domain objects from . /// public static class CapnpSerializable { interface IConstructibleFromDeserializerState { object? Create(DeserializerState state); } class FromStruct: IConstructibleFromDeserializerState where T : ICapnpSerializable, new() { public object Create(DeserializerState state) { var result = new T(); if (state.Kind != ObjectKind.Nil) { result.Deserialize(state); } return result; } } class FromList: IConstructibleFromDeserializerState where T: class { readonly Func _elementSerializer; public FromList() { var deser = GetSerializer(typeof(T)); _elementSerializer = d => (deser(d) as T)!; } public object Create(DeserializerState state) { return state.RequireList().Cast(_elementSerializer); } } class FromCapability: IConstructibleFromDeserializerState where T: class { public object? Create(DeserializerState state) { return state.RequireCap(); } } static object? CreateFromAny(DeserializerState state) { switch (state.Kind) { case ObjectKind.Capability: return state.RequireCap(); case ObjectKind.Nil: return null; default: return state; } } static readonly ConditionalWeakTable> _typeMap = new ConditionalWeakTable>(); static CapnpSerializable() { _typeMap.Add(typeof(object), CreateFromAny); _typeMap.Add(typeof(string), d => d.RequireList().CastText()); _typeMap.Add(typeof(IReadOnlyList), d => d.RequireList().CastBool()); _typeMap.Add(typeof(IReadOnlyList), d => d.RequireList().CastSByte()); _typeMap.Add(typeof(IReadOnlyList), d => d.RequireList().CastByte()); _typeMap.Add(typeof(IReadOnlyList), d => d.RequireList().CastShort()); _typeMap.Add(typeof(IReadOnlyList), d => d.RequireList().CastUShort()); _typeMap.Add(typeof(IReadOnlyList), d => d.RequireList().CastInt()); _typeMap.Add(typeof(IReadOnlyList), d => d.RequireList().CastUInt()); _typeMap.Add(typeof(IReadOnlyList), d => d.RequireList().CastLong()); _typeMap.Add(typeof(IReadOnlyList), d => d.RequireList().CastULong()); _typeMap.Add(typeof(IReadOnlyList), d => d.RequireList().CastFloat()); _typeMap.Add(typeof(IReadOnlyList), d => d.RequireList().CastDouble()); } static Func CreateSerializer(Type type) { if (typeof(ICapnpSerializable).IsAssignableFrom(type)) { try { return ((IConstructibleFromDeserializerState) Activator.CreateInstance(typeof(FromStruct<>).MakeGenericType(type))!).Create; } catch (Exception ex) { throw new ArgumentException( $"Cannot create serializer, probably because serializer {type.Name} does not expose a public parameterless constructor", ex); } } else if (type.IsGenericType && typeof(IReadOnlyList<>) == type.GetGenericTypeDefinition()) { try { var elementType = type.GetGenericArguments()[0]; return ((IConstructibleFromDeserializerState) Activator.CreateInstance(typeof(FromList<>).MakeGenericType(elementType))!).Create; } catch (TargetInvocationException ex) { throw ex.InnerException!; } catch (Exception ex) { throw new ArgumentException( $"Cannot create list serializer, probably because the element type is not a reference type", ex); } } else { try { Rpc.CapabilityReflection.ValidateCapabilityInterface(type); } catch (Exception ex) { throw new ArgumentException( $"Don't know how to construct a serializer from {type.Name}. Tried to interpret it as capability interface, but it didn't work.", ex); } try { return ((IConstructibleFromDeserializerState) Activator.CreateInstance(typeof(FromCapability<>).MakeGenericType(type))!).Create; } catch (Exception ex) { throw new ArgumentException( $"Cannot create serializer, probably because serializer {type.Name} a not a viable capability interface", ex); } } } static Func GetSerializer(Type type) { return _typeMap.GetValue(type, CreateSerializer); } /// /// Constructs a domain object from a given deserializer state. /// /// Type of domain object to construct. Must be one of the following: /// /// Type implementing . The type must must have a public parameterless constructor. /// A capability interface ( for further explanation) /// /// IReadOnlyList<bool>, IReadOnlyList<sbyte>, IReadOnlyList<byte> /// IReadOnlyList<short>, IReadOnlyList<ushort>, IReadOnlyList<int> /// IReadOnlyList<uint>, IReadOnlyList<long>, IReadOnlyList<ulong> /// IReadOnlyList<float>, IReadOnlyList<double> /// IReadOnlyList<T> whereby T is one of the things listed here. /// /// /// deserializer state to construct from /// The domain object instance. Nullability note: The returned reference may be null if /// represents the nil object. /// Cannot construct object of type /// Note that capability ownership is moved to the domain object public static T? Create(DeserializerState state) where T: class { try { return (T?)GetSerializer(typeof(T))(state); } catch (TargetInvocationException ex) { throw new ArgumentException("Failed to construct domain object", ex); } } } }