namespace CsharpEs.Library; public readonly record struct Result { private readonly T _value; private readonly E _error; public bool IsSuccess { get; } public bool IsFailure => !IsSuccess; public T Value => IsSuccess ? _value : throw new InvalidOperationException("No value present for failed result."); public E Error => IsFailure ? _error : throw new InvalidOperationException("No error present for successful result."); private Result(T value) { _value = value; _error = default!; IsSuccess = true; } private Result(E error) { _value = default!; _error = error; IsSuccess = false; } public static Result Ok(T value) => new(value); public static Result Fail(E error) => new(error); public TResult Match(Func onSuccess, Func onFailure) => IsSuccess ? onSuccess(_value) : onFailure(_error); public void Switch(Action onSuccess, Action onFailure) { if (IsSuccess) onSuccess(_value); else onFailure(_error); } public static Result Catch(Func f, Func exceptionMapper) { try { var result = f(); return Result.Ok(result); } catch (Exception e) { return Result.Fail(exceptionMapper(e)); } } public static Result Catch(Func> f, Func exceptionMapper) { try { return f(); } catch (Exception e) { return Result.Fail(exceptionMapper(e)); } } } public static class ResultExtensions { public static Result Bind( this Result result, Func> binder ) => result.IsSuccess ? binder(result.Value) : Result.Fail(result.Error); public static Result Map( this Result result, Func mapper ) => result.IsSuccess ? Result.Ok(mapper(result.Value)) : Result.Fail(result.Error); public static Result Tap(this Result result, Action action) { if (result.IsSuccess) action(result.Value); return result; } public static Result TapError(this Result result, Action action) { if (result.IsFailure) action(result.Error); return result; } public static Result MapError( this Result result, Func map ) => result.IsSuccess ? Result.Ok(result.Value) : Result.Fail(map(result.Error)); public static Result Log(this Result result, string src, string msg) => Tap(result, Logging.OutputDelegate(src, msg)).TapError(m => Logging.OutputError(src, m)); public static Result Log( this Result result, string src, Func renderText ) => Tap(result, x => Logging.OutputDelegate(src, renderText(x))(x)) .TapError(m => Logging.OutputError(src, m)); public static Result Select( this Result result, Func mapper ) => result.Map(mapper); public static Result SelectMany( this Result result, Func> binder, Func projector ) => result.Bind(x => binder(x).Map(y => projector(x, y))); }