Skip to content

Commit

Permalink
a new try to wrap rust enums
Browse files Browse the repository at this point in the history
  • Loading branch information
darkfriend77 committed Sep 11, 2024
1 parent 27fa88f commit f4ee76b
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 2 deletions.
99 changes: 99 additions & 0 deletions Substrate.NetApi.Test/TypeConverters/EnumRustTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using NUnit.Framework;
using Substrate.NetApi.Model.Types;
using Substrate.NetApi.Model.Types.Base;
using Substrate.NetApi.Model.Types.Primitive;
using Substrate.NetApi.Test;
using System;
using System.Collections.Generic;

namespace Substrate.NetApi.Tests
{
public enum PhaseState
{
None = 0,
Finalization = 1,
Initialization = 2
}

[TestFixture]
public class BaseEnumRustTests
{
[Test]
public void ExtEnumEncodingTest()
{
var typeDecoderMap = new Dictionary<byte, Type>
{
{ 0x00, typeof(U8) },
{ 0x01, typeof(BaseVoid) },
{ 0x02, typeof(BaseVoid) }
};

var extEnumType = new BaseEnumRust<PhaseState>(typeDecoderMap);


int p = 0;
extEnumType.Decode(new byte[] { 0x00, 0x01 }, ref p);

Assert.AreEqual(PhaseState.None, extEnumType.Value);
Assert.AreEqual("U8", extEnumType.Value2.GetType().Name);
Assert.AreEqual(1, (extEnumType.Value2 as U8).Value);
}


[Test]
public void ExtEnumDencodingTest()
{
var typeDecoderMap = new Dictionary<byte, Type>
{
{ 0x00, typeof(U8) },
{ 0x01, typeof(BaseVoid) },
{ 0x02, typeof(BaseVoid) }
};

var extEnumType = new BaseEnumRust<PhaseState>(typeDecoderMap);

int p = 0;
extEnumType.Decode(new byte[] { 0x00, 0x01 }, ref p);

Assert.AreEqual(PhaseState.None, extEnumType.Value);
Assert.AreEqual("U8", extEnumType.Value2.GetType().Name);
Assert.AreEqual(1, (extEnumType.Value2 as U8).Value);

Assert.AreEqual(new byte[] { 0x00, 0x01 }, extEnumType.Bytes);
}

[Test]
public void ExtEnumCreateTest()
{
var typeDecoderMap = new Dictionary<byte, Type>
{
{ 0x00, typeof(U8) },
{ 0x01, typeof(BaseVoid) },
{ 0x02, typeof(BaseVoid) }
};

var u8 = new U8(1);
var byValue = new BaseEnumRust<PhaseState>(typeDecoderMap);
byValue.Create(PhaseState.None, u8);

var byArray = new BaseEnumRust<PhaseState>();
byArray.AddTypeDecoder<U8>(0x00);
byArray.AddTypeDecoder<BaseVoid>(0x01);
byArray.AddTypeDecoder<BaseVoid>(0x02);
byArray.Create(new byte[] { 0, 1 });

var byHex = new BaseEnumRust<PhaseState>();
byHex.AddTypeDecoder<U8>(0x00);
byHex.AddTypeDecoder<BaseVoid>(0x01);
byHex.AddTypeDecoder<BaseVoid>(0x02);
byHex.Create("0x0001");

Assert.That(byValue.Bytes, Is.EqualTo(byArray.Bytes));
Assert.That(byValue.Value, Is.EqualTo(byArray.Value));

Assert.That(byValue.Bytes, Is.EqualTo(byHex.Bytes));
Assert.That(byValue.Value, Is.EqualTo(byHex.Value));
}
}

}
4 changes: 2 additions & 2 deletions Substrate.NetApi.Test/TypeConverters/TypeEncodingTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public enum PhaseState
[Test]
public void ExtEnumEncodingTest()
{
var extEnumType = new BaseEnumExt<PhaseState, U8, BaseVoid, BaseVoid, BaseVoid, BaseVoid, BaseVoid, BaseVoid, BaseVoid, BaseVoid>();
var extEnumType = new BaseEnumExt<PhaseState, U8, BaseVoid, BaseVoid>();

int p = 0;
extEnumType.Decode(new byte[] { 0x00, 0x01 }, ref p);
Expand All @@ -125,7 +125,7 @@ public void ExtEnumEncodingTest()
[Test]
public void ExtEnumDencodingTest()
{
var extEnumType = new BaseEnumExt<PhaseState, U8, BaseVoid, BaseVoid, BaseVoid, BaseVoid, BaseVoid, BaseVoid, BaseVoid, BaseVoid>();
var extEnumType = new BaseEnumExt<PhaseState, U8, BaseVoid, BaseVoid>();

int p = 0;
extEnumType.Decode(new byte[] { 0x00, 0x01 }, ref p);
Expand Down
116 changes: 116 additions & 0 deletions Substrate.NetApi/Model/Types/Base/BaseEnumRust.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;

namespace Substrate.NetApi.Model.Types.Base
{
/// <summary>
/// Next version of BaseEnumExt to support Rust enums
/// </summary>
/// <typeparam name="TEnum"></typeparam>
public class BaseEnumRust<TEnum> : BaseEnumType where TEnum : Enum
{
private readonly Dictionary<byte, Func<byte[], int, Tuple<IType, int>>> _typeDecoders;

/// <summary>
/// Constructor
/// </summary>
public BaseEnumRust()
{
_typeDecoders = new Dictionary<byte, Func<byte[], int, Tuple<IType, int>>>();
}

/// <summary>
/// Constructor
/// </summary>
public BaseEnumRust(Dictionary<byte, Type> typeDecoderMap)
{
_typeDecoders = new Dictionary<byte, Func<byte[], int, Tuple<IType, int>>>();
foreach (var decoder in typeDecoderMap)
{
var enumByte = decoder.Key;
var type = decoder.Value;

_typeDecoders.Add(enumByte, (byteArray, p) =>
{
var typeInstance = (IType)Activator.CreateInstance(type);
typeInstance.Decode(byteArray, ref p);
return new Tuple<IType, int>(typeInstance, p);
});
}
}

/// <summary>
/// Add a type decoder
/// </summary>
/// <typeparam name="TType"></typeparam>
/// <param name="enumByte"></param>
public void AddTypeDecoder<TType>(byte enumByte) where TType : IType, new()
{
_typeDecoders.Add(enumByte, (byteArray, p) =>
{
var typeInstance = new TType();
typeInstance.Decode(byteArray, ref p);
return new Tuple<IType, int>(typeInstance, p);
});
}

/// <inheritdoc/>
public override void Decode(byte[] byteArray, ref int p)
{
var start = p;
var enumByte = byteArray[p];
p += 1;

Value = (TEnum)Enum.Parse(typeof(TEnum), enumByte.ToString(), true);

if (_typeDecoders.TryGetValue(enumByte, out var decoder))
{
var result = decoder(byteArray, p);
Value2 = result.Item1;
p = result.Item2;
}
else
{
throw new Exception($"No decoder found for enum byte {enumByte}");
}

TypeSize = p - start;
Bytes = new byte[TypeSize];
Array.Copy(byteArray, start, Bytes, 0, TypeSize);
}

/// <inheritdoc/>
public override byte[] Encode()
{
return Bytes;
}

/// <summary>
/// Create from enum and it's value
/// </summary>
/// <param name="t"></param>
/// <param name="iType"></param>
public void Create(TEnum t, IType iType)
{
var enumByte = Convert.ToByte(t);

if (!_typeDecoders.ContainsKey(enumByte))
{
throw new Exception($"No decoder found for enum byte {enumByte}, make sure to use BaseVoid, if there is no value.");
}

Value = t;
Value2 = iType;

// Encode the enum byte and IType
var bytes = new List<byte> { enumByte };
bytes.AddRange(iType.Encode());
Bytes = bytes.ToArray();
}

/// <inheritdoc/>
public TEnum Value { get; set; }
/// <inheritdoc/>
public IType Value2 { get; set; }
}
}

0 comments on commit f4ee76b

Please sign in to comment.