235 lines
7.5 KiB
C#
235 lines
7.5 KiB
C#
using System.Collections.Immutable;
|
|
|
|
namespace Just.Railway;
|
|
|
|
public static partial class ResultExtensions
|
|
{
|
|
#region Finally
|
|
|
|
public static Result Finally(this in Result result, Action action)
|
|
{
|
|
switch (result.State)
|
|
{
|
|
case ResultState.Success:
|
|
case ResultState.Error:
|
|
action.Invoke();
|
|
break;
|
|
|
|
default: throw new ResultNotInitializedException(nameof(result));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public static async Task<Result> Finally(this Task<Result> resultTask, Action action)
|
|
{
|
|
var result = await resultTask;
|
|
switch (result.State)
|
|
{
|
|
case ResultState.Success:
|
|
case ResultState.Error:
|
|
action.Invoke();
|
|
break;
|
|
|
|
default: throw new ResultNotInitializedException(nameof(resultTask));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
public static async Task<Result> Finally(this Result result, Func<Task> action)
|
|
{
|
|
switch (result.State)
|
|
{
|
|
case ResultState.Success:
|
|
case ResultState.Error:
|
|
await action.Invoke();
|
|
break;
|
|
|
|
default: throw new ResultNotInitializedException(nameof(result));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
public static async Task<Result> Finally(this Task<Result> resultTask, Func<Task> action)
|
|
{
|
|
var result = await resultTask;
|
|
switch (result.State)
|
|
{
|
|
case ResultState.Success:
|
|
case ResultState.Error:
|
|
await action.Invoke();
|
|
break;
|
|
|
|
default: throw new ResultNotInitializedException(nameof(resultTask));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ValueOr
|
|
|
|
public static T ValueOr<T>(this in Result<T> result, T fallback)
|
|
{
|
|
return result.State switch
|
|
{
|
|
ResultState.Success => result.Value,
|
|
ResultState.Error => fallback,
|
|
_ => throw new ResultNotInitializedException(nameof(result))
|
|
};
|
|
}
|
|
public static T ValueOr<T>(this in Result<T> result, Func<Error, T> fallback)
|
|
{
|
|
return result.State switch
|
|
{
|
|
ResultState.Success => result.Value,
|
|
ResultState.Error => fallback(result.Error!),
|
|
_ => throw new ResultNotInitializedException(nameof(result))
|
|
};
|
|
}
|
|
|
|
public static async Task<T> ValueOr<T>(this Task<Result<T>> resultTask, T fallback)
|
|
{
|
|
var result = await resultTask.ConfigureAwait(false);
|
|
return result.State switch
|
|
{
|
|
ResultState.Success => result.Value,
|
|
ResultState.Error => fallback,
|
|
_ => throw new ResultNotInitializedException(nameof(resultTask))
|
|
};
|
|
}
|
|
public static async Task<T> ValueOr<T>(this Result<T> result, Func<Error, Task<T>> fallback)
|
|
{
|
|
return result.State switch
|
|
{
|
|
ResultState.Success => result.Value,
|
|
ResultState.Error => await fallback(result.Error!).ConfigureAwait(false),
|
|
_ => throw new ResultNotInitializedException(nameof(result))
|
|
};
|
|
}
|
|
public static async Task<T> ValueOr<T>(this Task<Result<T>> resultTask, Func<Error, T> fallback)
|
|
{
|
|
var result = await resultTask.ConfigureAwait(false);
|
|
return result.State switch
|
|
{
|
|
ResultState.Success => result.Value,
|
|
ResultState.Error => fallback(result.Error!),
|
|
_ => throw new ResultNotInitializedException(nameof(resultTask))
|
|
};
|
|
}
|
|
public static async Task<T> ValueOr<T>(this Task<Result<T>> resultTask, Func<Error, Task<T>> fallback)
|
|
{
|
|
var result = await resultTask.ConfigureAwait(false);
|
|
return result.State switch
|
|
{
|
|
ResultState.Success => result.Value,
|
|
ResultState.Error => await fallback(result.Error!).ConfigureAwait(false),
|
|
_ => throw new ResultNotInitializedException(nameof(resultTask))
|
|
};
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Merge
|
|
|
|
public static Result Merge(this IEnumerable<Result> results)
|
|
{
|
|
ImmutableArray<Error>.Builder? errors = null;
|
|
|
|
foreach (var result in results)
|
|
{
|
|
switch (result.State)
|
|
{
|
|
case ResultState.Success:
|
|
break;
|
|
|
|
case ResultState.Error:
|
|
errors ??= ImmutableArray.CreateBuilder<Error>();
|
|
ManyErrors.AppendSanitized(errors, result.Error!);
|
|
break;
|
|
|
|
default: throw new ResultNotInitializedException(nameof(results));
|
|
}
|
|
}
|
|
|
|
return errors is null
|
|
? new(null)
|
|
: new(new ManyErrors(errors.ToImmutable()));
|
|
}
|
|
public static async Task<Result> Merge(this IEnumerable<Task<Result>> tasks)
|
|
{
|
|
var taskList = tasks.ToArray();
|
|
var results = new Result[taskList.Length];
|
|
|
|
// Wait for all tasks to complete (suppress AggregateException — we'll check each individually)
|
|
try { await Task.WhenAll(taskList).ConfigureAwait(false); } catch { /* handled per-task below */ }
|
|
|
|
for (int i = 0; i < taskList.Length; i++)
|
|
{
|
|
var task = taskList[i];
|
|
results[i] = task.Status switch
|
|
{
|
|
TaskStatus.RanToCompletion => task.Result,
|
|
TaskStatus.Faulted => Result.Failure(task.Exception!),
|
|
TaskStatus.Canceled => Result.Failure(Error.New("task_canceled", "Task was canceled")),
|
|
_ => Result.Failure(Error.New("task_unexpected", $"Task in unexpected state: {task.Status}"))
|
|
};
|
|
}
|
|
|
|
return results.Merge();
|
|
}
|
|
|
|
public static Result<IEnumerable<T>> Merge<T>(this IEnumerable<Result<T>> results)
|
|
{
|
|
ImmutableArray<Error>.Builder? errors = null;
|
|
ImmutableArray<T>.Builder? values = null;
|
|
|
|
foreach (var result in results)
|
|
{
|
|
switch (result.State)
|
|
{
|
|
case ResultState.Success:
|
|
values ??= ImmutableArray.CreateBuilder<T>();
|
|
values.Add(result.Value);
|
|
break;
|
|
|
|
case ResultState.Error:
|
|
errors ??= ImmutableArray.CreateBuilder<Error>();
|
|
ManyErrors.AppendSanitized(errors, result.Error!);
|
|
break;
|
|
|
|
default: throw new ResultNotInitializedException(nameof(results));
|
|
}
|
|
}
|
|
|
|
return errors is null
|
|
? new(values?.ToImmutable() ?? Enumerable.Empty<T>())
|
|
: new(new ManyErrors(errors.ToImmutable()));
|
|
}
|
|
public static async Task<Result<IEnumerable<T>>> Merge<T>(this IEnumerable<Task<Result<T>>> tasks)
|
|
{
|
|
var taskList = tasks.ToArray();
|
|
var results = new Result<T>[taskList.Length];
|
|
|
|
// Wait for all tasks to complete (suppress AggregateException — we'll check each individually)
|
|
try { await Task.WhenAll(taskList).ConfigureAwait(false); } catch { /* handled per-task below */ }
|
|
|
|
for (int i = 0; i < taskList.Length; i++)
|
|
{
|
|
var task = taskList[i];
|
|
results[i] = task.Status switch
|
|
{
|
|
TaskStatus.RanToCompletion => task.Result,
|
|
TaskStatus.Faulted => Result.Failure<T>(task.Exception!),
|
|
TaskStatus.Canceled => Result.Failure<T>(Error.New("task_canceled", "Task was canceled")),
|
|
_ => Result.Failure<T>(Error.New("task_unexpected", $"Task in unexpected state: {task.Status}"))
|
|
};
|
|
}
|
|
|
|
return results.Merge();
|
|
}
|
|
|
|
#endregion
|
|
}
|