Innovenergy_trunk/csharp/Lib/Utils/ExceptionHandling.cs

185 lines
4.0 KiB
C#

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;
}
}