2022-07-06 23:19:05 +02:00
|
|
|
package hook
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
var StopPropagation = errors.New("Event hook propagation stopped")
|
|
|
|
|
|
|
|
// Handler defines a hook handler function.
|
2022-11-14 19:30:13 +02:00
|
|
|
type Handler[T any] func(e T) error
|
2022-07-06 23:19:05 +02:00
|
|
|
|
|
|
|
// Hook defines a concurrent safe structure for handling event hooks
|
|
|
|
// (aka. callbacks propagation).
|
|
|
|
type Hook[T any] struct {
|
|
|
|
mux sync.RWMutex
|
|
|
|
handlers []Handler[T]
|
|
|
|
}
|
|
|
|
|
2022-09-21 13:41:20 +02:00
|
|
|
// PreAdd registers a new handler to the hook by prepending it to the existing queue.
|
|
|
|
func (h *Hook[T]) PreAdd(fn Handler[T]) {
|
|
|
|
h.mux.Lock()
|
|
|
|
defer h.mux.Unlock()
|
|
|
|
|
|
|
|
// minimize allocations by shifting the slice
|
|
|
|
h.handlers = append(h.handlers, nil)
|
|
|
|
copy(h.handlers[1:], h.handlers)
|
|
|
|
h.handlers[0] = fn
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add registers a new handler to the hook by appending it to the existing queue.
|
2022-07-06 23:19:05 +02:00
|
|
|
func (h *Hook[T]) Add(fn Handler[T]) {
|
|
|
|
h.mux.Lock()
|
|
|
|
defer h.mux.Unlock()
|
|
|
|
|
|
|
|
h.handlers = append(h.handlers, fn)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset removes all registered handlers.
|
|
|
|
func (h *Hook[T]) Reset() {
|
|
|
|
h.mux.Lock()
|
|
|
|
defer h.mux.Unlock()
|
|
|
|
|
|
|
|
h.handlers = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Trigger executes all registered hook handlers one by one
|
|
|
|
// with the specified `data` as an argument.
|
|
|
|
//
|
|
|
|
// Optionally, this method allows also to register additional one off
|
|
|
|
// handlers that will be temporary appended to the handlers queue.
|
|
|
|
//
|
|
|
|
// The execution stops when:
|
|
|
|
// - hook.StopPropagation is returned in one of the handlers
|
|
|
|
// - any non-nil error is returned in one of the handlers
|
|
|
|
func (h *Hook[T]) Trigger(data T, oneOffHandlers ...Handler[T]) error {
|
2022-10-30 10:28:14 +02:00
|
|
|
h.mux.RLock()
|
2022-07-14 21:35:57 +02:00
|
|
|
handlers := make([]Handler[T], 0, len(h.handlers)+len(oneOffHandlers))
|
|
|
|
handlers = append(handlers, h.handlers...)
|
|
|
|
handlers = append(handlers, oneOffHandlers...)
|
2022-07-11 15:16:01 +02:00
|
|
|
// unlock is not deferred to avoid deadlocks when Trigger is called recursive by the handlers
|
2022-10-30 10:28:14 +02:00
|
|
|
h.mux.RUnlock()
|
2022-07-06 23:19:05 +02:00
|
|
|
|
|
|
|
for _, fn := range handlers {
|
|
|
|
err := fn(data)
|
|
|
|
if err == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if errors.Is(err, StopPropagation) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|