Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5494,26 +5494,37 @@ namespace Akka.Util
public static Akka.Util.Option<T> op_Implicit(T value) { }
public static bool !=(Akka.Util.Option<T> left, Akka.Util.Option<T> right) { }
}
[System.Runtime.CompilerServices.NullableAttribute(0)]
public class static Result
{
public static Akka.Util.Result<T> Failure<T>(System.Exception exception) { }
public static Akka.Util.Result<T> From<T>(System.Func<T> func) { }
public static Akka.Util.Result<T> FromTask<T>(System.Threading.Tasks.Task<T> task) { }
public static Akka.Util.Result<T> Success<T>(T value) { }
[return: System.Runtime.CompilerServices.NullableAttribute(new byte[] {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API change - make fields into nullable properties.

0,
1})]
public static Akka.Util.Result<T> Failure<[System.Runtime.CompilerServices.NullableAttribute(2)] T>(System.Exception exception) { }
[return: System.Runtime.CompilerServices.NullableAttribute(new byte[] {
0,
1})]
public static Akka.Util.Result<T> From<[System.Runtime.CompilerServices.NullableAttribute(2)] T>(System.Func<T> func) { }
[return: System.Runtime.CompilerServices.NullableAttribute(new byte[] {
0,
1})]
public static Akka.Util.Result<T> FromTask<[System.Runtime.CompilerServices.NullableAttribute(2)] T>(System.Threading.Tasks.Task<T> task) { }
[return: System.Runtime.CompilerServices.NullableAttribute(new byte[] {
0,
1})]
public static Akka.Util.Result<T> Success<[System.Runtime.CompilerServices.NullableAttribute(2)] T>(T value) { }
}
public struct Result<T> : System.IEquatable<Akka.Util.Result<T>>
[System.Runtime.CompilerServices.IsReadOnlyAttribute()]
public struct Result<[System.Runtime.CompilerServices.NullableAttribute(2)] T> : System.IEquatable<Akka.Util.Result<T>>
{
public readonly System.Exception Exception;
public readonly bool IsSuccess;
public readonly T Value;
public Result(T value) { }
public Result(System.Exception exception) { }
public bool Equals(Akka.Util.Result<T> other) { }
public override bool Equals(object obj) { }
public override int GetHashCode() { }
[System.Runtime.CompilerServices.NullableAttribute(2)]
public System.Exception Exception { get; }
public bool IsSuccess { get; }
[System.Runtime.CompilerServices.NullableAttribute(2)]
public T Value { get; }
public override string ToString() { }
public static bool ==(Akka.Util.Result<T> left, Akka.Util.Result<T> right) { }
public static bool !=(Akka.Util.Result<T> left, Akka.Util.Result<T> right) { }
}
public class Right<T>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5483,26 +5483,36 @@ namespace Akka.Util
public static Akka.Util.Option<T> op_Implicit(T value) { }
public static bool !=(Akka.Util.Option<T> left, Akka.Util.Option<T> right) { }
}
[System.Runtime.CompilerServices.NullableAttribute(0)]
public class static Result
{
public static Akka.Util.Result<T> Failure<T>(System.Exception exception) { }
public static Akka.Util.Result<T> From<T>(System.Func<T> func) { }
public static Akka.Util.Result<T> FromTask<T>(System.Threading.Tasks.Task<T> task) { }
public static Akka.Util.Result<T> Success<T>(T value) { }
[return: System.Runtime.CompilerServices.NullableAttribute(new byte[] {
0,
1})]
public static Akka.Util.Result<T> Failure<[System.Runtime.CompilerServices.NullableAttribute(2)] T>(System.Exception exception) { }
[return: System.Runtime.CompilerServices.NullableAttribute(new byte[] {
0,
1})]
public static Akka.Util.Result<T> From<[System.Runtime.CompilerServices.NullableAttribute(2)] T>(System.Func<T> func) { }
[return: System.Runtime.CompilerServices.NullableAttribute(new byte[] {
0,
1})]
public static Akka.Util.Result<T> FromTask<[System.Runtime.CompilerServices.NullableAttribute(2)] T>(System.Threading.Tasks.Task<T> task) { }
[return: System.Runtime.CompilerServices.NullableAttribute(new byte[] {
0,
1})]
public static Akka.Util.Result<T> Success<[System.Runtime.CompilerServices.NullableAttribute(2)] T>(T value) { }
}
public struct Result<T> : System.IEquatable<Akka.Util.Result<T>>
public struct Result<[System.Runtime.CompilerServices.NullableAttribute(2)] T> : System.IEquatable<Akka.Util.Result<T>>
{
public readonly System.Exception Exception;
public readonly bool IsSuccess;
public readonly T Value;
public Result(T value) { }
public Result(System.Exception exception) { }
public bool Equals(Akka.Util.Result<T> other) { }
public override bool Equals(object obj) { }
public override int GetHashCode() { }
[System.Runtime.CompilerServices.NullableAttribute(2)]
public System.Exception Exception { get; }
public bool IsSuccess { get; }
[System.Runtime.CompilerServices.NullableAttribute(2)]
public T Value { get; }
public override string ToString() { }
public static bool ==(Akka.Util.Result<T> left, Akka.Util.Result<T> right) { }
public static bool !=(Akka.Util.Result<T> left, Akka.Util.Result<T> right) { }
}
public class Right<T>
{
Expand Down
21 changes: 21 additions & 0 deletions src/core/Akka.Tests/Util/ResultSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,27 @@ public void IncompleteTaskResult()
.Should().Throw<ArgumentException>().WithMessage("Task is not completed.*");
}

[Fact]
public void ResultEqualitySpec()
{
var result1 = Result.Success(1);
var result2 = Result.Success(1);
var exception = new TestException("BOOM"); // equality by value does not work for exceptions
var result3 = Result.Failure<int>(exception);
var result4 = Result.Failure<int>(exception);
var result5 = Result.Success("foo");
var result6 = Result.Success("bar");
var result51 = Result.Success("foo");

result1.Equals(result2).Should().BeTrue();
result1.Equals(result3).Should().BeFalse();
result3.Equals(result4).Should().BeTrue();
result5.Equals(result51).Should().BeTrue();
(result5 == result51).Should().BeTrue(); // test operator overloads
(result5 == result6).Should().BeFalse(); // test operator overloads
(result5 != result6).Should().BeTrue(); // test operator overloads
}

private static Task<int> CompletedTask(int n)
{
var tcs = new TaskCompletionSource<int>();
Expand Down
116 changes: 18 additions & 98 deletions src/core/Akka/Util/Result.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,30 @@

using System;
using System.Threading.Tasks;
#nullable enable

namespace Akka.Util
{
//A generic type can't have a explicit layout
//[StructLayout(LayoutKind.Explicit)]
/// <summary>
/// TBD
/// A result type frequently used inside Akka.Streams and elsewhere.
/// </summary>
/// <typeparam name="T">TBD</typeparam>
public struct Result<T> : IEquatable<Result<T>>
public readonly record struct Result<T>
{
//[FieldOffset(0)]
/// <summary>
/// TBD
/// </summary>
public readonly bool IsSuccess;
//[FieldOffset(1)]
/// <summary>
/// TBD
/// <c>true</c> if the result is successful, <c>false</c> otherwise.
/// </summary>
public readonly T Value;
//[FieldOffset(1)]
public bool IsSuccess { get; }

/// <summary>
/// TBD
/// <c>null</c> when <see cref="IsSuccess"/> is <c>false</c>.
/// </summary>
public readonly Exception Exception;

public T? Value { get; }
/// <summary>
/// TBD
/// <c>null</c> when <see cref="IsSuccess"/> is <c>true</c>.
/// </summary>
/// <param name="value">TBD</param>
public Exception? Exception { get; }

public Result(T value) : this()
{
IsSuccess = true;
Expand All @@ -52,89 +45,25 @@ public Result(Exception exception) : this()
IsSuccess = false;
Exception = exception;
}


public bool Equals(Result<T> other)
{
if (IsSuccess ^ other.IsSuccess) return false;
return IsSuccess
? Equals(Value, other.Value)
: Equals(Exception, other.Exception);
}


public override bool Equals(object obj)
{
if (obj is Result<T> result) return Equals(result);
return false;
}


public override int GetHashCode()
{
return IsSuccess
? (Value == null ? 0 : Value.GetHashCode())
: (Exception == null ? 0 : Exception.GetHashCode());
}

/// <summary>
/// Compares two specified <see cref="Result{T}"/> for equality.
/// </summary>
/// <param name="left">The first <see cref="Result{T}"/> used for comparison</param>
/// <param name="right">The second <see cref="Result{T}"/> used for comparison</param>
/// <returns><c>true</c> if both <see cref="Result{T}"/> are equal; otherwise <c>false</c></returns>
public static bool operator ==(Result<T> left, Result<T> right)
{
return left.Equals(right);
}

/// <summary>
/// Compares two specified <see cref="Result{T}"/> for inequality.
/// </summary>
/// <param name="left">The first <see cref="Result{T}"/> used for comparison</param>
/// <param name="right">The second <see cref="Result{T}"/> used for comparison</param>
/// <returns><c>true</c> if both <see cref="Result{T}"/> are not equal; otherwise <c>false</c></returns>
public static bool operator !=(Result<T> left, Result<T> right)
{
return !(left == right);
}

public override string ToString() => IsSuccess ? $"Success ({Value})" : $"Failure ({Exception})";
}

/// <summary>
/// TBD
/// Helper methods for creating <see cref="Result{T}"/> instances.
/// </summary>
public static class Result
{
/// <summary>
/// TBD
/// </summary>
/// <typeparam name="T">TBD</typeparam>
/// <param name="value">TBD</param>
/// <returns>TBD</returns>
public static Result<T> Success<T>(T value)
{
return new Result<T>(value);
}

/// <summary>
/// TBD
/// </summary>
/// <typeparam name="T">TBD</typeparam>
/// <param name="exception">TBD</param>
/// <returns>TBD</returns>

public static Result<T> Failure<T>(Exception exception)
{
return new Result<T>(exception);
}

/// <summary>
/// TBD
/// </summary>
/// <typeparam name="T">TBD</typeparam>
/// <param name="task">TBD</param>
/// <returns>TBD</returns>

public static Result<T> FromTask<T>(Task<T> task)
{
if(!task.IsCompleted)
Expand All @@ -143,7 +72,7 @@ public static Result<T> FromTask<T>(Task<T> task)
if(task.Exception is not null)
return new Result<T>(task.Exception);

if (task.IsCanceled && task.Exception is null)
if (task is { IsCanceled: true, Exception: null })
{
try
{
Expand All @@ -157,18 +86,12 @@ public static Result<T> FromTask<T>(Task<T> task)
throw new InvalidOperationException("Should never reach this line!");
}

if(task.IsFaulted && task.Exception is null)
if(task is { IsFaulted: true, Exception: null })
throw new InvalidOperationException("Should never happen! something is wrong with .NET Task code!");

return new Result<T>(task.Result);
}

/// <summary>
/// TBD
/// </summary>
/// <typeparam name="T">TBD</typeparam>
/// <param name="func">TBD</param>
/// <returns>TBD</returns>

public static Result<T> From<T>(Func<T> func)
{
try
Expand All @@ -181,8 +104,5 @@ public static Result<T> From<T>(Func<T> func)
return new Result<T>(e);
}
}



}
}
Loading