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()
{
_elementSerializer = (Func)GetSerializer(typeof(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 readonly ConditionalWeakTable> _typeMap =
new ConditionalWeakTable>();
static CapnpSerializable()
{
_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{Boolean}
/// IReadOnlyList{SByte}"
/// IReadOnlyList{Byte}"
/// IReadOnlyList{Int16}"
/// IReadOnlyList{UInt16}"
/// IReadOnlyList{Int32}"
/// IReadOnlyList{UInt32}"
/// IReadOnlyList{Int64}"
/// IReadOnlyList{UInt64}"
/// IReadOnlyList{Single}"
/// 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 will be null if (and only if) is a capability interface and
/// represents the nil object (obtained from a null pointer). For all other types, when the state is nil,
/// the method still constructs a valid but "empty" object instance (such as domain object without any properties set, empty string, empty list etc.)
public static T? Create(DeserializerState state)
where T: class
{
return (T?)GetSerializer(typeof(T))(state);
}
}
}