using System.Runtime.CompilerServices; using static System.Runtime.CompilerServices.MethodImplOptions; namespace InnovEnergy.Lib.Utils; public static class ExceptionHandling { // TODO: https://blog.stephencleary.com/2020/06/a-new-pattern-for-exception-logging.html public static Try Try(Func func) { Object wrapped; try { wrapped = func()!; } catch (Exception e) { wrapped = e; } return new Try(wrapped); } public static async Task> Try(Func> func) { Object wrapped; try { wrapped = (await func())!; } catch (Exception e) { wrapped = e; } return new Try(wrapped); } public static Try ThenTry(this Try @try, Func> func) { var wrapped = @try.Wrapped; if (wrapped is Exception e) return new Try(e); return func((T) wrapped); } public static Try ThenTry(this Try @try, Func func) { var wrapped = @try.Wrapped; if (wrapped is Exception e) return new Try(e); return Try(() => func((T) wrapped)); } public static Try TryApply(this T t, Func func) { return Try(() => func(t)); } [MethodImpl(AggressiveInlining | AggressiveOptimization)] public static Try TryApply(this (T1 p1, T2 p2) t, Func f) { return Try(() => f(t.p1, t.p2)); } [MethodImpl(AggressiveInlining | AggressiveOptimization)] public static Try TryApply(this (T1 p1, T2 p2, T3 p3) t, Func f) { return Try(() => f(t.p1, t.p2, t.p3)); } // public static Try Try(Action action) // { // Boolean Func() // { // action(); // return true; // } // // return Try(Func); // } // public static Try TryApply(this T t, Action action) // { // return Try(() => action(t)); // } public static Try OnErrorDo(this Try @try, Action onError) { if (@try.Wrapped is Exception originalException) { try { onError(originalException); } catch (Exception handlerException) // make absolutely sure no exception can escape, { // even if the handler fails const String msg = nameof(OnErrorDo) + " handler failed"; var ex = new AggregateException(msg, handlerException, originalException); return new Try(ex); } } return @try; } public static Try OnErrorLog(this Try @try, String msg) // TODO: Func { if (@try.Wrapped is Exception e) return new Try(new Exception(msg, e)); return @try; } public static T Catch(this Try @try, T onError) { var wrapped = @try.Wrapped; if (wrapped is Exception) return onError; return (T) wrapped; } public static Try Catch(this Try @try, Func onError) { var wrapped = @try.Wrapped; if (wrapped is Exception e) return Try(() => onError(e)); return @try; } public static Boolean Succeeds(Action action) { try { action(); return true; } catch { return false; } } public static Boolean Succeeds(Func func) { try { func(); return true; } catch { return false; } } } public readonly struct Try // phantom type { internal readonly Object Wrapped; internal Try(Object wrapped) { Wrapped = wrapped; } }