using System.Collections.Immutable; namespace Just.Railway; public static partial class ResultExtensions { #region Match (with fallback) public static T Match(this in Result result, Func fallback) { return result.State switch { ResultState.Success => result.Value, ResultState.Error => fallback(result.Error!), _ => throw new ResultNotInitializedException(nameof(result)) }; } public static async Task Match(this Result result, Func> 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 Match(this Task> resultTask, Func 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 Match(this Task> resultTask, Func> 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)) }; } public static async ValueTask Match(this Result result, Func> 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 ValueTask Match(this ValueTask> resultTask, Func 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 ValueTask Match(this ValueTask> resultTask, Func> 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 results) { ImmutableArray.Builder? errors = null; bool hasErrors = false; foreach (var result in results.OrderBy(x => x.State)) { switch (result.State) { case ResultState.Error: hasErrors = true; errors ??= ImmutableArray.CreateBuilder(); ManyErrors.AppendSanitized(errors, result.Error!); break; case ResultState.Success: if (hasErrors) goto afterLoop; break; default: throw new ResultNotInitializedException(nameof(results)); } } afterLoop: return hasErrors ? new(new ManyErrors(errors!.ToImmutable())) : new(null); } public static async Task Merge(this IEnumerable> tasks) { var results = await Task.WhenAll(tasks).ConfigureAwait(false); return results.Merge(); } public static Result> Merge(this IEnumerable> results) { ImmutableList.Builder? values = null; ImmutableArray.Builder? errors = null; bool hasErrors = false; foreach (var result in results.OrderBy(x => x.State)) { switch (result.State) { case ResultState.Error: hasErrors = true; errors ??= ImmutableArray.CreateBuilder(); ManyErrors.AppendSanitized(errors, result.Error!); break; case ResultState.Success: if (hasErrors) goto afterLoop; values ??= ImmutableList.CreateBuilder(); values.Add(result.Value); break; default: throw new ResultNotInitializedException(nameof(results)); } } afterLoop: return hasErrors ? new(new ManyErrors(errors!.ToImmutable())) : new(values is not null ? values.ToImmutable() : ImmutableList.Empty); } public static async Task>> Merge(this IEnumerable>> tasks) { var results = await Task.WhenAll(tasks).ConfigureAwait(false); return results.Merge(); } #endregion }