Skip to content

Commit

Permalink
Added custom serialization to Either and EitherOrNone
Browse files Browse the repository at this point in the history
  • Loading branch information
stidsborg committed Jan 24, 2025
1 parent 7e3d468 commit 2810b91
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,79 @@ public void OptionWithNoValueCanBeDeserializedByDefaultSerializer()
deserialized.ShouldBe(option);
}

[TestMethod]
public void EithersCanBeDeserializedByDefaultSerializer()
{
{
var either = SerializeAndDeserialize(Either<string, int>.CreateFirst("First"));
either.HasFirst.ShouldBeTrue();
either.First.ShouldBe("First");
}
{
var either = SerializeAndDeserialize(Either<string, int>.CreateSecond(2));
either.HasSecond.ShouldBeTrue();
either.Second.ShouldBe(2);
}

{
var either = SerializeAndDeserialize(Either<string, int, Option<string>>.CreateFirst("First"));
either.HasFirst.ShouldBeTrue();
either.First.ShouldBe("First");
}
{
var either = SerializeAndDeserialize(Either<string, int, Option<string>>.CreateSecond(2));
either.HasSecond.ShouldBeTrue();
either.Second.ShouldBe(2);
}
{
var either = SerializeAndDeserialize(Either<string, int, Option<string>>.CreateThird(Option.Create("option")));
either.HasThird.ShouldBeTrue();
either.Third.ShouldBe(Option.Create<string>("option"));
}

{
var either = SerializeAndDeserialize(EitherOrNone<string, int>.CreateFirst("First"));
either.HasFirst.ShouldBeTrue();
either.First.ShouldBe("First");
}
{
var either = SerializeAndDeserialize(EitherOrNone<string, int>.CreateSecond(2));
either.HasSecond.ShouldBeTrue();
either.Second.ShouldBe(2);
}
{
var either = SerializeAndDeserialize(EitherOrNone<string, int>.CreateNone());
either.HasNone.ShouldBeTrue();
}

{
var either = SerializeAndDeserialize(EitherOrNone<string, int, Option<string>>.CreateFirst("First"));
either.HasFirst.ShouldBeTrue();
either.First.ShouldBe("First");
}
{
var either = SerializeAndDeserialize(EitherOrNone<string, int, Option<string>>.CreateSecond(2));
either.HasSecond.ShouldBeTrue();
either.Second.ShouldBe(2);
}
{
var either = SerializeAndDeserialize(EitherOrNone<string, int, Option<string>>.CreateThird(Option.Create("option")));
either.HasThird.ShouldBeTrue();
either.Third.ShouldBe(Option.Create<string>("option"));
}
{
var either = SerializeAndDeserialize(EitherOrNone<string, int, Option<string>>.CreateNone());
either.HasNone.ShouldBeTrue();
}
}

private T SerializeAndDeserialize<T>(T value)
{
var serializer = new CustomSerializableDecorator(DefaultSerializer.Instance);
var bytes = serializer.Serialize(value);
return serializer.Deserialize<T>(bytes);
}

[TestMethod]
public void ExceptionCanBeConvertedToAndFromFatalWorkflowException()
{
Expand Down
77 changes: 68 additions & 9 deletions Core/Cleipnir.ResilientFunctions/Reactive/Utilities/Either.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Cleipnir.ResilientFunctions.CoreRuntime.Serialization;

namespace Cleipnir.ResilientFunctions.Reactive.Utilities;

public class Either<T1, T2>
public class Either<T1, T2> : ICustomSerializable
{
public Value ValueSpecified { get; }

Expand Down Expand Up @@ -94,17 +96,44 @@ public T As<T>() where T : T1, T2
};
}

public enum Value
public enum Value : byte
{
First,
Second
First = 1,
Second = 2
}

public static Either<T1, T2> CreateFirst(T1 first) => new(Value.First, first, second: default!);
public static Either<T1, T2> CreateSecond(T2 second) => new(Value.Second, first: default!, second);

public byte[] Serialize(ISerializer serializer)
{
var valueSpecified = (byte) ValueSpecified;
byte[] valueBytes;
if (ValueSpecified == Value.First)
valueBytes = serializer.Serialize(_first);
else if (ValueSpecified == Value.Second)
valueBytes = serializer.Serialize(_second);
else
throw new SerializationException("Unexpected ValueSpecified-value");

var bytes = new byte[valueBytes.Length + 1];
bytes[0] = valueSpecified;
valueBytes.CopyTo(bytes, index: 1);
return bytes;
}

public static object Deserialize(byte[] bytes, ISerializer serializer)
{
if (bytes[0] == 1)
return CreateFirst(serializer.Deserialize<T1>(bytes[1..]));
if(bytes[0] == 2)
return CreateSecond(serializer.Deserialize<T2>(bytes[1..]));

throw new SerializationException("Unexpected byte value");
}
}

public class Either<T1, T2, T3>
public class Either<T1, T2, T3> : ICustomSerializable
{
public Value ValueSpecified { get; }

Expand Down Expand Up @@ -201,14 +230,44 @@ public T As<T>() where T : T1, T2, T3
};
}

public enum Value
public enum Value : byte
{
First,
Second,
Third
First = 1,
Second = 2,
Third = 3
}

public static Either<T1, T2, T3> CreateFirst(T1 first) => new(Value.First, first, second: default!, third: default!);
public static Either<T1, T2, T3> CreateSecond(T2 second) => new(Value.Second, first: default!, second, third: default!);
public static Either<T1, T2, T3> CreateThird(T3 third) => new(Value.Third, first: default!, second: default!, third);
public byte[] Serialize(ISerializer serializer)
{
var valueSpecified = (byte) ValueSpecified;
byte[] valueBytes;
if (ValueSpecified == Value.First)
valueBytes = serializer.Serialize(_first);
else if (ValueSpecified == Value.Second)
valueBytes = serializer.Serialize(_second);
else if (ValueSpecified == Value.Third)
valueBytes = serializer.Serialize(_third);
else
throw new SerializationException("Unexpected ValueSpecified-value");

var bytes = new byte[valueBytes.Length + 1];
bytes[0] = valueSpecified;
valueBytes.CopyTo(bytes, index: 1);
return bytes;
}

public static object Deserialize(byte[] bytes, ISerializer serializer)
{
if (bytes[0] == 1)
return CreateFirst(serializer.Deserialize<T1>(bytes[1..]));
if(bytes[0] == 2)
return CreateSecond(serializer.Deserialize<T2>(bytes[1..]));
if(bytes[0] == 3)
return CreateThird(serializer.Deserialize<T3>(bytes[1..]));

throw new SerializationException("Unexpected byte value");
}
}
88 changes: 78 additions & 10 deletions Core/Cleipnir.ResilientFunctions/Reactive/Utilities/EitherOrNone.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Cleipnir.ResilientFunctions.CoreRuntime.Serialization;

namespace Cleipnir.ResilientFunctions.Reactive.Utilities;

public class EitherOrNone<T1, T2>
public class EitherOrNone<T1, T2> : ICustomSerializable
{
public Value ValueSpecified { get; }

Expand Down Expand Up @@ -105,11 +107,11 @@ public async Task Do(Func<T1, Task> first, Func<T2, Task> second, Func<Task> non
};
}

public enum Value
public enum Value : byte
{
None,
First,
Second
None = 0,
First = 1,
Second = 2
}

public static EitherOrNone<T1, T2> CreateNone() => new(Value.None, first: default!, second: default!);
Expand All @@ -118,9 +120,40 @@ public enum Value

public static EitherOrNone<T1, T2> CreateFromEither(Either<T1, T2> either) =>
either.HasFirst ? CreateFirst(either.First) : CreateSecond(either.Second);

public byte[] Serialize(ISerializer serializer)
{
var valueSpecified = (byte) ValueSpecified;
byte[] valueBytes;
if (ValueSpecified == Value.None)
return [0];
if (ValueSpecified == Value.First)
valueBytes = serializer.Serialize(_first);
else if (ValueSpecified == Value.Second)
valueBytes = serializer.Serialize(_second);
else
throw new SerializationException("Unexpected ValueSpecified-value");

var bytes = new byte[valueBytes.Length + 1];
bytes[0] = valueSpecified;
valueBytes.CopyTo(bytes, index: 1);
return bytes;
}

public static object Deserialize(byte[] bytes, ISerializer serializer)
{
if (bytes[0] == 0)
return CreateNone();
if (bytes[0] == 1)
return CreateFirst(serializer.Deserialize<T1>(bytes[1..]));
if(bytes[0] == 2)
return CreateSecond(serializer.Deserialize<T2>(bytes[1..]));

throw new SerializationException("Unexpected byte value");
}
}

public class EitherOrNone<T1, T2, T3>
public class EitherOrNone<T1, T2, T3> : ICustomSerializable
{
public Value ValueSpecified { get; }

Expand Down Expand Up @@ -249,15 +282,50 @@ public async Task Do(Func<T1, Task> first, Func<T2, Task> second, Func<T3, Task>

public enum Value
{
None,
First,
Second,
Third
None = 0,
First = 1,
Second = 2,
Third = 3
}

public static EitherOrNone<T1, T2, T3> CreateNone() => new(Value.None, first: default!, second: default!, third: default!);
public static EitherOrNone<T1, T2, T3> CreateFirst(T1 first) => new(Value.First, first, second: default!, third: default!);
public static EitherOrNone<T1, T2, T3> CreateSecond(T2 second) => new(Value.Second, first: default!, second, third: default!);
public static EitherOrNone<T1, T2, T3> CreateThird(T3 third) => new(Value.Third, first: default!, second: default!, third);
public static EitherOrNone<T1, T2, T3> CreateFromEither(Either<T1, T2, T3> either) => either.Match(CreateFirst, CreateSecond, CreateThird);

public byte[] Serialize(ISerializer serializer)
{
var valueSpecified = (byte) ValueSpecified;
byte[] valueBytes;
if (ValueSpecified == Value.None)
return [0];
if (ValueSpecified == Value.First)
valueBytes = serializer.Serialize(_first);
else if (ValueSpecified == Value.Second)
valueBytes = serializer.Serialize(_second);
else if (ValueSpecified == Value.Third)
valueBytes = serializer.Serialize(_third);
else
throw new SerializationException("Unexpected ValueSpecified-value");

var bytes = new byte[valueBytes.Length + 1];
bytes[0] = valueSpecified;
valueBytes.CopyTo(bytes, index: 1);
return bytes;
}

public static object Deserialize(byte[] bytes, ISerializer serializer)
{
if (bytes[0] == 0)
return CreateNone();
if (bytes[0] == 1)
return CreateFirst(serializer.Deserialize<T1>(bytes[1..]));
if(bytes[0] == 2)
return CreateSecond(serializer.Deserialize<T2>(bytes[1..]));
if(bytes[0] == 3)
return CreateThird(serializer.Deserialize<T3>(bytes[1..]));

throw new SerializationException("Unexpected byte value");
}
}

0 comments on commit 2810b91

Please sign in to comment.