Skip to content

Commit

Permalink
Add Then, ThenAsync, Else, ElseAsync
Browse files Browse the repository at this point in the history
  • Loading branch information
amantinband committed Jan 3, 2024
1 parent e8e795f commit 747c041
Show file tree
Hide file tree
Showing 11 changed files with 484 additions and 130 deletions.
73 changes: 55 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
- [`MatchFirst` / `MatchFirstAsync`](#matchfirst--matchfirstasync)
- [`Switch` / `SwitchAsync`](#switch--switchasync)
- [`SwitchFirst` / `SwitchFirstAsync`](#switchfirst--switchfirstasync)
- [`Chain` / `ChainAsync`](#chain--chainasync)
- [`Then` / `ThenAsync`](#then--thenasync)
- [`Else` / `ElseAsync`](#else--elseasync)
- [Error Types](#error-types)
- [Built-in Error Types](#built-in-error-types)
- [Custom error types](#custom-error-types)
Expand Down Expand Up @@ -510,9 +511,9 @@ await errorOrString.SwitchFirstAsync(
firstError => { Console.WriteLine(firstError.Description); return Task.CompletedTask; });
```

### `Chain` / `ChainAsync`
### `Then` / `ThenAsync`

Multiple methods that return `ErrorOr<T>` can be chained as follows
Multiple methods that return `ErrorOr<T>` can be chained as follows:

```csharp
static ErrorOr<string> ConvertToString(int num) => num.ToString();
Expand All @@ -521,30 +522,66 @@ static ErrorOr<int> ConvertToInt(string str) => int.Parse(str);
ErrorOr<string> errorOrString = "5";

ErrorOr<string> result = errorOrString
.Chain(str => ConvertToInt(str))
.Chain(num => ConvertToString(num))
.Chain(str => ConvertToInt(str))
.Chain(num => ConvertToString(num))
.Chain(str => ConvertToInt(str))
.Chain(num => ConvertToString(num));
.Then(str => ConvertToInt(str))
.Then(num => ConvertToString(num))
.Then(str => ConvertToInt(str))
.Then(num => ConvertToString(num));
```

```csharp
static Task<ErrorOr<string>> ConvertToString(int num) => Task.FromResult(ErrorOrFactory.From(num.ToString()));
static Task<ErrorOr<int>> ConvertToInt(string str) => Task.FromResult(ErrorOrFactory.From(int.Parse(str)));
static ErrorOr<string> ConvertToString(int num) => num.ToString();
static Task<ErrorOr<string>> ConvertToStringAsync(int num) => Task.FromResult(ErrorOrFactory.From(num.ToString()));
static Task<ErrorOr<int>> ConvertToIntAsync(string str) => Task.FromResult(ErrorOrFactory.From(int.Parse(str)));

ErrorOr<string> errorOrString = "5";

ErrorOr<string> result = await errorOrString
.ChainAsync(str => ConvertToInt(str))
.ChainAsync(num => ConvertToString(num))
.ChainAsync(str => ConvertToInt(str))
.ChainAsync(num => ConvertToString(num))
.ChainAsync(str => ConvertToInt(str))
.ChainAsync(num => ConvertToString(num));
.ThenAsync(str => ConvertToIntAsync(str))
.ThenAsync(num => ConvertToStringAsync(num))
.ThenAsync(str => ConvertToIntAsync(str))
.ThenAsync(num => ConvertToStringAsync(num));

// mixing `ThenAsync` and `Then`
ErrorOr<string> result = await errorOrString
.ThenAsync(str => ConvertToIntAsync(str))
.Then(num => ConvertToString(num))
.ThenAsync(str => ConvertToIntAsync(str))
.Then(num => ConvertToString(num));
```

If any of the methods return an error, the chain will be broken and the error will be returned.
If any of the methods return an error, the chain will break and the errors will be returned.

### `Else` / `ElseAsync`

The `Else` / `ElseAsync` methods can be used to specify a fallback value in case the state is error anywhere in the chain.

```csharp
// ignoring the errors
string result = errorOrString
.Then(str => ConvertToInt(str))
.Then(num => ConvertToString(num))
.Else("fallback value");

// using the errors
string result = errorOrString
.Then(str => ConvertToInt(str))
.Then(num => ConvertToString(num))
.Else(errors => $"{errors.Count} errors occurred.");
```

```csharp
// ignoring the errors
string result = await errorOrString
.ThenAsync(str => ConvertToInt(str))
.ThenAsync(num => ConvertToString(num))
.ElseAsync(Task.FromResult("fallback value"));

// using the errors
string result = await errorOrString
.ThenAsync(str => ConvertToInt(str))
.ThenAsync(num => ConvertToString(num))
.ElseAsync(errors => Task.FromResult($"{errors.Count} errors occurred."));
```

## Error Types

Expand Down
64 changes: 62 additions & 2 deletions src/ErrorOr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ public async Task<TResult> MatchFirstAsync<TResult>(Func<TValue, Task<TResult>>
/// <typeparam name="TResult">The type of the result.</typeparam>
/// <param name="onValue">The function to execute if the state is a value.</param>
/// <returns>The result from calling <paramref name="onValue"/> if state is value; otherwise the original <see cref="Errors"/>.</returns>
public ErrorOr<TResult> Chain<TResult>(Func<TValue, ErrorOr<TResult>> onValue)
public ErrorOr<TResult> Then<TResult>(Func<TValue, ErrorOr<TResult>> onValue)
{
if (IsError)
{
Expand All @@ -282,7 +282,7 @@ public ErrorOr<TResult> Chain<TResult>(Func<TValue, ErrorOr<TResult>> onValue)
/// <typeparam name="TResult">The type of the result.</typeparam>
/// <param name="onValue">The function to execute if the state is a value.</param>
/// <returns>The result from calling <paramref name="onValue"/> if state is value; otherwise the original <see cref="Errors"/>.</returns>
public async Task<ErrorOr<TResult>> ChainAsync<TResult>(Func<TValue, Task<ErrorOr<TResult>>> onValue)
public async Task<ErrorOr<TResult>> ThenAsync<TResult>(Func<TValue, Task<ErrorOr<TResult>>> onValue)
{
if (IsError)
{
Expand All @@ -291,4 +291,64 @@ public async Task<ErrorOr<TResult>> ChainAsync<TResult>(Func<TValue, Task<ErrorO

return await onValue(Value).ConfigureAwait(false);
}

/// <summary>
/// If the state is error, the provided function <paramref name="onError"/> is executed and its result is returned.
/// </summary>
/// <param name="onError">The function to execute if the state is error.</param>
/// <returns>The result from calling <paramref name="onError"/> if state is value; otherwise the original <see cref="Errors"/>.</returns>
public TValue Else(Func<List<Error>, TValue> onError)
{
if (!IsError)
{
return Value;
}

return onError(Errors);
}

/// <summary>
/// If the state is error, the provided function <paramref name="onError"/> is executed and its result is returned.
/// </summary>
/// <param name="onError">The value to return if the state is error.</param>
/// <returns>The result from calling <paramref name="onError"/> if state is value; otherwise the original <see cref="Errors"/>.</returns>
public TValue Else(TValue onError)
{
if (!IsError)
{
return Value;
}

return onError;
}

/// <summary>
/// If the state is error, the provided function <paramref name="onError"/> is executed asynchronously and its result is returned.
/// </summary>
/// <param name="onError">The function to execute if the state is error.</param>
/// <returns>The result from calling <paramref name="onError"/> if state is value; otherwise the original <see cref="Errors"/>.</returns>
public async Task<TValue> ElseAsync(Func<List<Error>, Task<TValue>> onError)
{
if (!IsError)
{
return Value;
}

return await onError(Errors).ConfigureAwait(false);
}

/// <summary>
/// If the state is error, the provided function <paramref name="onError"/> is executed asynchronously and its result is returned.
/// </summary>
/// <param name="onError">The function to execute if the state is error.</param>
/// <returns>The result from calling <paramref name="onError"/> if state is value; otherwise the original <see cref="Errors"/>.</returns>
public async Task<TValue> ElseAsync(Task<TValue> onError)
{
if (!IsError)
{
return Value;
}

return await onError.ConfigureAwait(false);
}
}
73 changes: 70 additions & 3 deletions src/ErrorOrExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@ namespace ErrorOr;

public static class ErrorOrExtensions
{
/// <summary>
/// If the state of <paramref name="errorOr"/> is a value, the provided function <paramref name="onValue"/> is executed and its result is returned.
/// </summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
/// <typeparam name="TNextResult">The type of the next result.</typeparam>
/// <param name="errorOr">The error.</param>
/// <param name="onValue">The function to execute if the state is a value.</param>
/// <returns>The result from calling <paramref name="onValue"/> if state is value; otherwise the original errors.</returns>
public static async Task<ErrorOr<TNextResult>> Then<TResult, TNextResult>(this Task<ErrorOr<TResult>> errorOr, Func<TResult, ErrorOr<TNextResult>> onValue)
{
var result = await errorOr.ConfigureAwait(false);

return result.Then(onValue);
}

/// <summary>
/// If the state of <paramref name="errorOr"/> is a value, the provided function <paramref name="onValue"/> is executed asynchronously and its result is returned.
/// </summary>
Expand All @@ -10,10 +25,62 @@ public static class ErrorOrExtensions
/// <param name="errorOr">The error.</param>
/// <param name="onValue">The function to execute if the state is a value.</param>
/// <returns>The result from calling <paramref name="onValue"/> if state is value; otherwise the original errors.</returns>
public static async Task<ErrorOr<TNextResult>> ChainAsync<TResult, TNextResult>(this Task<ErrorOr<TResult>> errorOr, Func<TResult, Task<ErrorOr<TNextResult>>> onValue)
public static async Task<ErrorOr<TNextResult>> ThenAsync<TResult, TNextResult>(this Task<ErrorOr<TResult>> errorOr, Func<TResult, Task<ErrorOr<TNextResult>>> onValue)
{
var result = await errorOr.ConfigureAwait(false);

return await result.ThenAsync(onValue).ConfigureAwait(false);
}

/// <summary>
/// If the state is error, the provided function <paramref name="onError"/> is executed asynchronously and its result is returned.
/// </summary>
/// <param name="errorOr">The error.</param>
/// <param name="onError">The function to execute if the state is error.</param>
/// <returns>The result from calling <paramref name="onError"/> if state is value; otherwise the original errors.</returns>
public static async Task<TValue> Else<TValue>(this Task<ErrorOr<TValue>> errorOr, Func<List<Error>, TValue> onError)
{
var result = await errorOr.ConfigureAwait(false);

return result.Else(onError);
}

/// <summary>
/// If the state is error, the provided function <paramref name="onError"/> is executed asynchronously and its result is returned.
/// </summary>
/// <param name="errorOr">The error.</param>
/// <param name="onError">The function to execute if the state is error.</param>
/// <returns>The result from calling <paramref name="onError"/> if state is value; otherwise the original errors.</returns>
public static async Task<TValue> Else<TValue>(this Task<ErrorOr<TValue>> errorOr, TValue onError)
{
var result = await errorOr.ConfigureAwait(false);

return result.Else(onError);
}

/// <summary>
/// If the state is error, the provided function <paramref name="onError"/> is executed asynchronously and its result is returned.
/// </summary>
/// <param name="errorOr">The error.</param>
/// <param name="onError">The function to execute if the state is error.</param>
/// <returns>The result from calling <paramref name="onError"/> if state is value; otherwise the original errors.</returns>
public static async Task<TValue> ElseAsync<TValue>(this Task<ErrorOr<TValue>> errorOr, Func<List<Error>, Task<TValue>> onError)
{
var result = await errorOr.ConfigureAwait(false);

return await result.ElseAsync(onError).ConfigureAwait(false);
}

/// <summary>
/// If the state is error, the provided function <paramref name="onError"/> is executed asynchronously and its result is returned.
/// </summary>
/// <param name="errorOr">The error.</param>
/// <param name="onError">The function to execute if the state is error.</param>
/// <returns>The result from calling <paramref name="onError"/> if state is value; otherwise the original errors.</returns>
public static async Task<TValue> ElseAsync<TValue>(this Task<ErrorOr<TValue>> errorOr, Task<TValue> onError)
{
var result = await errorOr;
var result = await errorOr.ConfigureAwait(false);

return await result.ChainAsync(onValue).ConfigureAwait(false);
return await result.ElseAsync(onError).ConfigureAwait(false);
}
}
51 changes: 0 additions & 51 deletions tests/ErrorOr.ChainAsyncTests.cs

This file was deleted.

47 changes: 0 additions & 47 deletions tests/ErrorOr.ChainTests.cs

This file was deleted.

Loading

0 comments on commit 747c041

Please sign in to comment.