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<T> Try<T>(Func<T> func) { Object wrapped; try { wrapped = func()!; } catch (Exception e) { wrapped = e; } return new Try<T>(wrapped); } public static async Task<Try<T>> Try<T>(Func<Task<T>> func) { Object wrapped; try { wrapped = (await func())!; } catch (Exception e) { wrapped = e; } return new Try<T>(wrapped); } public static Try<R> ThenTry<T, R>(this Try<T> @try, Func<T, Try<R>> func) { var wrapped = @try.Wrapped; if (wrapped is Exception e) return new Try<R>(e); return func((T) wrapped); } public static Try<R> ThenTry<T, R>(this Try<T> @try, Func<T, R> func) { var wrapped = @try.Wrapped; if (wrapped is Exception e) return new Try<R>(e); return Try(() => func((T) wrapped)); } public static Try<R> TryApply<T,R>(this T t, Func<T, R> func) { return Try(() => func(t)); } [MethodImpl(AggressiveInlining | AggressiveOptimization)] public static Try<R> TryApply<T1, T2, R>(this (T1 p1, T2 p2) t, Func<T1, T2, R> f) { return Try(() => f(t.p1, t.p2)); } [MethodImpl(AggressiveInlining | AggressiveOptimization)] public static Try<R> TryApply<T1, T2, T3, R>(this (T1 p1, T2 p2, T3 p3) t, Func<T1, T2, T3, R> f) { return Try(() => f(t.p1, t.p2, t.p3)); } // public static Try<Boolean> Try(Action action) // { // Boolean Func() // { // action(); // return true; // } // // return Try(Func); // } // public static Try<Boolean> TryApply<T>(this T t, Action<T> action) // { // return Try(() => action(t)); // } public static Try<T> OnErrorDo<T>(this Try<T> @try, Action<Exception> 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<T>(ex); } } return @try; } public static Try<T> OnErrorLog<T>(this Try<T> @try, String msg) // TODO: Func<String> { if (@try.Wrapped is Exception e) return new Try<T>(new Exception(msg, e)); return @try; } public static T Catch<T>(this Try<T> @try, T onError) { var wrapped = @try.Wrapped; if (wrapped is Exception) return onError; return (T) wrapped; } public static Try<T> Catch<T>(this Try<T> @try, Func<Exception, T> 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<T>(Func<T> func) { try { func(); return true; } catch { return false; } } } public readonly struct Try<T> // phantom type { internal readonly Object Wrapped; internal Try(Object wrapped) { Wrapped = wrapped; } }