package hook import ( "datamanager/backend/tools/security" "errors" "fmt" "sync" ) type Handler[T any] func(e T) error type handlerPair[T any] struct { id string handler Handler[T] } type Hook[T any] struct { mux sync.RWMutex handlers []*handlerPair[T] } var StopPropagation = errors.New("event hook propagation stopped") func (h *Hook[T]) Trigger(data T, oneOffHandlers ...Handler[T]) error { h.mux.RLock() handlers := make([]*handlerPair[T], 0, len(h.handlers)+len(oneOffHandlers)) handlers = append(handlers, h.handlers...) // append the one off handlers for i, oneOff := range oneOffHandlers { handlers = append(handlers, &handlerPair[T]{ id: fmt.Sprintf("@%d", i), handler: oneOff, }) } // unlock is not deferred to avoid deadlocks in case Trigger // is called recursively by the handlers h.mux.RUnlock() for _, item := range handlers { err := item.handler(data) if err == nil { continue } if errors.Is(err, StopPropagation) { return nil } return err } return nil } func generateHookId() string { return security.PseudorandomString(8) }