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