From c1315a827e2bbbf543e59b6b8840b00b01956be8 Mon Sep 17 00:00:00 2001 From: "Gustavo L de Mello (Guz)" Date: Mon, 27 Jan 2025 10:05:26 -0300 Subject: [PATCH] feat(blogo,plugins): multi error handler --- blogo/plugins/multi_error_handler.go | 111 +++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 blogo/plugins/multi_error_handler.go diff --git a/blogo/plugins/multi_error_handler.go b/blogo/plugins/multi_error_handler.go new file mode 100644 index 0000000..23a4648 --- /dev/null +++ b/blogo/plugins/multi_error_handler.go @@ -0,0 +1,111 @@ +// Copyright 2025-present Gustavo "Guz" L. de Mello +// Copyright 2025-present The Lored.dev Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugins + +import ( + "io" + "log/slog" + + "forge.capytal.company/loreddev/x/blogo/plugin" + "forge.capytal.company/loreddev/x/tinyssert" +) + +const multiErrorHandlerName = "blogo-multierrorhandler-errorhandler" + +func NewMultiErrorHandler(opts ...MultiErrorHandlerOpts) MultiErrorHandler { + opt := MultiErrorHandlerOpts{} + if len(opts) > 0 { + opt = opts[0] + } + + if opt.Assertions == nil { + opt.Assertions = tinyssert.NewDisabledAssertions() + } + if opt.Logger == nil { + opt.Logger = slog.New(slog.NewTextHandler(io.Discard, &slog.HandlerOptions{})) + } + + return &multiErrorHandler{ + handlers: []plugin.ErrorHandler{}, + + assert: opt.Assertions, + log: opt.Logger, + } +} + +type MultiErrorHandler interface { + plugin.ErrorHandler + plugin.WithPlugins +} + +type MultiErrorHandlerOpts struct { + Assertions tinyssert.Assertions + Logger *slog.Logger +} + +type multiErrorHandler struct { + handlers []plugin.ErrorHandler + + assert tinyssert.Assertions + log *slog.Logger +} + +func (h *multiErrorHandler) Name() string { + return multiErrorHandlerName +} + +func (h *multiErrorHandler) Use(p plugin.Plugin) { + h.assert.NotNil(h.handlers, "Error handlers slice should not be nil") + h.assert.NotNil(h.log) + + log := h.log.With(slog.String("plugin", p.Name())) + log.Debug("Adding plugin") + + if p, ok := p.(plugin.Group); ok { + log.Debug("Plugin is a group, using children plugins") + for _, p := range p.Plugins() { + h.Use(p) + } + } + + if p, ok := p.(plugin.ErrorHandler); ok { + h.handlers = append(h.handlers, p) + } else { + log.Debug("Plugin does not implement ErrorHandler, ignoring") + } +} + +func (h *multiErrorHandler) Handle(err error) (recovr any, handled bool) { + h.assert.NotNil(h.handlers, "Error handlers slice should not be nil") + h.assert.NotNil(h.log) + + log := h.log.With(slog.String("err", err.Error())) + log.Debug("Handling error") + + for _, handler := range h.handlers { + log := log.With(slog.String("plugin", handler.Name())) + log.Debug("Handling error with plugin") + + recovr, ok := handler.Handle(err) + if ok { + log.Debug("Error successfully handled with plugin") + return recovr, ok + } + } + + log.Debug("Failed to handle error with any plugin") + return nil, false +}