Skip to content

Commit

Permalink
feat: implement GetFailureUnsafe on Result<T>
Browse files Browse the repository at this point in the history
  • Loading branch information
Tr00d committed Dec 6, 2023
1 parent f961208 commit 038c24e
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 47 deletions.
68 changes: 40 additions & 28 deletions Vonage.Common.Test/Monads/ResultTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,34 @@ namespace Vonage.Common.Test.Monads
{
public class ResultTest
{
[Fact]
public void BiMap_ShouldReturnFailure_GivenOperationThrowsException()
{
var expectedException = new Exception("Error");
CreateSuccess(5)
.BiMap(value =>
{
throw expectedException;
return value;
}, _ => _)
.Should()
.BeFailure(SystemFailure.FromException(expectedException));
}

[Fact]
public void BiMap_ShouldReturnFailure_GivenValueIsFailure() =>
CreateFailure()
.BiMap(Increment, f => ResultFailure.FromErrorMessage("New Failure"))
.Should()
.BeFailure(ResultFailure.FromErrorMessage("New Failure"));

[Fact]
public void BiMap_ShouldReturnSuccess_GivenValueIsSuccess() =>
CreateSuccess(5)
.BiMap(Increment, _ => _)
.Should()
.BeSuccess(6);

[Fact]
public void Bind_ShouldReturnFailure_GivenOperationThrowsException()
{
Expand Down Expand Up @@ -132,6 +160,18 @@ public void FromSuccess_ShouldReturnSuccess()
result.IsSuccess.Should().BeTrue();
}

[Fact]
public void GetFailureUnsafe_ShouldReturn_GivenFailure() =>
CreateFailure().GetFailureUnsafe().Should().Be(CreateResultFailure());

[Fact]
public void GetFailureUnsafe_ShouldThrowResultException_GivenFailure()
{
Action act = () => CreateSuccess(5).GetFailureUnsafe();
act.Should().Throw<InvalidOperationException>().Which.Message.Should()
.Be("Result is not in Failure state.");
}

[Fact]
public void GetHashCode_ShouldReturnValue_GivenFailure()
{
Expand Down Expand Up @@ -356,48 +396,20 @@ public void Map_ShouldReturnFailure_GivenOperationThrowsException()
.Should()
.BeFailure(SystemFailure.FromException(expectedException));
}

[Fact]
public void BiMap_ShouldReturnFailure_GivenOperationThrowsException()
{
var expectedException = new Exception("Error");
CreateSuccess(5)
.BiMap(value =>
{
throw expectedException;
return value;
}, _ => _)
.Should()
.BeFailure(SystemFailure.FromException(expectedException));
}

[Fact]
public void Map_ShouldReturnFailure_GivenValueIsFailure() =>
CreateFailure()
.Map(Increment)
.Should()
.BeFailure(CreateResultFailure());

[Fact]
public void BiMap_ShouldReturnFailure_GivenValueIsFailure() =>
CreateFailure()
.BiMap(Increment, f => ResultFailure.FromErrorMessage("New Failure"))
.Should()
.BeFailure( ResultFailure.FromErrorMessage("New Failure"));

[Fact]
public void Map_ShouldReturnSuccess_GivenValueIsSuccess() =>
CreateSuccess(5)
.Map(Increment)
.Should()
.BeSuccess(6);

[Fact]
public void BiMap_ShouldReturnSuccess_GivenValueIsSuccess() =>
CreateSuccess(5)
.BiMap(Increment, _ => _)
.Should()
.BeSuccess(6);

[Fact]
public async Task MapAsync_ShouldReturnFailure_GivenOperationThrowsException()
Expand Down
47 changes: 28 additions & 19 deletions Vonage.Common/Monads/Result.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,39 +47,39 @@ private Result(IResultFailure failure)
public bool IsSuccess => this.state == ResultState.Success;

/// <summary>
/// Monadic bind operation.
/// Projects from one value to another for each state of the Monad.
/// </summary>
/// <param name="bind">Bind operation.</param>
/// <typeparam name="TB">Return type.</typeparam>
/// <returns>Bound functor.</returns>
public Result<TB> Bind<TB>(Func<T, Result<TB>> bind)
/// <param name="successMap">Projection function for success state.</param>
/// <param name="failureMap">Projection function for failure state.</param>
/// <typeparam name="TB">Resulting functor value type.</typeparam>
/// <returns>Mapped functor.</returns>
public Result<TB> BiMap<TB>(Func<T, TB> successMap, Func<IResultFailure, IResultFailure> failureMap)
{
try
{
return this.IsFailure
? Result<TB>.FromFailure(this.failure)
: bind(this.success);
? Result<TB>.FromFailure(failureMap(this.failure))
: Result<TB>.FromSuccess(successMap(this.success));
}
catch (Exception exception)
{
return SystemFailure.FromException(exception).ToResult<TB>();
}
}

/// <summary>
/// Projects from one value to another for each state of the Monad.
/// Monadic bind operation.
/// </summary>
/// <param name="successMap">Projection function for success state.</param>
/// <param name="failureMap">Projection function for failure state.</param>
/// <typeparam name="TB">Resulting functor value type.</typeparam>
/// <returns>Mapped functor.</returns>
public Result<TB> BiMap<TB>(Func<T, TB> successMap, Func<IResultFailure, IResultFailure> failureMap)
/// <param name="bind">Bind operation.</param>
/// <typeparam name="TB">Return type.</typeparam>
/// <returns>Bound functor.</returns>
public Result<TB> Bind<TB>(Func<T, Result<TB>> bind)
{
try
{
return this.IsFailure
? Result<TB>.FromFailure(failureMap(this.failure))
: Result<TB>.FromSuccess(successMap(this.success));
? Result<TB>.FromFailure(this.failure)
: bind(this.success);
}
catch (Exception exception)
{
Expand All @@ -93,7 +93,7 @@ public Result<TB> BiMap<TB>(Func<T, TB> successMap, Func<IResultFailure, IResult
/// <param name="bind">Asynchronous bind operation.</param>
/// <typeparam name="TB">Return type.</typeparam>
/// <returns>Asynchronous bound functor.</returns>
public async Task<Result<TB>> BindAsync<TB>(Func<T, Task<Result<TB>>> bind)
public async Task<Result<TB>> BindAsync<TB>(Func<T, Task<Result<TB>>> bind)
{
try
{
Expand Down Expand Up @@ -124,6 +124,15 @@ public async Task<Result<TB>> BindAsync<TB>(Func<T, Task<Result<TB>>> bind)
/// <returns>Success Result.</returns>
public static Result<T> FromSuccess(T value) => new(value);

/// <summary>
/// Retrieves the Failure value. This method is unsafe and will throw an exception if in Success state.
/// </summary>
/// <returns>The Failure value if in Failure state.</returns>
/// <exception cref="InvalidOperationException">When Result is not in Failure state.</exception>
public IResultFailure GetFailureUnsafe() => this.IsFailure
? this.failure
: throw new InvalidOperationException("Result is not in Failure state.");

/// <inheritdoc />
public override int GetHashCode() => this.IsSuccess ? this.success.GetHashCode() : this.failure.GetHashCode();

Expand Down Expand Up @@ -268,8 +277,6 @@ public Result<TB> Merge<TB>(Result<T> other, Func<T, T, TB> merge) =>
? Result<TB>.FromSuccess(merge(this.success, other.success))
: Result<TB>.FromFailure(this.FetchFailure(other));

private IResultFailure FetchFailure(Result<T> other) => this.IsFailure ? this.failure : other.failure;

/// <summary>
/// Implicit operator from TA to Result of TA.
/// </summary>
Expand Down Expand Up @@ -305,6 +312,8 @@ private bool EqualsSuccess(Result<T> other) =>
? this.success.Equals(other.success)
: other.IsFailure;

private IResultFailure FetchFailure(Result<T> other) => this.IsFailure ? this.failure : other.failure;

/// <summary>
/// Enum representing the state of Result.
/// </summary>
Expand Down

0 comments on commit 038c24e

Please sign in to comment.