From ba590c7339461aa6734d35af62b93e9fe1be83d9 Mon Sep 17 00:00:00 2001 From: Maciej Tyski Date: Thu, 4 Aug 2022 20:16:05 +0200 Subject: [PATCH 1/2] feat: add lazy error evaluation overloads for Result.OkIf and FailIf methods Refers to #141 --- .../ResultWithoutValueTests.cs | 89 +++++++++++++++++++ src/FluentResults/Factories/Results.cs | 44 +++++++++ 2 files changed, 133 insertions(+) diff --git a/src/FluentResults.Test/ResultWithoutValueTests.cs b/src/FluentResults.Test/ResultWithoutValueTests.cs index 11c3fde..235a189 100644 --- a/src/FluentResults.Test/ResultWithoutValueTests.cs +++ b/src/FluentResults.Test/ResultWithoutValueTests.cs @@ -228,6 +228,50 @@ public void FailIf_FailedConditionIsFalseAndWithObjectErrorMessage_CreateFailedR result.IsFailed.Should().BeFalse(); } + [Fact] + public void FailIf_FailedConditionIsFalseAndWithObjectErrorFactory_CreateSuccessResult() + { + var result = Result.FailIf(false, LazyError); + + result.IsFailed.Should().BeFalse(); + + Error LazyError() + { + throw new Exception("This should not be thrown!"); + } + } + + [Fact] + public void FailIf_FailedConditionIsFalseAndWithStringErrorMessageFactory_CreateSuccessResult() + { + var result = Result.FailIf(false, LazyError); + + result.IsFailed.Should().BeFalse(); + + string LazyError() + { + throw new Exception("This should not be thrown!"); + } + } + + [Fact] + public void FailIf_FailedConditionIsTrueAndWithObjectErrorFactory_CreateFailedResult() + { + var result = Result.FailIf(true, () => "Error message"); + + result.IsFailed.Should().BeTrue(); + result.Errors.Single().Message.Should().Be("Error message"); + } + + [Fact] + public void FailIf_FailedConditionIsTrueAndWithStringErrorMessageFactory_CreateFailedResult() + { + var result = Result.FailIf(true, () => new Error("Error message")); + + result.IsFailed.Should().BeTrue(); + result.Errors.Single().Message.Should().Be("Error message"); + } + [Fact] public void OkIf_SuccessConditionIsTrueAndWithStringErrorMessage_CreateFailedResult() { @@ -246,6 +290,32 @@ public void OkIf_SuccessConditionIsTrueAndWithObjectErrorMessage_CreateFailedRes result.IsSuccess.Should().BeTrue(); } + [Fact] + public void OkIf_SuccessConditionIsTrueAndWithStringErrorMessageFactory_CreateSuccessResult() + { + var result = Result.OkIf(true, LazyError); + + result.IsSuccess.Should().BeTrue(); + + string LazyError() + { + throw new Exception("This should not be thrown!"); + } + } + + [Fact] + public void OkIf_SuccessConditionIsTrueAnWithObjectErrorMessageFactory_CreateSuccessResult() + { + var result = Result.OkIf(true, LazyError); + + result.IsSuccess.Should().BeTrue(); + + Error LazyError() + { + throw new Exception("This should not be thrown!"); + } + } + [Fact] public void OkIf_SuccessConditionIsFalseAndWithStringErrorMessage_CreateFailedResult() { @@ -266,6 +336,25 @@ public void OkIf_SuccessConditionIsFalseAndWithObjectErrorMessage_CreateFailedRe result.Errors.Single().Message.Should().Be("Error message"); } + [Fact] + public void OkIf_SuccessConditionIsFalseAndWithStringErrorMessageFactory_CreateFailedResult() + { + const string errorMessage = "Error message"; + var result = Result.OkIf(false, () => errorMessage); + + result.IsFailed.Should().BeTrue(); + result.Errors.Single().Message.Should().Be(errorMessage); + } + + [Fact] + public void OkIf_SuccessConditionIsFalseAndWithObjectErrorMessageFactory_CreateFailedResult() + { + const string errorMessage = "Error message"; + var result = Result.OkIf(false, () => new Error(errorMessage)); + + result.IsFailed.Should().BeTrue(); + result.Errors.Single().Message.Should().Be(errorMessage); + } [Fact] public void Try_execute_successfully_action_return_success_result() diff --git a/src/FluentResults/Factories/Results.cs b/src/FluentResults/Factories/Results.cs index 9639bc2..ae8d0a9 100644 --- a/src/FluentResults/Factories/Results.cs +++ b/src/FluentResults/Factories/Results.cs @@ -168,6 +168,28 @@ public static Result OkIf(bool isSuccess, string error) return isSuccess ? Ok() : Fail(error); } + /// + /// Create a success/failed result depending on the parameter isSuccess + /// + /// + /// Error is lazily evaluated. + /// + public static Result OkIf(bool isSuccess, Func errorFactory) + { + return isSuccess ? Ok() : Fail(errorFactory.Invoke()); + } + + /// + /// Create a success/failed result depending on the parameter isSuccess + /// + /// + /// Error is lazily evaluated. + /// + public static Result OkIf(bool isSuccess, Func errorMessageFactory) + { + return isSuccess ? Ok() : Fail(errorMessageFactory.Invoke()); + } + /// /// Create a success/failed result depending on the parameter isFailure /// @@ -184,6 +206,28 @@ public static Result FailIf(bool isFailure, string error) return isFailure ? Fail(error) : Ok(); } + /// + /// Create a success/failed result depending on the parameter isFailure + /// + /// + /// Error is lazily evaluated. + /// + public static Result FailIf(bool isFailure, Func errorFactory) + { + return isFailure ? Fail(errorFactory.Invoke()) : Ok(); + } + + /// + /// Create a success/failed result depending on the parameter isFailure + /// + /// + /// Error is lazily evaluated. + /// + public static Result FailIf(bool isFailure, Func errorMessageFactory) + { + return isFailure ? Fail(errorMessageFactory.Invoke()) : Ok(); + } + /// /// Executes the action. If an exception is thrown within the action then this exception is transformed via the catchHandler to an Error object /// From 27151a609c61cd7fc889e9719aa1d28858980190 Mon Sep 17 00:00:00 2001 From: Maciej Tyski Date: Thu, 4 Aug 2022 20:19:22 +0200 Subject: [PATCH 2/2] docs: document lazy evaluation of errors Refers to #141 --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 69ea863..e61bc62 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,20 @@ With the methods ```FailIf()``` and ```OkIf()``` you can also write in a more re var result = Result.FailIf(string.IsNullOrEmpty(firstName), "First Name is empty"); ``` +If an error instance should be lazily initialized, overloads accepting ```Func``` or ```Func``` can be used to that effect: + +```csharp +var list = Enumerable.Range(1, 9).ToList(); + +var result = Result.FailIf( + list.Any(IsDivisibleByTen), + () => new Error()$"Item {list.First(IsDivisibleByTen)}" should not be on the list)); + +bool IsDivisibleByTen(int i) => i % 10 == 0; + +// rest of the code +``` + ### Try In some scenarios you want to execute an action. If this action throws an exception then the exception should be catched and transformed to a result object.