2019-06-12 21:56:55 +02:00
using System ;
using System.Collections.Generic ;
using System.Reflection ;
using System.Runtime.CompilerServices ;
namespace Capnp
{
/// <summary>
/// Provides functionality to construct domain objects from <see cref="DeserializerState"/>.
/// </summary>
public static class CapnpSerializable
{
2020-01-11 17:21:31 +01:00
interface IConstructibleFromDeserializerState
2019-06-12 21:56:55 +02:00
{
2020-01-11 17:21:31 +01:00
object? Create ( DeserializerState state ) ;
2019-06-12 21:56:55 +02:00
}
2020-01-11 17:21:31 +01:00
class FromStruct < T > : IConstructibleFromDeserializerState
2019-06-12 21:56:55 +02:00
where T : ICapnpSerializable , new ( )
{
2020-01-11 17:21:31 +01:00
public object Create ( DeserializerState state )
2019-06-12 21:56:55 +02:00
{
var result = new T ( ) ;
if ( state . Kind ! = ObjectKind . Nil )
{
result . Deserialize ( state ) ;
}
return result ;
}
}
2020-01-11 17:21:31 +01:00
class FromList < T > : IConstructibleFromDeserializerState
2019-06-12 21:56:55 +02:00
where T : class
{
readonly Func < DeserializerState , T > _elementSerializer ;
public FromList ( )
{
2020-03-01 14:12:20 +01:00
var deser = GetSerializer ( typeof ( T ) ) ;
_elementSerializer = d = > ( deser ( d ) as T ) ! ;
2019-06-12 21:56:55 +02:00
}
2020-01-11 17:21:31 +01:00
public object Create ( DeserializerState state )
2019-06-12 21:56:55 +02:00
{
return state . RequireList ( ) . Cast ( _elementSerializer ) ;
}
}
2020-01-11 17:21:31 +01:00
class FromCapability < T > : IConstructibleFromDeserializerState
2019-06-12 21:56:55 +02:00
where T : class
{
2020-01-11 17:21:31 +01:00
public object? Create ( DeserializerState state )
2019-06-12 21:56:55 +02:00
{
return state . RequireCap < T > ( ) ;
}
}
2020-01-11 17:21:31 +01:00
static readonly ConditionalWeakTable < Type , Func < DeserializerState , object? > > _typeMap =
new ConditionalWeakTable < Type , Func < DeserializerState , object? > > ( ) ;
2019-06-12 21:56:55 +02:00
static CapnpSerializable ( )
{
_typeMap . Add ( typeof ( string ) , d = > d . RequireList ( ) . CastText ( ) ) ;
_typeMap . Add ( typeof ( IReadOnlyList < bool > ) , d = > d . RequireList ( ) . CastBool ( ) ) ;
_typeMap . Add ( typeof ( IReadOnlyList < sbyte > ) , d = > d . RequireList ( ) . CastSByte ( ) ) ;
_typeMap . Add ( typeof ( IReadOnlyList < byte > ) , d = > d . RequireList ( ) . CastByte ( ) ) ;
_typeMap . Add ( typeof ( IReadOnlyList < short > ) , d = > d . RequireList ( ) . CastShort ( ) ) ;
_typeMap . Add ( typeof ( IReadOnlyList < ushort > ) , d = > d . RequireList ( ) . CastUShort ( ) ) ;
_typeMap . Add ( typeof ( IReadOnlyList < int > ) , d = > d . RequireList ( ) . CastInt ( ) ) ;
_typeMap . Add ( typeof ( IReadOnlyList < uint > ) , d = > d . RequireList ( ) . CastUInt ( ) ) ;
_typeMap . Add ( typeof ( IReadOnlyList < long > ) , d = > d . RequireList ( ) . CastLong ( ) ) ;
_typeMap . Add ( typeof ( IReadOnlyList < ulong > ) , d = > d . RequireList ( ) . CastULong ( ) ) ;
_typeMap . Add ( typeof ( IReadOnlyList < float > ) , d = > d . RequireList ( ) . CastFloat ( ) ) ;
_typeMap . Add ( typeof ( IReadOnlyList < double > ) , d = > d . RequireList ( ) . CastDouble ( ) ) ;
}
2020-01-11 17:21:31 +01:00
static Func < DeserializerState , object? > CreateSerializer ( Type type )
2019-06-12 21:56:55 +02:00
{
if ( typeof ( ICapnpSerializable ) . IsAssignableFrom ( type ) )
{
try
{
2020-01-11 17:21:31 +01:00
return ( ( IConstructibleFromDeserializerState )
Activator . CreateInstance ( typeof ( FromStruct < > ) . MakeGenericType ( type ) ) ! ) . Create ;
2019-06-12 21:56:55 +02:00
}
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 ] ;
2020-01-11 17:21:31 +01:00
return ( ( IConstructibleFromDeserializerState )
Activator . CreateInstance ( typeof ( FromList < > ) . MakeGenericType ( elementType ) ) ! ) . Create ;
2019-06-12 21:56:55 +02:00
}
catch ( TargetInvocationException ex )
{
2020-01-11 17:21:31 +01:00
throw ex . InnerException ! ;
2019-06-12 21:56:55 +02:00
}
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
{
2020-01-11 17:21:31 +01:00
return ( ( IConstructibleFromDeserializerState )
Activator . CreateInstance ( typeof ( FromCapability < > ) . MakeGenericType ( type ) ) ! ) . Create ;
2019-06-12 21:56:55 +02:00
}
catch ( Exception ex )
{
throw new ArgumentException (
$"Cannot create serializer, probably because serializer {type.Name} a not a viable capability interface" ,
ex ) ;
}
}
}
2020-01-11 17:21:31 +01:00
static Func < DeserializerState , object? > GetSerializer ( Type type )
2019-06-12 21:56:55 +02:00
{
return _typeMap . GetValue ( type , CreateSerializer ) ;
}
/// <summary>
/// Constructs a domain object from a given deserializer state.
/// </summary>
/// <typeparam name="T">Type of domain object to construct. Must be one of the following:
/// <list type="bullet">
/// <item><description>Type implementing <see cref="ICapnpSerializable"/>. The type must must have a public parameterless constructor.</description></item>
/// <item><description>A capability interface (<seealso cref="Rpc.InvalidCapabilityInterfaceException"/> for further explanation)</description></item>
2020-03-01 14:12:20 +01:00
/// <item><description><see cref="string"/></description></item>
/// <item><description>IReadOnlyList<bool>, IReadOnlyList<sbyte>, IReadOnlyList<byte></description></item>
/// <item><description>IReadOnlyList<short>, IReadOnlyList<ushort>, IReadOnlyList<int></description></item>
/// <item><description>IReadOnlyList<uint>, IReadOnlyList<long>, IReadOnlyList<ulong></description></item>
/// <item><description>IReadOnlyList<float>, IReadOnlyList<double></description></item>
/// <item><description>IReadOnlyList<T> whereby T is one of the things listed here.</description></item>
2019-06-12 21:56:55 +02:00
/// </list>
/// </typeparam>
2020-01-22 22:21:11 +01:00
/// <param name="state">deserializer state to construct from</param>
2020-03-01 13:18:55 +01:00
/// <returns>The domain object instance. Nullability note: The returned reference may be null if
/// <paramref name="state"/> represents the nil object.</returns>
2020-03-01 14:12:20 +01:00
/// <exception cref="ArgumentException">Cannot construct object of type <typeparamref name="T"/></exception>
2020-03-21 13:27:46 +01:00
/// <remarks>Note that capability ownership is moved to the domain object</remarks>
2020-01-11 17:21:31 +01:00
public static T ? Create < T > ( DeserializerState state )
2019-06-12 21:56:55 +02:00
where T : class
{
2020-03-01 14:12:20 +01:00
try
{
return ( T ? ) GetSerializer ( typeof ( T ) ) ( state ) ;
}
catch ( TargetInvocationException ex )
{
throw new ArgumentException ( "Failed to construct domain object" , ex ) ;
}
2019-06-12 21:56:55 +02:00
}
}
2020-01-11 17:56:12 +01:00
}