diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cb6bc4..8ecd515 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,57 @@ # Changelog -All notable changes to this project will be documented in this file. +All notable changes to this project are documented in this file. ## [1.10.0] - 2024-02-14 + ### Added -- Forbidden Error Type + +- `ErrorType.Forbidden` - README to NuGet package ## [1.9.0] - 2024-01-06 + +### Added + +- `ToErrorOr` + +## [2.0.0] - 2024-03-26 + ### Added -- ToErrorOr -### Fixed -- Added Missing tests +- `FailIf` + +```csharp +public ErrorOr FailIf(Func onValue, Error error) +``` + +```csharp +ErrorOr errorOr = 1; +errorOr.FailIf(x => x > 0, Error.Failure()); +``` + +### Breaking Changes + +- `Then` that receives an action is now called `ThenDo` + +```diff +-public ErrorOr Then(Action action) ++public ErrorOr ThenDo(Action action) +``` + +```diff +-public static async Task> Then(this Task> errorOr, Action action) ++public static async Task> ThenDo(this Task> errorOr, Action action) +``` + +- `ThenAsync` that receives an action is now called `ThenDoAsync` + +```diff +-public async Task> ThenAsync(Func action) ++public async Task> ThenDoAsync(Func action) +``` + +```diff +-public static async Task> ThenAsync(this Task> errorOr, Func action) ++public static async Task> ThenDoAsync(this Task> errorOr, Func action) +``` diff --git a/Directory.Build.props b/Directory.Build.props index 1b7f76e..5506a74 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,21 +1,21 @@ - - - - - - 10.0 - true - enable - enable - True - true - - - - - all - runtime; build; native; contentfiles; analyzers - - - - + + + + + + 10.0 + true + enable + enable + True + true + + + + + all + runtime; build; native; contentfiles; analyzers + + + + diff --git a/README.md b/README.md index f58c29a..326c22b 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,12 @@ - [Give it a star ⭐!](#give-it-a-star-) - [Getting Started 🏃](#getting-started-) - [Replace throwing exceptions with `ErrorOr`](#replace-throwing-exceptions-with-errorort) - - [Return Multiple Errors When Needed](#return-multiple-errors-when-needed) + - [Support For Multiple Errors](#support-for-multiple-errors) + - [Various Functional Methods and Extension Methods](#various-functional-methods-and-extension-methods) + - [Real world example](#real-world-example) + - [Simple Example with intermediate steps](#simple-example-with-intermediate-steps) + - [No Failure](#no-failure) + - [Failure](#failure) - [Creating an `ErrorOr` instance](#creating-an-erroror-instance) - [Using implicit conversion](#using-implicit-conversion) - [Using The `ErrorOrFactory`](#using-the-errororfactory) @@ -44,11 +49,13 @@ - [`Then`](#then) - [`Then`](#then-1) - [`ThenAsync`](#thenasync) - - [Mixing `Then` and `ThenAsync`](#mixing-then-and-thenasync) + - [`ThenDo` and `ThenDoAsync`](#thendo-and-thendoasync) + - [Mixing `Then`, `ThenDo`, `ThenAsync`, `ThenDoAsync`](#mixing-then-thendo-thenasync-thendoasync) + - [`FailIf`](#failif) - [`Else`](#else) - [`Else`](#else-1) - [`ElseAsync`](#elseasync) -- [Mixing Features (`Then`, `Else`, `Switch`, `Match`)](#mixing-features-then-else-switch-match) +- [Mixing Features (`Then`, `FailIf`, `Else`, `Switch`, `Match`)](#mixing-features-then-failif-else-switch-match) - [Error Types](#error-types) - [Built in error types](#built-in-error-types) - [Custom error types](#custom-error-types) @@ -127,7 +134,7 @@ Divide(4, 2) onFirstError: error => Console.WriteLine(error.Description)); ``` -## Return Multiple Errors When Needed +## Support For Multiple Errors Internally, the `ErrorOr` object has a list of `Error`s, so if you have multiple errors, you don't need to compromise and have only the first one. @@ -163,6 +170,62 @@ public class User(string _name) } ``` +## Various Functional Methods and Extension Methods + +The `ErrorOr` object has a variety of methods that allow you to work with it in a functional way. + +This allows you to chain methods together, and handle the result in a clean and concise way. + +### Real world example + +```cs +return await _userRepository.GetByIdAsync(id) + .Then(user => user.IncrementAge() + .Then(success => user) + .Else(errors => Error.Unexpected("Not expected to fail"))) + .FailIf(user => !user.IsOverAge(18), UserErrors.UnderAge) + .ThenDo(user => _logger.LogInformation($"User {user.Id} incremented age to {user.Age}")) + .ThenAsync(user => _userRepository.UpdateAsync(user)) + .Match( + _ => NoContent(), + errors => errors.ToActionResult()); +``` + +### Simple Example with intermediate steps + +#### No Failure + +```cs +ErrorOr foo = await "2".ToErrorOr() + .Then(int.Parse) // 2 + .FailIf(val => val > 2, Error.Validation(description: $"{val} is too big") // 2 + .ThenDoAsync(Task.Delay) // Sleep for 2 milliseconds + .ThenDo(val => Console.WriteLine($"Finished waiting {val} milliseconds.")) // Finished waiting 2 milliseconds. + .ThenAsync(val => Task.FromResult(val * 2)) // 4 + .Then(val => $"The result is {val}") // "The result is 4" + .Else(errors => Error.Unexpected(description: "Yikes")) // "The result is 4" + .MatchFirst( + value => value, // "The result is 4" + firstError => $"An error occurred: {firstError.Description}"); +``` + +#### Failure + +```cs +ErrorOr foo = await "5".ToErrorOr() + .Then(int.Parse) // 5 + .FailIf(val => val > 2, Error.Validation(description: $"{val} is too big") // Error.Validation() + .ThenDoAsync(Task.Delay) // Error.Validation() + .ThenDo(val => Console.WriteLine($"Finished waiting {val} milliseconds.")) // Error.Validation() + .ThenAsync(val => Task.FromResult(val * 2)) // Error.Validation() + .Then(val => $"The result is {val}") // Error.Validation() + .Else(errors => Error.Unexpected(description: "Yikes")) // Error.Unexpected() + .MatchFirst( + value => value, + firstError => $"An error occurred: {firstError.Description}"); // An error occurred: 5 is too big +``` + + # Creating an `ErrorOr` instance ## Using implicit conversion @@ -388,7 +451,7 @@ await result.SwitchFirstAsync( ### `Then` -`Then` receives an action or a function, and invokes it only if the result is not an error. +`Then` receives a function, and invokes it only if the result is not an error. ```cs ErrorOr foo = result @@ -417,26 +480,65 @@ ErrorOr foo = result ### `ThenAsync` -`ThenAsync` receives an asynchronous action or function, and invokes it only if the result is not an error. +`ThenAsync` receives an asynchronous function, and invokes it only if the result is not an error. ```cs ErrorOr foo = await result - .ThenAsync(val => Task.Delay(val)) - .ThenAsync(val => Task.FromResult($"The result is {val}")); + .ThenAsync(val => DoSomethingAsync(val)) + .ThenAsync(val => DoSomethingElseAsync($"The result is {val}")); ``` -### Mixing `Then` and `ThenAsync` +### `ThenDo` and `ThenDoAsync` -You can mix `Then` and `ThenAsync` methods together. +`ThenDo` and `ThenDoAsync` are similar to `Then` and `ThenAsync`, but instead of invoking a function that returns a value, they invoke an action. + +```cs +ErrorOr foo = result + .ThenDo(val => Console.WriteLine(val)) + .ThenDo(val => Console.WriteLine($"The result is {val}")); +``` ```cs ErrorOr foo = await result - .ThenAsync(val => Task.Delay(val)) - .Then(val => Console.WriteLine($"Finsihed waiting {val} seconds.")) + .ThenDoAsync(val => Task.Delay(val)) + .ThenDo(val => Console.WriteLine($"Finsihed waiting {val} seconds.")) + .ThenDoAsync(val => Task.FromResult(val * 2)) + .ThenDo(val => $"The result is {val}"); +``` + +### Mixing `Then`, `ThenDo`, `ThenAsync`, `ThenDoAsync` + +You can mix and match `Then`, `ThenDo`, `ThenAsync`, `ThenDoAsync` methods. + +```cs +ErrorOr foo = await result + .ThenDoAsync(val => Task.Delay(val)) + .Then(val => val * 2) + .ThenAsync(val => DoSomethingAsync(val)) + .ThenDo(val => Console.WriteLine($"Finsihed waiting {val} seconds.")) .ThenAsync(val => Task.FromResult(val * 2)) .Then(val => $"The result is {val}"); ``` +## `FailIf` + +`FailIf` receives a predicate and an error. If the predicate is true, `FailIf` will return the error. Otherwise, it will return the value of the result. + +```cs +ErrorOr foo = result + .FailIf(val => val > 2, Error.Validation(description: $"{val} is too big")); +``` + +Once an error is returned, the chain will break and the error will be returned. + +```cs +var result = "2".ToErrorOr() + .Then(int.Parse) // 2 + .FailIf(val => val > 1, Error.Validation(description: $"{val} is too big") // validation error + .Then(num => num * 2) // this function will not be invoked + .Then(num => num * 2) // this function will not be invoked +``` + ## `Else` `Else` receives a value or a function. If the result is an error, `Else` will return the value or invoke the function. Otherwise, it will return the value of the result. @@ -465,14 +567,15 @@ ErrorOr foo = await result .ElseAsync(errors => Task.FromResult($"{errors.Count} errors occurred.")); ``` -# Mixing Features (`Then`, `Else`, `Switch`, `Match`) +# Mixing Features (`Then`, `FailIf`, `Else`, `Switch`, `Match`) -You can mix `Then`, `Else`, `Switch` and `Match` methods together. +You can mix `Then`, `FailIf`, `Else`, `Switch` and `Match` methods together. ```cs ErrorOr foo = await result - .ThenAsync(val => Task.Delay(val)) - .Then(val => Console.WriteLine($"Finsihed waiting {val} seconds.")) + .ThenDoAsync(val => Task.Delay(val)) + .FailIf(val => val > 2, Error.Validation(description: $"{val} is too big")) + .ThenDo(val => Console.WriteLine($"Finished waiting {val} seconds.")) .ThenAsync(val => Task.FromResult(val * 2)) .Then(val => $"The result is {val}") .Else(errors => Error.Unexpected()) diff --git a/src/ErrorOr.Else.cs b/src/ErrorOr.Else.cs new file mode 100644 index 0000000..6e706e9 --- /dev/null +++ b/src/ErrorOr.Else.cs @@ -0,0 +1,157 @@ +namespace ErrorOr; + +/// +/// A discriminated union of errors or a value. +/// +public readonly partial record struct ErrorOr : IErrorOr +{ + /// + /// If the state is error, the provided function is executed and its result is returned. + /// + /// The function to execute if the state is error. + /// The result from calling if state is error; otherwise the original . + public ErrorOr Else(Func, Error> onError) + { + if (!IsError) + { + return Value; + } + + return onError(Errors); + } + + /// + /// If the state is error, the provided function is executed and its result is returned. + /// + /// The function to execute if the state is error. + /// The result from calling if state is error; otherwise the original . + public ErrorOr Else(Func, List> onError) + { + if (!IsError) + { + return Value; + } + + return onError(Errors); + } + + /// + /// If the state is error, the provided is returned. + /// + /// The error to return. + /// The given . + public ErrorOr Else(Error error) + { + if (!IsError) + { + return Value; + } + + return error; + } + + /// + /// If the state is error, the provided function is executed and its result is returned. + /// + /// The function to execute if the state is error. + /// The result from calling if state is error; otherwise the original . + public ErrorOr Else(Func, TValue> onError) + { + if (!IsError) + { + return Value; + } + + return onError(Errors); + } + + /// + /// If the state is error, the provided function is executed and its result is returned. + /// + /// The value to return if the state is error. + /// The result from calling if state is error; otherwise the original . + public ErrorOr Else(TValue onError) + { + if (!IsError) + { + return Value; + } + + return onError; + } + + /// + /// If the state is error, the provided function is executed asynchronously and its result is returned. + /// + /// The function to execute if the state is error. + /// The result from calling if state is error; otherwise the original . + public async Task> ElseAsync(Func, Task> onError) + { + if (!IsError) + { + return Value; + } + + return await onError(Errors).ConfigureAwait(false); + } + + /// + /// If the state is error, the provided function is executed asynchronously and its result is returned. + /// + /// The function to execute if the state is error. + /// The result from calling if state is error; otherwise the original . + public async Task> ElseAsync(Func, Task> onError) + { + if (!IsError) + { + return Value; + } + + return await onError(Errors).ConfigureAwait(false); + } + + /// + /// If the state is error, the provided function is executed asynchronously and its result is returned. + /// + /// The function to execute if the state is error. + /// The result from calling if state is error; otherwise the original . + public async Task> ElseAsync(Func, Task>> onError) + { + if (!IsError) + { + return Value; + } + + return await onError(Errors).ConfigureAwait(false); + } + + /// + /// If the state is error, the provided is awaited and returned. + /// + /// The error to return if the state is error. + /// The result from awaiting the given . + public async Task> ElseAsync(Task error) + { + if (!IsError) + { + return Value; + } + + return await error.ConfigureAwait(false); + } + + /// + /// If the state is error, the provided function is executed asynchronously and its result is returned. + /// + /// The function to execute if the state is error. + /// The result from calling if state is error; otherwise the original . + public async Task> ElseAsync(Task onError) + { + if (!IsError) + { + return Value; + } + + return await onError.ConfigureAwait(false); + } +} diff --git a/src/ErrorOr.FailIf.cs b/src/ErrorOr.FailIf.cs new file mode 100644 index 0000000..5e51d79 --- /dev/null +++ b/src/ErrorOr.FailIf.cs @@ -0,0 +1,23 @@ +namespace ErrorOr; + +/// +/// A discriminated union of errors or a value. +/// +public readonly partial record struct ErrorOr : IErrorOr +{ + /// + /// If the state is error, the provided function is executed and its result is returned. + /// + /// The function to execute if the state is value. + /// The to return if the given function returned true.. + /// The given if returns true; otherwise, the original instance. + public ErrorOr FailIf(Func onValue, Error error) + { + if (IsError) + { + return this; + } + + return onValue(Value) ? error : this; + } +} diff --git a/src/ErrorOr.ImplicitConverters.cs b/src/ErrorOr.ImplicitConverters.cs new file mode 100644 index 0000000..d2e155e --- /dev/null +++ b/src/ErrorOr.ImplicitConverters.cs @@ -0,0 +1,39 @@ +namespace ErrorOr; + +/// +/// A discriminated union of errors or a value. +/// +public readonly partial record struct ErrorOr : IErrorOr +{ + /// + /// Creates an from a value. + /// + public static implicit operator ErrorOr(TValue value) + { + return new ErrorOr(value); + } + + /// + /// Creates an from an error. + /// + public static implicit operator ErrorOr(Error error) + { + return new ErrorOr(error); + } + + /// + /// Creates an from a list of errors. + /// + public static implicit operator ErrorOr(List errors) + { + return new ErrorOr(errors); + } + + /// + /// Creates an from a list of errors. + /// + public static implicit operator ErrorOr(Error[] errors) + { + return new ErrorOr(errors.ToList()); + } +} diff --git a/src/ErrorOr.Match.cs b/src/ErrorOr.Match.cs new file mode 100644 index 0000000..db76d48 --- /dev/null +++ b/src/ErrorOr.Match.cs @@ -0,0 +1,83 @@ +namespace ErrorOr; + +/// +/// A discriminated union of errors or a value. +/// +public readonly partial record struct ErrorOr : IErrorOr +{ + /// + /// Executes the appropriate function based on the state of the . + /// If the state is a value, the provided function is executed and its result is returned. + /// If the state is an error, the provided function is executed and its result is returned. + /// + /// The type of the result. + /// The function to execute if the state is a value. + /// The function to execute if the state is an error. + /// The result of the executed function. + public TNextValue Match(Func onValue, Func, TNextValue> onError) + { + if (IsError) + { + return onError(Errors); + } + + return onValue(Value); + } + + /// + /// Asynchronously executes the appropriate function based on the state of the . + /// If the state is a value, the provided function is executed asynchronously and its result is returned. + /// If the state is an error, the provided function is executed asynchronously and its result is returned. + /// + /// The type of the result. + /// The asynchronous function to execute if the state is a value. + /// The asynchronous function to execute if the state is an error. + /// A task representing the asynchronous operation that yields the result of the executed function. + public async Task MatchAsync(Func> onValue, Func, Task> onError) + { + if (IsError) + { + return await onError(Errors).ConfigureAwait(false); + } + + return await onValue(Value).ConfigureAwait(false); + } + + /// + /// Executes the appropriate function based on the state of the . + /// If the state is a value, the provided function is executed and its result is returned. + /// If the state is an error, the provided function is executed using the first error, and its result is returned. + /// + /// The type of the result. + /// The function to execute if the state is a value. + /// The function to execute with the first error if the state is an error. + /// The result of the executed function. + public TNextValue MatchFirst(Func onValue, Func onFirstError) + { + if (IsError) + { + return onFirstError(FirstError); + } + + return onValue(Value); + } + + /// + /// Asynchronously executes the appropriate function based on the state of the . + /// If the state is a value, the provided function is executed asynchronously and its result is returned. + /// If the state is an error, the provided function is executed asynchronously using the first error, and its result is returned. + /// + /// The type of the result. + /// The asynchronous function to execute if the state is a value. + /// The asynchronous function to execute with the first error if the state is an error. + /// A task representing the asynchronous operation that yields the result of the executed function. + public async Task MatchFirstAsync(Func> onValue, Func> onFirstError) + { + if (IsError) + { + return await onFirstError(FirstError).ConfigureAwait(false); + } + + return await onValue(Value).ConfigureAwait(false); + } +} diff --git a/src/ErrorOr.Switch.cs b/src/ErrorOr.Switch.cs new file mode 100644 index 0000000..1fabdbc --- /dev/null +++ b/src/ErrorOr.Switch.cs @@ -0,0 +1,81 @@ +namespace ErrorOr; + +/// +/// A discriminated union of errors or a value. +/// +public readonly partial record struct ErrorOr : IErrorOr +{ + /// + /// Executes the appropriate action based on the state of the . + /// If the state is an error, the provided action is executed. + /// If the state is a value, the provided action is executed. + /// + /// The action to execute if the state is a value. + /// The action to execute if the state is an error. + public void Switch(Action onValue, Action> onError) + { + if (IsError) + { + onError(Errors); + return; + } + + onValue(Value); + } + + /// + /// Asynchronously executes the appropriate action based on the state of the . + /// If the state is an error, the provided action is executed asynchronously. + /// If the state is a value, the provided action is executed asynchronously. + /// + /// The asynchronous action to execute if the state is a value. + /// The asynchronous action to execute if the state is an error. + /// A task representing the asynchronous operation. + public async Task SwitchAsync(Func onValue, Func, Task> onError) + { + if (IsError) + { + await onError(Errors).ConfigureAwait(false); + return; + } + + await onValue(Value).ConfigureAwait(false); + } + + /// + /// Executes the appropriate action based on the state of the . + /// If the state is an error, the provided action is executed using the first error as input. + /// If the state is a value, the provided action is executed. + /// + /// The action to execute if the state is a value. + /// The action to execute with the first error if the state is an error. + public void SwitchFirst(Action onValue, Action onFirstError) + { + if (IsError) + { + onFirstError(FirstError); + return; + } + + onValue(Value); + } + + /// + /// Asynchronously executes the appropriate action based on the state of the . + /// If the state is an error, the provided action is executed asynchronously using the first error as input. + /// If the state is a value, the provided action is executed asynchronously. + /// + /// The asynchronous action to execute if the state is a value. + /// The asynchronous action to execute with the first error if the state is an error. + /// A task representing the asynchronous operation. + public async Task SwitchFirstAsync(Func onValue, Func onFirstError) + { + if (IsError) + { + await onFirstError(FirstError).ConfigureAwait(false); + return; + } + + await onValue(Value).ConfigureAwait(false); + } +} diff --git a/src/ErrorOr.Then.cs b/src/ErrorOr.Then.cs new file mode 100644 index 0000000..5f24448 --- /dev/null +++ b/src/ErrorOr.Then.cs @@ -0,0 +1,105 @@ +namespace ErrorOr; + +/// +/// A discriminated union of errors or a value. +/// +public readonly partial record struct ErrorOr : IErrorOr +{ + /// + /// If the state is a value, the provided function is executed and its result is returned. + /// + /// The type of the result. + /// The function to execute if the state is a value. + /// The result from calling if state is value; otherwise the original . + public ErrorOr Then(Func> onValue) + { + if (IsError) + { + return Errors; + } + + return onValue(Value); + } + + /// + /// If the state is a value, the provided is invoked. + /// + /// The action to execute if the state is a value. + /// The original instance. + public ErrorOr ThenDo(Action action) + { + if (IsError) + { + return Errors; + } + + action(Value); + + return this; + } + + /// + /// If the state is a value, the provided function is executed and its result is returned. + /// + /// The type of the result. + /// The function to execute if the state is a value. + /// The result from calling if state is value; otherwise the original . + public ErrorOr Then(Func onValue) + { + if (IsError) + { + return Errors; + } + + return onValue(Value); + } + + /// + /// If the state is a value, the provided function is executed asynchronously and its result is returned. + /// + /// The type of the result. + /// The function to execute if the state is a value. + /// The result from calling if state is value; otherwise the original . + public async Task> ThenAsync(Func>> onValue) + { + if (IsError) + { + return Errors; + } + + return await onValue(Value).ConfigureAwait(false); + } + + /// + /// If the state is a value, the provided is invoked asynchronously. + /// + /// The action to execute if the state is a value. + /// The original instance. + public async Task> ThenDoAsync(Func action) + { + if (IsError) + { + return Errors; + } + + await action(Value).ConfigureAwait(false); + + return this; + } + + /// + /// If the state is a value, the provided function is executed asynchronously and its result is returned. + /// + /// The type of the result. + /// The function to execute if the state is a value. + /// The result from calling if state is value; otherwise the original . + public async Task> ThenAsync(Func> onValue) + { + if (IsError) + { + return Errors; + } + + return await onValue(Value).ConfigureAwait(false); + } +} diff --git a/src/ErrorOr.ThenExtensions.cs b/src/ErrorOr.ThenExtensions.cs index 4a76310..a2c45ee 100644 --- a/src/ErrorOr.ThenExtensions.cs +++ b/src/ErrorOr.ThenExtensions.cs @@ -39,11 +39,11 @@ public static async Task> Then(this Task /// The instance. /// The action to execute if the state is a value. /// The original . - public static async Task> Then(this Task> errorOr, Action action) + public static async Task> ThenDo(this Task> errorOr, Action action) { var result = await errorOr.ConfigureAwait(false); - return result.Then(action); + return result.ThenDo(action); } /// @@ -83,10 +83,10 @@ public static async Task> ThenAsync(this /// The instance. /// The action to execute if the state is a value. /// The original . - public static async Task> ThenAsync(this Task> errorOr, Func action) + public static async Task> ThenDoAsync(this Task> errorOr, Func action) { var result = await errorOr.ConfigureAwait(false); - return await result.ThenAsync(action).ConfigureAwait(false); + return await result.ThenDoAsync(action).ConfigureAwait(false); } } diff --git a/src/ErrorOr.cs b/src/ErrorOr.cs index d2b764f..64d32ec 100644 --- a/src/ErrorOr.cs +++ b/src/ErrorOr.cs @@ -3,7 +3,7 @@ namespace ErrorOr; /// /// A discriminated union of errors or a value. /// -public readonly record struct ErrorOr : IErrorOr +public readonly partial record struct ErrorOr : IErrorOr { private readonly TValue? _value = default; private readonly List? _errors = null; @@ -77,434 +77,4 @@ private ErrorOr(TValue value) _value = value; IsError = false; } - - /// - /// Creates an from a value. - /// - public static implicit operator ErrorOr(TValue value) - { - return new ErrorOr(value); - } - - /// - /// Creates an from an error. - /// - public static implicit operator ErrorOr(Error error) - { - return new ErrorOr(error); - } - - /// - /// Creates an from a list of errors. - /// - public static implicit operator ErrorOr(List errors) - { - return new ErrorOr(errors); - } - - /// - /// Creates an from a list of errors. - /// - public static implicit operator ErrorOr(Error[] errors) - { - return new ErrorOr(errors.ToList()); - } - - /// - /// Executes the appropriate action based on the state of the . - /// If the state is an error, the provided action is executed. - /// If the state is a value, the provided action is executed. - /// - /// The action to execute if the state is a value. - /// The action to execute if the state is an error. - public void Switch(Action onValue, Action> onError) - { - if (IsError) - { - onError(Errors); - return; - } - - onValue(Value); - } - - /// - /// Asynchronously executes the appropriate action based on the state of the . - /// If the state is an error, the provided action is executed asynchronously. - /// If the state is a value, the provided action is executed asynchronously. - /// - /// The asynchronous action to execute if the state is a value. - /// The asynchronous action to execute if the state is an error. - /// A task representing the asynchronous operation. - public async Task SwitchAsync(Func onValue, Func, Task> onError) - { - if (IsError) - { - await onError(Errors).ConfigureAwait(false); - return; - } - - await onValue(Value).ConfigureAwait(false); - } - - /// - /// Executes the appropriate action based on the state of the . - /// If the state is an error, the provided action is executed using the first error as input. - /// If the state is a value, the provided action is executed. - /// - /// The action to execute if the state is a value. - /// The action to execute with the first error if the state is an error. - public void SwitchFirst(Action onValue, Action onFirstError) - { - if (IsError) - { - onFirstError(FirstError); - return; - } - - onValue(Value); - } - - /// - /// Asynchronously executes the appropriate action based on the state of the . - /// If the state is an error, the provided action is executed asynchronously using the first error as input. - /// If the state is a value, the provided action is executed asynchronously. - /// - /// The asynchronous action to execute if the state is a value. - /// The asynchronous action to execute with the first error if the state is an error. - /// A task representing the asynchronous operation. - public async Task SwitchFirstAsync(Func onValue, Func onFirstError) - { - if (IsError) - { - await onFirstError(FirstError).ConfigureAwait(false); - return; - } - - await onValue(Value).ConfigureAwait(false); - } - - /// - /// Executes the appropriate function based on the state of the . - /// If the state is a value, the provided function is executed and its result is returned. - /// If the state is an error, the provided function is executed and its result is returned. - /// - /// The type of the result. - /// The function to execute if the state is a value. - /// The function to execute if the state is an error. - /// The result of the executed function. - public TNextValue Match(Func onValue, Func, TNextValue> onError) - { - if (IsError) - { - return onError(Errors); - } - - return onValue(Value); - } - - /// - /// Asynchronously executes the appropriate function based on the state of the . - /// If the state is a value, the provided function is executed asynchronously and its result is returned. - /// If the state is an error, the provided function is executed asynchronously and its result is returned. - /// - /// The type of the result. - /// The asynchronous function to execute if the state is a value. - /// The asynchronous function to execute if the state is an error. - /// A task representing the asynchronous operation that yields the result of the executed function. - public async Task MatchAsync(Func> onValue, Func, Task> onError) - { - if (IsError) - { - return await onError(Errors).ConfigureAwait(false); - } - - return await onValue(Value).ConfigureAwait(false); - } - - /// - /// Executes the appropriate function based on the state of the . - /// If the state is a value, the provided function is executed and its result is returned. - /// If the state is an error, the provided function is executed using the first error, and its result is returned. - /// - /// The type of the result. - /// The function to execute if the state is a value. - /// The function to execute with the first error if the state is an error. - /// The result of the executed function. - public TNextValue MatchFirst(Func onValue, Func onFirstError) - { - if (IsError) - { - return onFirstError(FirstError); - } - - return onValue(Value); - } - - /// - /// Asynchronously executes the appropriate function based on the state of the . - /// If the state is a value, the provided function is executed asynchronously and its result is returned. - /// If the state is an error, the provided function is executed asynchronously using the first error, and its result is returned. - /// - /// The type of the result. - /// The asynchronous function to execute if the state is a value. - /// The asynchronous function to execute with the first error if the state is an error. - /// A task representing the asynchronous operation that yields the result of the executed function. - public async Task MatchFirstAsync(Func> onValue, Func> onFirstError) - { - if (IsError) - { - return await onFirstError(FirstError).ConfigureAwait(false); - } - - return await onValue(Value).ConfigureAwait(false); - } - - /// - /// If the state is a value, the provided function is executed and its result is returned. - /// - /// The type of the result. - /// The function to execute if the state is a value. - /// The result from calling if state is value; otherwise the original . - public ErrorOr Then(Func> onValue) - { - if (IsError) - { - return Errors; - } - - return onValue(Value); - } - - /// - /// If the state is a value, the provided is invoked. - /// - /// The action to execute if the state is a value. - /// The original instance. - public ErrorOr Then(Action action) - { - if (IsError) - { - return Errors; - } - - action(Value); - - return this; - } - - /// - /// If the state is a value, the provided function is executed and its result is returned. - /// - /// The type of the result. - /// The function to execute if the state is a value. - /// The result from calling if state is value; otherwise the original . - public ErrorOr Then(Func onValue) - { - if (IsError) - { - return Errors; - } - - return onValue(Value); - } - - /// - /// If the state is a value, the provided function is executed asynchronously and its result is returned. - /// - /// The type of the result. - /// The function to execute if the state is a value. - /// The result from calling if state is value; otherwise the original . - public async Task> ThenAsync(Func>> onValue) - { - if (IsError) - { - return Errors; - } - - return await onValue(Value).ConfigureAwait(false); - } - - /// - /// If the state is a value, the provided is invoked asynchronously. - /// - /// The action to execute if the state is a value. - /// The original instance. - public async Task> ThenAsync(Func action) - { - if (IsError) - { - return Errors; - } - - await action(Value).ConfigureAwait(false); - - return this; - } - - /// - /// If the state is a value, the provided function is executed asynchronously and its result is returned. - /// - /// The type of the result. - /// The function to execute if the state is a value. - /// The result from calling if state is value; otherwise the original . - public async Task> ThenAsync(Func> onValue) - { - if (IsError) - { - return Errors; - } - - return await onValue(Value).ConfigureAwait(false); - } - - /// - /// If the state is error, the provided function is executed and its result is returned. - /// - /// The function to execute if the state is error. - /// The result from calling if state is error; otherwise the original . - public ErrorOr Else(Func, Error> onError) - { - if (!IsError) - { - return Value; - } - - return onError(Errors); - } - - /// - /// If the state is error, the provided function is executed and its result is returned. - /// - /// The function to execute if the state is error. - /// The result from calling if state is error; otherwise the original . - public ErrorOr Else(Func, List> onError) - { - if (!IsError) - { - return Value; - } - - return onError(Errors); - } - - /// - /// If the state is error, the provided is returned. - /// - /// The error to return. - /// The given . - public ErrorOr Else(Error error) - { - if (!IsError) - { - return Value; - } - - return error; - } - - /// - /// If the state is error, the provided function is executed and its result is returned. - /// - /// The function to execute if the state is error. - /// The result from calling if state is error; otherwise the original . - public ErrorOr Else(Func, TValue> onError) - { - if (!IsError) - { - return Value; - } - - return onError(Errors); - } - - /// - /// If the state is error, the provided function is executed and its result is returned. - /// - /// The value to return if the state is error. - /// The result from calling if state is error; otherwise the original . - public ErrorOr Else(TValue onError) - { - if (!IsError) - { - return Value; - } - - return onError; - } - - /// - /// If the state is error, the provided function is executed asynchronously and its result is returned. - /// - /// The function to execute if the state is error. - /// The result from calling if state is error; otherwise the original . - public async Task> ElseAsync(Func, Task> onError) - { - if (!IsError) - { - return Value; - } - - return await onError(Errors).ConfigureAwait(false); - } - - /// - /// If the state is error, the provided function is executed asynchronously and its result is returned. - /// - /// The function to execute if the state is error. - /// The result from calling if state is error; otherwise the original . - public async Task> ElseAsync(Func, Task> onError) - { - if (!IsError) - { - return Value; - } - - return await onError(Errors).ConfigureAwait(false); - } - - /// - /// If the state is error, the provided function is executed asynchronously and its result is returned. - /// - /// The function to execute if the state is error. - /// The result from calling if state is error; otherwise the original . - public async Task> ElseAsync(Func, Task>> onError) - { - if (!IsError) - { - return Value; - } - - return await onError(Errors).ConfigureAwait(false); - } - - /// - /// If the state is error, the provided is awaited and returned. - /// - /// The error to return if the state is error. - /// The result from awaiting the given . - public async Task> ElseAsync(Task error) - { - if (!IsError) - { - return Value; - } - - return await error.ConfigureAwait(false); - } - - /// - /// If the state is error, the provided function is executed asynchronously and its result is returned. - /// - /// The function to execute if the state is error. - /// The result from calling if state is error; otherwise the original . - public async Task> ElseAsync(Task onError) - { - if (!IsError) - { - return Value; - } - - return await onError.ConfigureAwait(false); - } } diff --git a/src/ErrorOr.csproj b/src/ErrorOr.csproj index 4c8427f..3fb733b 100644 --- a/src/ErrorOr.csproj +++ b/src/ErrorOr.csproj @@ -8,7 +8,7 @@ ErrorOr - 1.10.0 + 2.0.0 Amichai Mantinband icon-square.png Result,Results,ErrorOr,Error,Handling diff --git a/tests/ErrorOr.ElseAsyncTests.cs b/tests/ErrorOr.ElseAsyncTests.cs index fce08f2..ad4bb07 100644 --- a/tests/ErrorOr.ElseAsyncTests.cs +++ b/tests/ErrorOr.ElseAsyncTests.cs @@ -57,7 +57,7 @@ public async Task CallingElseAsyncWithValue_WhenIsSuccess_ShouldNotReturnElseVal } [Fact] - public async Task CallingElseAsyncWithValue_WhenIsError_ShouldReturnElseValue() + public async Task CallingElseAsyncWithValue_WhenIsError_ShouldInvokeElseFunc() { // Arrange ErrorOr errorOrString = Error.NotFound(); diff --git a/tests/ErrorOr.ElseTests.cs b/tests/ErrorOr.ElseTests.cs index 7a16faa..c2cf37c 100644 --- a/tests/ErrorOr.ElseTests.cs +++ b/tests/ErrorOr.ElseTests.cs @@ -23,7 +23,7 @@ public void CallingElseWithValueFunc_WhenIsSuccess_ShouldNotInvokeElseFunc() } [Fact] - public void CallingElseWithValueFunc_WhenIsError_ShouldReturnElseValue() + public void CallingElseWithValueFunc_WhenIsError_ShouldInvokeElseFunc() { // Arrange ErrorOr errorOrString = Error.NotFound(); @@ -57,7 +57,7 @@ public void CallingElseWithValue_WhenIsSuccess_ShouldNotReturnElseValue() } [Fact] - public void CallingElseWithValue_WhenIsError_ShouldReturnElseValue() + public void CallingElseWithValue_WhenIsError_ShouldInvokeElseFunc() { // Arrange ErrorOr errorOrString = Error.NotFound(); @@ -176,7 +176,7 @@ public void CallingElseWithErrorsFunc_WhenIsSuccess_ShouldNotReturnElseErrors() } [Fact] - public async Task CallingElseWithValueAfterThenAsync_WhenIsError_ShouldReturnElseValue() + public async Task CallingElseWithValueAfterThenAsync_WhenIsError_ShouldInvokeElseFunc() { // Arrange ErrorOr errorOrString = Error.NotFound(); @@ -193,7 +193,7 @@ public async Task CallingElseWithValueAfterThenAsync_WhenIsError_ShouldReturnEls } [Fact] - public async Task CallingElseWithValueFuncAfterThenAsync_WhenIsError_ShouldReturnElseValue() + public async Task CallingElseWithValueFuncAfterThenAsync_WhenIsError_ShouldInvokeElseFunc() { // Arrange ErrorOr errorOrString = Error.NotFound(); diff --git a/tests/ErrorOr.FailIfTests.cs b/tests/ErrorOr.FailIfTests.cs new file mode 100644 index 0000000..0f6b591 --- /dev/null +++ b/tests/ErrorOr.FailIfTests.cs @@ -0,0 +1,52 @@ +using ErrorOr; +using FluentAssertions; + +namespace Tests; + +public class FailIfTests +{ + [Fact] + public void CallingFailIf_WhenFailsIf_ShouldReturnError() + { + // Arrange + ErrorOr errorOrInt = 5; + + // Act + ErrorOr result = errorOrInt + .FailIf(num => num > 3, Error.Failure()); + + // Assert + result.IsError.Should().BeTrue(); + result.FirstError.Type.Should().Be(ErrorType.Failure); + } + + [Fact] + public void CallingFailIf_WhenDoesNotFailIf_ShouldReturnValue() + { + // Arrange + ErrorOr errorOrInt = 5; + + // Act + ErrorOr result = errorOrInt + .FailIf(num => num > 10, Error.Failure()); + + // Assert + result.IsError.Should().BeFalse(); + result.Value.Should().Be(5); + } + + [Fact] + public void CallingFailIf_WhenIsError_ShouldNotInvokeFailIfFunc() + { + // Arrange + ErrorOr errorOrString = Error.NotFound(); + + // Act + ErrorOr result = errorOrString + .FailIf(str => str == string.Empty, Error.Failure()); + + // Assert + result.IsError.Should().BeTrue(); + result.FirstError.Type.Should().Be(ErrorType.NotFound); + } +} diff --git a/tests/ErrorOr.MatchAsyncTests.cs b/tests/ErrorOr.MatchAsyncTests.cs index b95fa26..cd8adbd 100644 --- a/tests/ErrorOr.MatchAsyncTests.cs +++ b/tests/ErrorOr.MatchAsyncTests.cs @@ -8,35 +8,35 @@ public class MatchAsyncTests private record Person(string Name); [Fact] - public async Task CallingMatchAsync_WhenIsSuccess_ShouldExecuteOnValueAction() + public async Task CallingMatchAsync_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new Person("Amichai"); - Task OnValueAction(Person person) + Task ThenAction(Person person) { person.Should().BeEquivalentTo(errorOrPerson.Value); return Task.FromResult("Nice"); } - Task OnErrorsAction(IReadOnlyList _) => throw new Exception("Should not be called"); + Task ElsesAction(IReadOnlyList _) => throw new Exception("Should not be called"); // Act Func> action = async () => await errorOrPerson.MatchAsync( - OnValueAction, - OnErrorsAction); + ThenAction, + ElsesAction); // Assert (await action.Should().NotThrowAsync()).Subject.Should().Be("Nice"); } [Fact] - public async Task CallingMatchAsync_WhenIsError_ShouldExecuteOnErrorAction() + public async Task CallingMatchAsync_WhenIsError_ShouldExecuteElseAction() { // Arrange ErrorOr errorOrPerson = new List { Error.Validation(), Error.Conflict() }; - Task OnValueAction(Person _) => throw new Exception("Should not be called"); + Task ThenAction(Person _) => throw new Exception("Should not be called"); - Task OnErrorsAction(IReadOnlyList errors) + Task ElsesAction(IReadOnlyList errors) { errors.Should().BeEquivalentTo(errorOrPerson.Errors); return Task.FromResult("Nice"); @@ -44,19 +44,19 @@ Task OnErrorsAction(IReadOnlyList errors) // Act Func> action = async () => await errorOrPerson.MatchAsync( - OnValueAction, - OnErrorsAction); + ThenAction, + ElsesAction); // Assert (await action.Should().NotThrowAsync()).Subject.Should().Be("Nice"); } [Fact] - public async Task CallingMatchFirstAsync_WhenIsSuccess_ShouldExecuteOnValueAction() + public async Task CallingMatchFirstAsync_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new Person("Amichai"); - Task OnValueAction(Person person) + Task ThenAction(Person person) { person.Should().BeEquivalentTo(errorOrPerson.Value); return Task.FromResult("Nice"); @@ -66,7 +66,7 @@ Task OnValueAction(Person person) // Act Func> action = async () => await errorOrPerson.MatchFirstAsync( - OnValueAction, + ThenAction, OnFirstErrorAction); // Assert @@ -78,7 +78,7 @@ public async Task CallingMatchFirstAsync_WhenIsError_ShouldExecuteOnFirstErrorAc { // Arrange ErrorOr errorOrPerson = new List { Error.Validation(), Error.Conflict() }; - Task OnValueAction(Person _) => throw new Exception("Should not be called"); + Task ThenAction(Person _) => throw new Exception("Should not be called"); Task OnFirstErrorAction(Error errors) { errors.Should().BeEquivalentTo(errorOrPerson.Errors[0]) @@ -89,7 +89,7 @@ Task OnFirstErrorAction(Error errors) // Act Func> action = async () => await errorOrPerson.MatchFirstAsync( - OnValueAction, + ThenAction, OnFirstErrorAction); // Assert @@ -97,11 +97,11 @@ Task OnFirstErrorAction(Error errors) } [Fact] - public async Task CallingMatchFirstAsyncAfterThenAsync_WhenIsSuccess_ShouldExecuteOnValueAction() + public async Task CallingMatchFirstAsyncAfterThenAsync_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new List { Error.Validation(), Error.Conflict() }; - Task OnValueAction(Person _) => throw new Exception("Should not be called"); + Task ThenAction(Person _) => throw new Exception("Should not be called"); Task OnFirstErrorAction(Error errors) { errors.Should().BeEquivalentTo(errorOrPerson.Errors[0]) @@ -113,20 +113,20 @@ Task OnFirstErrorAction(Error errors) // Act Func> action = () => errorOrPerson .ThenAsync(person => Task.FromResult(person)) - .MatchFirstAsync(OnValueAction, OnFirstErrorAction); + .MatchFirstAsync(ThenAction, OnFirstErrorAction); // Assert (await action.Should().NotThrowAsync()).Subject.Should().Be("Nice"); } [Fact] - public async Task CallingMatchAsyncAfterThenAsync_WhenIsSuccess_ShouldExecuteOnValueAction() + public async Task CallingMatchAsyncAfterThenAsync_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new List { Error.Validation(), Error.Conflict() }; - Task OnValueAction(Person _) => throw new Exception("Should not be called"); + Task ThenAction(Person _) => throw new Exception("Should not be called"); - Task OnErrorsAction(IReadOnlyList errors) + Task ElsesAction(IReadOnlyList errors) { errors.Should().BeEquivalentTo(errorOrPerson.Errors); return Task.FromResult("Nice"); @@ -135,7 +135,7 @@ Task OnErrorsAction(IReadOnlyList errors) // Act Func> action = () => errorOrPerson .ThenAsync(person => Task.FromResult(person)) - .MatchAsync(OnValueAction, OnErrorsAction); + .MatchAsync(ThenAction, ElsesAction); // Assert (await action.Should().NotThrowAsync()).Subject.Should().Be("Nice"); diff --git a/tests/ErrorOr.MatchTests.cs b/tests/ErrorOr.MatchTests.cs index bc4ced5..062b82c 100644 --- a/tests/ErrorOr.MatchTests.cs +++ b/tests/ErrorOr.MatchTests.cs @@ -8,35 +8,35 @@ public class MatchTests private record Person(string Name); [Fact] - public void CallingMatch_WhenIsSuccess_ShouldExecuteOnValueAction() + public void CallingMatch_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new Person("Amichai"); - string OnValueAction(Person person) + string ThenAction(Person person) { person.Should().BeEquivalentTo(errorOrPerson.Value); return "Nice"; } - string OnErrorsAction(IReadOnlyList _) => throw new Exception("Should not be called"); + string ElsesAction(IReadOnlyList _) => throw new Exception("Should not be called"); // Act Func action = () => errorOrPerson.Match( - OnValueAction, - OnErrorsAction); + ThenAction, + ElsesAction); // Assert action.Should().NotThrow().Subject.Should().Be("Nice"); } [Fact] - public void CallingMatch_WhenIsError_ShouldExecuteOnErrorAction() + public void CallingMatch_WhenIsError_ShouldExecuteElseAction() { // Arrange ErrorOr errorOrPerson = new List { Error.Validation(), Error.Conflict() }; - string OnValueAction(Person _) => throw new Exception("Should not be called"); + string ThenAction(Person _) => throw new Exception("Should not be called"); - string OnErrorsAction(IReadOnlyList errors) + string ElsesAction(IReadOnlyList errors) { errors.Should().BeEquivalentTo(errorOrPerson.Errors); return "Nice"; @@ -44,19 +44,19 @@ string OnErrorsAction(IReadOnlyList errors) // Act Func action = () => errorOrPerson.Match( - OnValueAction, - OnErrorsAction); + ThenAction, + ElsesAction); // Assert action.Should().NotThrow().Subject.Should().Be("Nice"); } [Fact] - public void CallingMatchFirst_WhenIsSuccess_ShouldExecuteOnValueAction() + public void CallingMatchFirst_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new Person("Amichai"); - string OnValueAction(Person person) + string ThenAction(Person person) { person.Should().BeEquivalentTo(errorOrPerson.Value); return "Nice"; @@ -66,7 +66,7 @@ string OnValueAction(Person person) // Act Func action = () => errorOrPerson.MatchFirst( - OnValueAction, + ThenAction, OnFirstErrorAction); // Assert @@ -78,7 +78,7 @@ public void CallingMatchFirst_WhenIsError_ShouldExecuteOnFirstErrorAction() { // Arrange ErrorOr errorOrPerson = new List { Error.Validation(), Error.Conflict() }; - string OnValueAction(Person _) => throw new Exception("Should not be called"); + string ThenAction(Person _) => throw new Exception("Should not be called"); string OnFirstErrorAction(Error errors) { errors.Should().BeEquivalentTo(errorOrPerson.Errors[0]) @@ -89,7 +89,7 @@ string OnFirstErrorAction(Error errors) // Act Func action = () => errorOrPerson.MatchFirst( - OnValueAction, + ThenAction, OnFirstErrorAction); // Assert @@ -97,11 +97,11 @@ string OnFirstErrorAction(Error errors) } [Fact] - public async Task CallingMatchFirstAfterThenAsync_WhenIsSuccess_ShouldExecuteOnValueAction() + public async Task CallingMatchFirstAfterThenAsync_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new Person("Amichai"); - string OnValueAction(Person person) + string ThenAction(Person person) { person.Should().BeEquivalentTo(errorOrPerson.Value); return "Nice"; @@ -112,29 +112,29 @@ string OnValueAction(Person person) // Act Func> action = () => errorOrPerson .ThenAsync(person => Task.FromResult(person)) - .MatchFirst(OnValueAction, OnFirstErrorAction); + .MatchFirst(ThenAction, OnFirstErrorAction); // Assert (await action.Should().NotThrowAsync()).Subject.Should().Be("Nice"); } [Fact] - public async Task CallingMatchAfterThenAsync_WhenIsSuccess_ShouldExecuteOnValueAction() + public async Task CallingMatchAfterThenAsync_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new Person("Amichai"); - string OnValueAction(Person person) + string ThenAction(Person person) { person.Should().BeEquivalentTo(errorOrPerson.Value); return "Nice"; } - string OnErrorsAction(IReadOnlyList _) => throw new Exception("Should not be called"); + string ElsesAction(IReadOnlyList _) => throw new Exception("Should not be called"); // Act Func> action = () => errorOrPerson .ThenAsync(person => Task.FromResult(person)) - .Match(OnValueAction, OnErrorsAction); + .Match(ThenAction, ElsesAction); // Assert (await action.Should().NotThrowAsync()).Subject.Should().Be("Nice"); diff --git a/tests/ErrorOr.SwitchAsyncTests.cs b/tests/ErrorOr.SwitchAsyncTests.cs index e1a7b4c..253d9e1 100644 --- a/tests/ErrorOr.SwitchAsyncTests.cs +++ b/tests/ErrorOr.SwitchAsyncTests.cs @@ -8,50 +8,50 @@ public class SwitchAsyncTests private record Person(string Name); [Fact] - public async Task CallingSwitchAsync_WhenIsSuccess_ShouldExecuteOnValueAction() + public async Task CallingSwitchAsync_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new Person("Amichai"); - Task OnValueAction(Person person) => Task.FromResult(person.Should().BeEquivalentTo(errorOrPerson.Value)); - Task OnErrorsAction(IReadOnlyList _) => throw new Exception("Should not be called"); + Task ThenAction(Person person) => Task.FromResult(person.Should().BeEquivalentTo(errorOrPerson.Value)); + Task ElsesAction(IReadOnlyList _) => throw new Exception("Should not be called"); // Act Func action = async () => await errorOrPerson.SwitchAsync( - OnValueAction, - OnErrorsAction); + ThenAction, + ElsesAction); // Assert await action.Should().NotThrowAsync(); } [Fact] - public async Task CallingSwitchAsync_WhenIsError_ShouldExecuteOnErrorAction() + public async Task CallingSwitchAsync_WhenIsError_ShouldExecuteElseAction() { // Arrange ErrorOr errorOrPerson = new List { Error.Validation(), Error.Conflict() }; - Task OnValueAction(Person _) => throw new Exception("Should not be called"); - Task OnErrorsAction(IReadOnlyList errors) => Task.FromResult(errors.Should().BeEquivalentTo(errorOrPerson.Errors)); + Task ThenAction(Person _) => throw new Exception("Should not be called"); + Task ElsesAction(IReadOnlyList errors) => Task.FromResult(errors.Should().BeEquivalentTo(errorOrPerson.Errors)); // Act Func action = async () => await errorOrPerson.SwitchAsync( - OnValueAction, - OnErrorsAction); + ThenAction, + ElsesAction); // Assert await action.Should().NotThrowAsync(); } [Fact] - public async Task CallingSwitchFirstAsync_WhenIsSuccess_ShouldExecuteOnValueAction() + public async Task CallingSwitchFirstAsync_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new Person("Amichai"); - Task OnValueAction(Person person) => Task.FromResult(person.Should().BeEquivalentTo(errorOrPerson.Value)); + Task ThenAction(Person person) => Task.FromResult(person.Should().BeEquivalentTo(errorOrPerson.Value)); Task OnFirstErrorAction(Error _) => throw new Exception("Should not be called"); // Act Func action = async () => await errorOrPerson.SwitchFirstAsync( - OnValueAction, + ThenAction, OnFirstErrorAction); // Assert @@ -63,14 +63,14 @@ public async Task CallingSwitchFirstAsync_WhenIsError_ShouldExecuteOnFirstErrorA { // Arrange ErrorOr errorOrPerson = new List { Error.Validation(), Error.Conflict() }; - Task OnValueAction(Person _) => throw new Exception("Should not be called"); + Task ThenAction(Person _) => throw new Exception("Should not be called"); Task OnFirstErrorAction(Error errors) => Task.FromResult(errors.Should().BeEquivalentTo(errorOrPerson.Errors[0]) .And.BeEquivalentTo(errorOrPerson.FirstError)); // Act Func action = async () => await errorOrPerson.SwitchFirstAsync( - OnValueAction, + ThenAction, OnFirstErrorAction); // Assert @@ -78,18 +78,18 @@ Task OnFirstErrorAction(Error errors) } [Fact] - public async Task CallingSwitchFirstAsyncAfterThenAsync_WhenIsSuccess_ShouldExecuteOnValueAction() + public async Task CallingSwitchFirstAsyncAfterThenAsync_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new Person("Amichai"); - Task OnValueAction(Person person) => Task.FromResult(person.Should().BeEquivalentTo(errorOrPerson.Value)); + Task ThenAction(Person person) => Task.FromResult(person.Should().BeEquivalentTo(errorOrPerson.Value)); Task OnFirstErrorAction(Error _) => throw new Exception("Should not be called"); // Act Func action = async () => await errorOrPerson .ThenAsync(person => Task.FromResult(person)) .SwitchFirstAsync( - OnValueAction, + ThenAction, OnFirstErrorAction); // Assert @@ -97,17 +97,17 @@ public async Task CallingSwitchFirstAsyncAfterThenAsync_WhenIsSuccess_ShouldExec } [Fact] - public async Task CallingSwitchAsyncAfterThenAsync_WhenIsSuccess_ShouldExecuteOnValueAction() + public async Task CallingSwitchAsyncAfterThenAsync_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new Person("Amichai"); - Task OnValueAction(Person person) => Task.FromResult(person.Should().BeEquivalentTo(errorOrPerson.Value)); - Task OnErrorsAction(IReadOnlyList _) => throw new Exception("Should not be called"); + Task ThenAction(Person person) => Task.FromResult(person.Should().BeEquivalentTo(errorOrPerson.Value)); + Task ElsesAction(IReadOnlyList _) => throw new Exception("Should not be called"); // Act Func action = async () => await errorOrPerson .ThenAsync(person => Task.FromResult(person)) - .SwitchAsync(OnValueAction, OnErrorsAction); + .SwitchAsync(ThenAction, ElsesAction); // Assert await action.Should().NotThrowAsync(); diff --git a/tests/ErrorOr.SwitchTests.cs b/tests/ErrorOr.SwitchTests.cs index 92a018c..6f48482 100644 --- a/tests/ErrorOr.SwitchTests.cs +++ b/tests/ErrorOr.SwitchTests.cs @@ -8,50 +8,50 @@ public class SwitchTests private record Person(string Name); [Fact] - public void CallingSwitch_WhenIsSuccess_ShouldExecuteOnValueAction() + public void CallingSwitch_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new Person("Amichai"); - void OnValueAction(Person person) => person.Should().BeEquivalentTo(errorOrPerson.Value); - void OnErrorsAction(IReadOnlyList _) => throw new Exception("Should not be called"); + void ThenAction(Person person) => person.Should().BeEquivalentTo(errorOrPerson.Value); + void ElsesAction(IReadOnlyList _) => throw new Exception("Should not be called"); // Act Action action = () => errorOrPerson.Switch( - OnValueAction, - OnErrorsAction); + ThenAction, + ElsesAction); // Assert action.Should().NotThrow(); } [Fact] - public void CallingSwitch_WhenIsError_ShouldExecuteOnErrorAction() + public void CallingSwitch_WhenIsError_ShouldExecuteElseAction() { // Arrange ErrorOr errorOrPerson = new List { Error.Validation(), Error.Conflict() }; - void OnValueAction(Person _) => throw new Exception("Should not be called"); - void OnErrorsAction(IReadOnlyList errors) => errors.Should().BeEquivalentTo(errorOrPerson.Errors); + void ThenAction(Person _) => throw new Exception("Should not be called"); + void ElsesAction(IReadOnlyList errors) => errors.Should().BeEquivalentTo(errorOrPerson.Errors); // Act Action action = () => errorOrPerson.Switch( - OnValueAction, - OnErrorsAction); + ThenAction, + ElsesAction); // Assert action.Should().NotThrow(); } [Fact] - public void CallingSwitchFirst_WhenIsSuccess_ShouldExecuteOnValueAction() + public void CallingSwitchFirst_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new Person("Amichai"); - void OnValueAction(Person person) => person.Should().BeEquivalentTo(errorOrPerson.Value); + void ThenAction(Person person) => person.Should().BeEquivalentTo(errorOrPerson.Value); void OnFirstErrorAction(Error _) => throw new Exception("Should not be called"); // Act Action action = () => errorOrPerson.SwitchFirst( - OnValueAction, + ThenAction, OnFirstErrorAction); // Assert @@ -63,14 +63,14 @@ public void CallingSwitchFirst_WhenIsError_ShouldExecuteOnFirstErrorAction() { // Arrange ErrorOr errorOrPerson = new List { Error.Validation(), Error.Conflict() }; - void OnValueAction(Person _) => throw new Exception("Should not be called"); + void ThenAction(Person _) => throw new Exception("Should not be called"); void OnFirstErrorAction(Error errors) => errors.Should().BeEquivalentTo(errorOrPerson.Errors[0]) .And.BeEquivalentTo(errorOrPerson.FirstError); // Act Action action = () => errorOrPerson.SwitchFirst( - OnValueAction, + ThenAction, OnFirstErrorAction); // Assert @@ -78,34 +78,34 @@ void OnFirstErrorAction(Error errors) } [Fact] - public async Task CallingSwitchFirstAfterThenAsync_WhenIsSuccess_ShouldExecuteOnValueAction() + public async Task CallingSwitchFirstAfterThenAsync_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new Person("Amichai"); - void OnValueAction(Person person) => person.Should().BeEquivalentTo(errorOrPerson.Value); + void ThenAction(Person person) => person.Should().BeEquivalentTo(errorOrPerson.Value); void OnFirstErrorAction(Error _) => throw new Exception("Should not be called"); // Act Func action = () => errorOrPerson .ThenAsync(person => Task.FromResult(person)) - .SwitchFirst(OnValueAction, OnFirstErrorAction); + .SwitchFirst(ThenAction, OnFirstErrorAction); // Assert await action.Should().NotThrowAsync(); } [Fact] - public async Task CallingSwitchAfterThenAsync_WhenIsSuccess_ShouldExecuteOnValueAction() + public async Task CallingSwitchAfterThenAsync_WhenIsSuccess_ShouldExecuteThenAction() { // Arrange ErrorOr errorOrPerson = new Person("Amichai"); - void OnValueAction(Person person) => person.Should().BeEquivalentTo(errorOrPerson.Value); - void OnErrorsAction(IReadOnlyList _) => throw new Exception("Should not be called"); + void ThenAction(Person person) => person.Should().BeEquivalentTo(errorOrPerson.Value); + void ElsesAction(IReadOnlyList _) => throw new Exception("Should not be called"); // Act Func action = () => errorOrPerson .ThenAsync(person => Task.FromResult(person)) - .Switch(OnValueAction, OnErrorsAction); + .Switch(ThenAction, ElsesAction); // Assert await action.Should().NotThrowAsync(); diff --git a/tests/ErrorOr.ThenAsyncTests.cs b/tests/ErrorOr.ThenAsyncTests.cs index 79c6983..f3b0305 100644 --- a/tests/ErrorOr.ThenAsyncTests.cs +++ b/tests/ErrorOr.ThenAsyncTests.cs @@ -15,7 +15,7 @@ public async Task CallingThenAsync_WhenIsSuccess_ShouldInvokeNextThen() ErrorOr result = await errorOrString .ThenAsync(str => ConvertToIntAsync(str)) .ThenAsync(num => Task.FromResult(num * 2)) - .ThenAsync(num => Task.Run(() => { _ = 5; })) + .ThenDoAsync(num => Task.Run(() => { _ = 5; })) .ThenAsync(num => ConvertToStringAsync(num)); // Assert @@ -33,7 +33,7 @@ public async Task CallingThenAsync_WhenIsError_ShouldReturnErrors() ErrorOr result = await errorOrString .ThenAsync(str => ConvertToIntAsync(str)) .ThenAsync(num => Task.FromResult(num * 2)) - .ThenAsync(num => Task.Run(() => { _ = 5; })) + .ThenDoAsync(num => Task.Run(() => { _ = 5; })) .ThenAsync(num => ConvertToStringAsync(num)); // Assert diff --git a/tests/ErrorOr.ThenTests.cs b/tests/ErrorOr.ThenTests.cs index 5b8325f..0e91bb9 100644 --- a/tests/ErrorOr.ThenTests.cs +++ b/tests/ErrorOr.ThenTests.cs @@ -30,9 +30,9 @@ public void CallingThen_WhenIsSuccess_ShouldInvokeGivenAction() // Act ErrorOr result = errorOrString - .Then(str => { _ = 5; }) + .ThenDo(str => { _ = 5; }) .Then(str => ConvertToInt(str)) - .Then(str => { _ = 5; }); + .ThenDo(str => { _ = 5; }); // Assert result.IsError.Should().BeFalse(); @@ -49,7 +49,7 @@ public void CallingThen_WhenIsError_ShouldReturnErrors() ErrorOr result = errorOrString .Then(str => ConvertToInt(str)) .Then(num => num * 2) - .Then(str => { _ = 5; }) + .ThenDo(str => { _ = 5; }) .Then(num => ConvertToString(num)); // Assert @@ -70,7 +70,7 @@ public async Task CallingThenAfterThenAsync_WhenIsSuccess_ShouldInvokeGivenFunc( .ThenAsync(num => ConvertToStringAsync(num)) .Then(str => ConvertToInt(str)) .ThenAsync(num => ConvertToStringAsync(num)) - .Then(num => { _ = 5; }); + .ThenDo(num => { _ = 5; }); // Assert result.IsError.Should().BeFalse();