Skip to content

Commit 7fab069

Browse files
authored
rust enum wrapper (#91)
* a new try to wrap rust enums * added reference to TEnum, for better readability * adding a similar solution for the BaseTuple * updated to rc4
1 parent 27fa88f commit 7fab069

File tree

6 files changed

+418
-3
lines changed

6 files changed

+418
-3
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
using NUnit.Framework;
2+
using Substrate.NetApi.Model.Types.Base;
3+
using Substrate.NetApi.Model.Types.Primitive;
4+
using System;
5+
using System.Collections.Generic;
6+
7+
namespace Substrate.NetApi.Tests
8+
{
9+
public enum PhaseState
10+
{
11+
None = 0,
12+
Finalization = 1,
13+
Initialization = 2
14+
}
15+
16+
[TestFixture]
17+
public class BaseEnumRustTests
18+
{
19+
[Test]
20+
public void ExtEnumEncodingTest()
21+
{
22+
var typeDecoderMap = new Dictionary<PhaseState, Type>
23+
{
24+
{ PhaseState.None, typeof(U8) },
25+
{ PhaseState.Finalization, typeof(BaseVoid) },
26+
{ PhaseState.Initialization, typeof(BaseVoid) }
27+
};
28+
29+
var extEnumType = new BaseEnumRust<PhaseState>(typeDecoderMap);
30+
31+
int p = 0;
32+
extEnumType.Decode(new byte[] { 0x00, 0x01 }, ref p);
33+
34+
Assert.AreEqual(PhaseState.None, extEnumType.Value);
35+
Assert.AreEqual("U8", extEnumType.Value2.GetType().Name);
36+
Assert.AreEqual(1, (extEnumType.Value2 as U8).Value);
37+
}
38+
39+
[Test]
40+
public void ExtEnumDencodingTest()
41+
{
42+
var typeDecoderMap = new Dictionary<PhaseState, Type>
43+
{
44+
{ PhaseState.None, typeof(U8) },
45+
{ PhaseState.Finalization, typeof(BaseVoid) },
46+
{ PhaseState.Initialization, typeof(BaseVoid) }
47+
};
48+
49+
var extEnumType = new BaseEnumRust<PhaseState>(typeDecoderMap);
50+
51+
int p = 0;
52+
extEnumType.Decode(new byte[] { 0x00, 0x01 }, ref p);
53+
54+
Assert.AreEqual(PhaseState.None, extEnumType.Value);
55+
Assert.AreEqual("U8", extEnumType.Value2.GetType().Name);
56+
Assert.AreEqual(1, (extEnumType.Value2 as U8).Value);
57+
58+
Assert.AreEqual(new byte[] { 0x00, 0x01 }, extEnumType.Bytes);
59+
}
60+
61+
[Test]
62+
public void ExtEnumCreateTest()
63+
{
64+
var typeDecoderMap = new Dictionary<PhaseState, Type>
65+
{
66+
{ PhaseState.None, typeof(U8) },
67+
{ PhaseState.Finalization, typeof(BaseVoid) },
68+
{ PhaseState.Initialization, typeof(BaseVoid) }
69+
};
70+
71+
var u8 = new U8(1);
72+
var byValue = new BaseEnumRust<PhaseState>(typeDecoderMap);
73+
byValue.Create(PhaseState.None, u8);
74+
75+
var byArray = new BaseEnumRust<PhaseState>();
76+
byArray.AddTypeDecoder<U8>(PhaseState.None);
77+
byArray.AddTypeDecoder<BaseVoid>(PhaseState.Finalization);
78+
byArray.AddTypeDecoder<BaseVoid>(PhaseState.Initialization);
79+
byArray.Create(new byte[] { 0, 1 });
80+
81+
var byHex = new BaseEnumRust<PhaseState>();
82+
byHex.AddTypeDecoder<U8>(PhaseState.None);
83+
byHex.AddTypeDecoder<BaseVoid>(PhaseState.Finalization);
84+
byHex.AddTypeDecoder<BaseVoid>(PhaseState.Initialization);
85+
byHex.Create("0x0001");
86+
87+
Assert.That(byValue.Bytes, Is.EqualTo(byArray.Bytes));
88+
Assert.That(byValue.Value, Is.EqualTo(byArray.Value));
89+
90+
Assert.That(byValue.Bytes, Is.EqualTo(byHex.Bytes));
91+
Assert.That(byValue.Value, Is.EqualTo(byHex.Value));
92+
}
93+
}
94+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using NUnit.Framework;
2+
using Substrate.NetApi.Model.Types.Base;
3+
using Substrate.NetApi.Model.Types.Primitive;
4+
using System;
5+
using System.Collections.Generic;
6+
7+
namespace Substrate.NetApi.Tests
8+
{
9+
[TestFixture]
10+
public class BaseTupleTests
11+
{
12+
[Test]
13+
public void BaseTupleTest()
14+
{
15+
var u16Param = new U16(42);
16+
17+
var t1 = new BaseTupleRust(typeof(U16));
18+
t1.Create("0x2a00");
19+
20+
Assert.AreEqual(1, t1.Value.Length);
21+
Assert.AreEqual(t1.Bytes, new BaseTupleRust(new U16(42)).Bytes);
22+
Assert.AreEqual(((U16)t1.Value[0]).Value, ((U16)new BaseTupleRust(new U16(42)).Value[0]).Value);
23+
Assert.AreEqual(t1.TypeSize, new BaseTupleRust(new U16(42)).TypeSize);
24+
25+
var t2 = new BaseTupleRust(typeof(U16), typeof(U16));
26+
t2.Create("0x2a002a00");
27+
Assert.AreEqual(2, t2.Value.Length);
28+
Assert.AreEqual(t2.Bytes, new BaseTupleRust(new U16(42), new U16(42)).Bytes);
29+
Assert.AreEqual(((U16)t2.Value[0]).Value, ((U16)new BaseTupleRust(new U16(42), new U16(42)).Value[0]).Value);
30+
Assert.AreEqual(((U16)t2.Value[1]).Value, ((U16)new BaseTupleRust(new U16(42), new U16(42)).Value[1]).Value);
31+
Assert.AreEqual(t2.TypeSize, new BaseTupleRust(new U16(42), new U16(42)).TypeSize);
32+
33+
var t3 = new BaseTupleRust(typeof(U16), typeof(U16), typeof(U16));
34+
t3.Create("0x2a002a002a00");
35+
Assert.AreEqual(3, t3.Value.Length);
36+
Assert.AreEqual(t3.Bytes, new BaseTupleRust(new U16(42), new U16(42), new U16(42)).Bytes);
37+
Assert.AreEqual(((U16)t3.Value[0]).Value, ((U16)new BaseTupleRust(new U16(42), new U16(42), new U16(42)).Value[0]).Value);
38+
Assert.AreEqual(((U16)t3.Value[1]).Value, ((U16)new BaseTupleRust(new U16(42), new U16(42), new U16(42)).Value[1]).Value);
39+
Assert.AreEqual(((U16)t3.Value[2]).Value, ((U16)new BaseTupleRust(new U16(42), new U16(42), new U16(42)).Value[2]).Value);
40+
Assert.AreEqual(t3.TypeSize, new BaseTupleRust(new U16(42), new U16(42), new U16(42)).TypeSize);
41+
}
42+
43+
[Test]
44+
public void BaseTupleCreateTest()
45+
{
46+
var u16 = new U16();
47+
u16.Create("0x2a00");
48+
49+
var u32 = new U32();
50+
u32.Create("0xffffff00");
51+
52+
var tupleOfTwo_1 = new BaseTupleRust(u16, u32);
53+
Assert.AreEqual("0x2A00FFFFFF00", Utils.Bytes2HexString(tupleOfTwo_1.Encode()));
54+
Assert.AreEqual(tupleOfTwo_1.Value.Length, new BaseTupleRust(u16, u32).Value.Length);
55+
Assert.AreEqual(tupleOfTwo_1.Bytes, new BaseTupleRust(u16, u32).Bytes);
56+
Assert.AreEqual(((U16)tupleOfTwo_1.Value[0]).Value, ((U16)new BaseTupleRust(u16, u32).Value[0]).Value);
57+
Assert.AreEqual(((U32)tupleOfTwo_1.Value[1]).Value, ((U32)new BaseTupleRust(u16, u32).Value[1]).Value);
58+
59+
var tupleOfTwo_2 = new BaseTupleRust(typeof(U16), typeof(U32));
60+
tupleOfTwo_2.Create("0x2A00FFFFFF00");
61+
62+
Assert.AreEqual(u16.Value, ((U16)tupleOfTwo_2.Value[0]).Value);
63+
Assert.AreEqual(u32.Value, ((U32)tupleOfTwo_2.Value[1]).Value);
64+
65+
Assert.AreEqual(tupleOfTwo_1.Value.Length, tupleOfTwo_2.Value.Length);
66+
Assert.AreEqual(tupleOfTwo_1.Bytes, tupleOfTwo_2.Bytes);
67+
Assert.AreEqual(((U16)tupleOfTwo_1.Value[0]).Value, ((U16)tupleOfTwo_2.Value[0]).Value);
68+
Assert.AreEqual(((U32)tupleOfTwo_1.Value[1]).Value, ((U32)tupleOfTwo_2.Value[1]).Value);
69+
}
70+
71+
[Test]
72+
public void BaseTypeEqualityWithNullBytes()
73+
{
74+
var baseTuple_1 = new BaseTupleRust(typeof(U32), typeof(U32));
75+
var baseTuple_2 = new BaseTupleRust(typeof(U32), typeof(U32));
76+
Assert.That(baseTuple_1.Bytes, Is.Null);
77+
Assert.That(baseTuple_1.Value, Is.Null);
78+
Assert.AreEqual(baseTuple_1.Bytes, baseTuple_2.Bytes);
79+
Assert.AreEqual(baseTuple_1.Value, baseTuple_2.Value);
80+
81+
var baseOpt_1 = new BaseOpt<U32>();
82+
var baseOpt_2 = new BaseOpt<U32>();
83+
Assert.That(baseOpt_1.Bytes, Is.Null);
84+
Assert.That(baseOpt_1.Value, Is.Null);
85+
Assert.AreEqual(baseOpt_1.Bytes, baseOpt_2.Bytes);
86+
Assert.AreEqual(baseOpt_1.Value, baseOpt_2.Value);
87+
88+
var baseVec_1 = new BaseVec<U32>();
89+
var baseVec_2 = new BaseVec<U32>();
90+
Assert.That(baseVec_1.Bytes, Is.Null);
91+
Assert.That(baseVec_1.Value, Is.Null);
92+
Assert.AreEqual(baseVec_1.Bytes, baseVec_2.Bytes);
93+
Assert.AreEqual(baseVec_1.Value, baseVec_2.Value);
94+
}
95+
}
96+
97+
}

Substrate.NetApi.Test/TypeConverters/TypeEncodingTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public enum PhaseState
112112
[Test]
113113
public void ExtEnumEncodingTest()
114114
{
115-
var extEnumType = new BaseEnumExt<PhaseState, U8, BaseVoid, BaseVoid, BaseVoid, BaseVoid, BaseVoid, BaseVoid, BaseVoid, BaseVoid>();
115+
var extEnumType = new BaseEnumExt<PhaseState, U8, BaseVoid, BaseVoid>();
116116

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

130130
int p = 0;
131131
extEnumType.Decode(new byte[] { 0x00, 0x01 }, ref p);
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Substrate.NetApi.Model.Types.Base
5+
{
6+
/// <summary>
7+
/// Next version of BaseEnumExt to support Rust enums
8+
/// </summary>
9+
/// <typeparam name="TEnum"></typeparam>
10+
public class BaseEnumRust<TEnum> : BaseType where TEnum : Enum
11+
{
12+
private readonly Dictionary<TEnum, Func<byte[], int, Tuple<IType, int>>> _typeDecoders;
13+
14+
/// <summary>
15+
/// Constructor
16+
/// </summary>
17+
public BaseEnumRust()
18+
{
19+
_typeDecoders = new Dictionary<TEnum, Func<byte[], int, Tuple<IType, int>>>();
20+
}
21+
22+
/// <summary>
23+
/// Constructor
24+
/// </summary>
25+
public BaseEnumRust(Dictionary<TEnum, Type> typeDecoderMap)
26+
{
27+
_typeDecoders = new Dictionary<TEnum, Func<byte[], int, Tuple<IType, int>>>();
28+
foreach (var decoder in typeDecoderMap)
29+
{
30+
var enumValue = decoder.Key;
31+
var type = decoder.Value;
32+
33+
_typeDecoders.Add(enumValue, (byteArray, p) =>
34+
{
35+
var typeInstance = (IType)Activator.CreateInstance(type);
36+
typeInstance.Decode(byteArray, ref p);
37+
return new Tuple<IType, int>(typeInstance, p);
38+
});
39+
}
40+
}
41+
42+
/// <summary>
43+
/// Add a type decoder
44+
/// </summary>
45+
/// <typeparam name="TType"></typeparam>
46+
/// <param name="enumValue"></param>
47+
public void AddTypeDecoder<TType>(TEnum enumValue) where TType : IType, new()
48+
{
49+
_typeDecoders.Add(enumValue, (byteArray, p) =>
50+
{
51+
var typeInstance = new TType();
52+
typeInstance.Decode(byteArray, ref p);
53+
return new Tuple<IType, int>(typeInstance, p);
54+
});
55+
}
56+
57+
/// <inheritdoc/>
58+
public override void Decode(byte[] byteArray, ref int p)
59+
{
60+
var start = p;
61+
var enumByte = byteArray[p];
62+
p += 1;
63+
64+
try
65+
{
66+
Value = (TEnum)Enum.Parse(typeof(TEnum), enumByte.ToString(), true);
67+
}
68+
catch (ArgumentException ex)
69+
{
70+
throw new Exception($"Invalid enum value: {enumByte}", ex);
71+
}
72+
73+
if (_typeDecoders.TryGetValue(Value, out var decoder))
74+
{
75+
var result = decoder(byteArray, p);
76+
Value2 = result.Item1;
77+
p = result.Item2;
78+
}
79+
else
80+
{
81+
throw new Exception($"No decoder found for enum byte {enumByte}");
82+
}
83+
84+
TypeSize = p - start;
85+
Bytes = new byte[TypeSize];
86+
Array.Copy(byteArray, start, Bytes, 0, TypeSize);
87+
}
88+
89+
/// <inheritdoc/>
90+
public override byte[] Encode()
91+
{
92+
return Bytes;
93+
}
94+
95+
/// <summary>
96+
/// Create from enum and it's value
97+
/// </summary>
98+
/// <param name="t"></param>
99+
/// <param name="iType"></param>
100+
public void Create(TEnum t, IType iType)
101+
{
102+
var enumByte = Convert.ToByte(t);
103+
104+
if (!_typeDecoders.ContainsKey(t))
105+
{
106+
throw new Exception($"No decoder found for enum byte {enumByte}, make sure to use BaseVoid, if there is no value.");
107+
}
108+
109+
Value = t;
110+
Value2 = iType;
111+
112+
// Encode the enum byte and IType
113+
var bytes = new List<byte> { enumByte };
114+
bytes.AddRange(iType.Encode());
115+
Bytes = bytes.ToArray();
116+
}
117+
118+
/// <inheritdoc/>
119+
public TEnum Value { get; set; }
120+
/// <inheritdoc/>
121+
public IType Value2 { get; set; }
122+
}
123+
}

0 commit comments

Comments
 (0)