using Microsoft.Extensions.Logging;
using Speckle.InterfaceGenerator;
using Speckle.Sdk;
namespace Speckle.Connectors.DUI.Bridge;
///
/// The functions provided by this class are designed to be used in all "top level" scenarios (e.g. Plugin, UI, and Event callbacks)
/// To provide "last ditch effort" handling of unexpected exceptions that have not been handled.
/// 1. Log events to the injected
/// 2. Display a toast notification with exception details
///
///
///
/// exceptions cannot be recovered from.
/// They will be rethrown to allow the host app to run its handlers
/// Depending on the host app, this may trigger windows event logging, and recovery snapshots before ultimately terminating the process
/// Attempting to swallow them may lead to data corruption, deadlocking, or things worse than a managed host app crash.
///
[GenerateAutoInterface]
public sealed class TopLevelExceptionHandler : ITopLevelExceptionHandler
{
private readonly ILogger _logger;
public string Name => nameof(TopLevelExceptionHandler);
private const string UNHANDLED_LOGGER_TEMPLATE = "An unhandled Exception occured";
public TopLevelExceptionHandler(ILogger logger)
{
_logger = logger;
}
///
/// Invokes the given within a / block,
/// and provides exception handling for unexpected exceptions that have not been handled.
///
/// The function to invoke and provide error handling for
/// will be rethrown, these should be allowed to bubble up to the host app
///
public Result CatchUnhandled(Action function)
{
var r = CatchUnhandled(() =>
{
function();
return true;
});
if (r.IsSuccess)
{
return new Result();
}
return new Result(r.Exception);
}
///
/// return type
/// A result pattern struct (where exceptions have been handled)
public Result CatchUnhandled(Func function)
{
try
{
return new Result(function());
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogError(ex, UNHANDLED_LOGGER_TEMPLATE);
// _eventAggregator.GetEvent().PublishAsync(ex).Wait();
return new(ex);
}
catch (Exception ex)
{
_logger.LogCritical(ex, UNHANDLED_LOGGER_TEMPLATE);
throw;
}
}
///
/// A result pattern struct (where exceptions have been handled)
public async Task CatchUnhandledAsync(Func function)
{
var r = await CatchUnhandledAsync(async () =>
{
await function();
return true;
});
if (r.IsSuccess)
{
return new Result();
}
return new Result(r.Exception);
}
///
public async Task> CatchUnhandledAsync(Func> function)
{
try
{
try
{
return new(await function.Invoke());
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogError(ex, UNHANDLED_LOGGER_TEMPLATE);
// await _eventAggregator.GetEvent().PublishAsync(ex);
return new(ex);
}
}
catch (Exception ex)
{
_logger.LogCritical(ex, UNHANDLED_LOGGER_TEMPLATE);
throw;
}
}
///
/// Triggers an async action without explicitly needing to await it.
/// Any thrown by invoking will be handled by the
///
///
/// This function should only be used as an event handler that doesn't allow for handlers to return a
/// In cases where you can use keyword, you should prefer using
///
///
public async void FireAndForget(Func function) => await CatchUnhandledAsync(function).ConfigureAwait(false);
}