namespace CsharpEs.Library; // disable match exhaustion warnings - C# thinks we could have other // derived instances of Result but there's no way to say // "don't allow creation of further derived types" while leaving // the type publicly visible. #pragma warning disable CS8509 public abstract record Result { public static implicit operator Result(T value) => new ResultOk(value); public static implicit operator Result(E error) => new ResultFail(error); } public sealed record ResultOk(T Value) : Result; public sealed record ResultFail(E Error) : Result; public static class ResultModule { public static T Ok(T v) => v; public static Result OkNone() where T : class? => new ResultOk(null); public static E Fail(E e) => e; } public static class ResultExtensions { extension(Result result) { public bool IsSuccess => result is ResultOk; public bool IsFailure => !result.IsSuccess; public Result Bind(Func> binder) => result switch { ResultOk(var v) => binder(v), ResultFail(var e) => new ResultFail(e), }; public Result Map(Func mapper) => result switch { ResultOk(var v) => new ResultOk(mapper(v)), ResultFail(var e) => new ResultFail(e), }; public Result MapError(Func mapper) => result switch { ResultOk(var v) => new ResultOk(v), ResultFail(var e) => new ResultFail(mapper(e)), }; public Result Tap(Action action) { if (result is ResultOk(var v)) action(v); return result; } public Result TapError(Action action) { if (result is ResultFail(var e)) action(e); return result; } public Result Log(string src, string msg) => Tap(result, Logging.OutputDelegate(src, msg)) .TapError(m => Logging.OutputError(src, m)); public Result Log(string src, Func renderText) => Tap(result, x => Logging.OutputDelegate(src, renderText(x))(x)) .TapError(m => Logging.OutputError(src, m)); public Result Select(Func mapper) => Map(result, mapper); public Result SelectMany( Func> binder, Func projector ) => Bind(result, x => binder(x).Map(y => projector(x, y))); public TResult Match(Func onSuccess, Func onFailure) => result switch { ResultOk(var v) => onSuccess(v), ResultFail(var e) => onFailure(e), }; public void Switch(Action onSuccess, Action onFailure) { switch (result) { case ResultOk(var v): onSuccess(v); break; case ResultFail(var e): onFailure(e); break; } } } extension(Result) { public static Result Catch(Func f, Func exceptionMapper) { try { return new ResultOk(f()); } catch (Exception e) { return new ResultFail(exceptionMapper(e)); } } public static Result Catch( Func> f, Func exceptionMapper ) { try { return f(); } catch (Exception e) { return new ResultFail(exceptionMapper(e)); } } } }