Skip to content

Commit

Permalink
ResultOrErrors deconstruct:
Browse files Browse the repository at this point in the history
Result<TValue> now has a new deconstruct (golang-style style): `Deconstruct(out TValue value, out List<IError> errors)`
Returning isSuccess and isFailed were redundant since we can infer success/failure from non-null values in `TValue value` and `List<IError> errors`.
Breaking change: anyone who was using Result<TValue> and was using the descontruct that returns bool/bool  (that person was already ignoring the TValue!) will probably get a build-break since the returned tuple won't be bool anymore

`Deconstruct(out bool isSuccess, out bool isFailed)` was moved from ResultBase to the Results class (the one without TValue) so it can still use that isSuccess/isFailed deconstruction.
  • Loading branch information
Drizin committed Aug 3, 2024
1 parent 0c3376f commit 5b300e6
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 34 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -509,12 +509,18 @@ var outcome = result switch
};
```

### Deconstruct Operators
### Golang-style Deconstruct Operators

```csharp
var (isSuccess, isFailed, value, errors) = Result.Fail<bool>("Failure 1");
// For Result<TValue> you get TValue and Errors
var (result, errors) = Result.Ok(1);
var (result, errors) = Result.Ok<int>(1);
// beware that on error you will get "result==0" (since TValue is a non-nullable int), so you probably want to check errors first!
var (result, errors) = Result.Fail<int>("fail");

var (isSuccess, isFailed, errors) = Result.Fail("Failure 1");
// For Result (without underlying value) you get bool isSuccess and Errors
var (isSuccess, errors) = Result.Fail("Failure 1");
var (isSuccess, errors) = Result.Ok();
```

### Logging
Expand Down
35 changes: 29 additions & 6 deletions src/FluentResults.Test/ResultWithValueTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -998,21 +998,21 @@ public void Implicit_conversion_Result_Value_is_converted_to_Result_object_with_
}

[Fact]
public void Can_deconstruct_generic_Ok_to_isSuccess_and_isFailed()
public void Can_deconstruct_generic_Ok_to_isSuccess_and_errors()
{
var (isSuccess, isFailed) = Result.Ok(true);
var (isSuccess, errors) = Result.Ok(true);

isSuccess.Should().Be(true);
isFailed.Should().Be(false);
errors.Should().BeNull();
}

[Fact]
public void Can_deconstruct_generic_Fail_to_isSuccess_and_isFailed()
public void Can_deconstruct_generic_Fail_to_isSuccess_and_errors()
{
var (isSuccess, isFailed) = Result.Fail<bool>("fail");
var (isSuccess, errors) = Result.Fail<bool>("fail");

isSuccess.Should().Be(false);
isFailed.Should().Be(true);
errors.Should().NotBeNullOrEmpty();
}

[Fact]
Expand All @@ -1035,6 +1035,29 @@ public void Can_deconstruct_generic_Fail_to_isSuccess_and_isFailed_and_value()
value.Should().Be(default);
}

[Fact]
public void Can_deconstruct_generic_Ok_to_value_with_errors()
{
var (value, errors) = Result.Ok(100);

value.Should().Be(100);
errors.Should().BeNull();
}

[Fact]
public void Can_deconstruct_generic_Fail_to_value_with_errors()
{
var error = new Error("fail");

var (value, errors) = Result.Fail<int>(error);

value.Should().Be(default);

errors.Count.Should().Be(1);
errors.FirstOrDefault().Should().Be(error);
}


[Fact]
public void Can_deconstruct_generic_Ok_to_isSuccess_and_isFailed_and_value_with_errors()
{
Expand Down
37 changes: 36 additions & 1 deletion src/FluentResults/Results/Result.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,31 @@ public async ValueTask<Result> Bind(Func<ValueTask<Result>> action)

return result;
}


/// <summary>
/// Deconstruct Result
/// </summary>
/// <param name="isSuccess"></param>
/// <param name="isFailed"></param>
public void Deconstruct(out bool isSuccess, out bool isFailed)
{
isSuccess = IsSuccess;
isFailed = IsFailed;
}

/// <summary>
/// Deconstruct Result
/// </summary>
/// <param name="isSuccess"></param>
/// <param name="isFailed"></param>
/// <param name="errors"></param>
public void Deconstruct(out bool isSuccess, out bool isFailed, out List<IError> errors)
{
isSuccess = IsSuccess;
isFailed = IsFailed;
errors = IsFailed ? Errors : default;
}

/// <summary>
/// Implict conversion from <see cref="Error"/> to a <see cref="Result"/>
/// </summary>
Expand Down Expand Up @@ -535,6 +559,17 @@ public static implicit operator Result<TValue>(List<Error> errors)
return Result.Fail(errors);
}

/// <summary>
/// Deconstruct Result
/// </summary>
/// <param name="value"></param>
/// <param name="errors"></param>
public void Deconstruct(out TValue value, out List<IError> errors)
{
value = IsSuccess ? Value : default;
errors = IsFailed ? Errors : default;
}

/// <summary>
/// Deconstruct Result
/// </summary>
Expand Down
24 changes: 0 additions & 24 deletions src/FluentResults/Results/ResultBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,30 +213,6 @@ public bool HasSuccess(Func<ISuccess, bool> predicate)
{
return ResultHelper.HasSuccess(Successes, predicate, out _);
}

/// <summary>
/// Deconstruct Result
/// </summary>
/// <param name="isSuccess"></param>
/// <param name="isFailed"></param>
public void Deconstruct(out bool isSuccess, out bool isFailed)
{
isSuccess = IsSuccess;
isFailed = IsFailed;
}

/// <summary>
/// Deconstruct Result
/// </summary>
/// <param name="isSuccess"></param>
/// <param name="isFailed"></param>
/// <param name="errors"></param>
public void Deconstruct(out bool isSuccess, out bool isFailed, out List<IError> errors)
{
isSuccess = IsSuccess;
isFailed = IsFailed;
errors = IsFailed ? Errors : default;
}
}

/// <summary>
Expand Down

0 comments on commit 5b300e6

Please sign in to comment.