120 lines
2.9 KiB
Go
120 lines
2.9 KiB
Go
// Package event is the event handler
|
|
package event
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
type Loop struct {
|
|
HandlerFunctions map[reflect.Type][]reflect.Value
|
|
Handlers []any
|
|
HandlerIndex int
|
|
}
|
|
|
|
type Unhandled struct {
|
|
Val any
|
|
}
|
|
|
|
type Anything struct {
|
|
Val any
|
|
}
|
|
|
|
type (
|
|
OnStart struct{}
|
|
Tick struct{}
|
|
)
|
|
|
|
// HandlerDoneErr will stop the event loop and return the error if provided.
|
|
type HandlerDoneErr struct {
|
|
Return error
|
|
}
|
|
|
|
func (e HandlerDoneErr) Error() string {
|
|
return "HandlerDoneErr"
|
|
}
|
|
|
|
func (e HandlerDoneErr) Unwrap() error {
|
|
return e.Return
|
|
}
|
|
|
|
// ErrDontForward will stop the event from being forwarded to other handlers, including anything handlers.
|
|
var ErrDontForward = errors.New("dont forward")
|
|
|
|
// SetHandlerFunctions initializes and populates the HandlerFunctions map by reflecting over methods of registered handlers.
|
|
func (l *Loop) SetHandlerFunctions() {
|
|
l.HandlerFunctions = make(map[reflect.Type][]reflect.Value)
|
|
for _, inst := range l.Handlers {
|
|
t := reflect.TypeOf(inst)
|
|
v := reflect.ValueOf(inst)
|
|
for i := 0; i < t.NumMethod(); i++ {
|
|
method := t.Method(i)
|
|
if strings.HasPrefix(method.Name, "On") {
|
|
if method.Type.NumIn() != 2 || method.Type.NumOut() != 1 || method.Type.Out(0) != reflect.TypeFor[error]() {
|
|
fmt.Println("Invalid event handler signature:", t.String(), ".", method.Name)
|
|
continue
|
|
}
|
|
} else {
|
|
continue
|
|
}
|
|
eventType := method.Type.In(1)
|
|
l.HandlerFunctions[eventType] = append(l.HandlerFunctions[eventType], v.Method(i))
|
|
slog.Debug("Registered Handler", "at", t.String()+"."+method.Name, "for", eventType.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
// FireFound triggers all handlers registered for the specified event type and returns if handlers were found or an error occurred.
|
|
func (l *Loop) FireFound(event any) (found bool, err error) {
|
|
var handlers []reflect.Value
|
|
if handlers, found = l.HandlerFunctions[reflect.TypeOf(event)]; found {
|
|
for _, handler := range handlers {
|
|
errV := handler.Call([]reflect.Value{reflect.ValueOf(event)})[0]
|
|
if !errV.IsNil() {
|
|
err = errV.Interface().(error)
|
|
err = errors.Join(fmt.Errorf("event handler failed at: %s(%T)", handler.Type().In(0), event), err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Fire dispatches the provided event to all appropriate handlers and invokes fallback handlers if the event is unhandled.
|
|
func (l *Loop) Fire(event any) (err error) {
|
|
found, err := l.FireFound(event)
|
|
if err != nil {
|
|
if errors.Is(err, ErrDontForward) {
|
|
err = nil
|
|
}
|
|
return
|
|
}
|
|
if !found {
|
|
_, err = l.FireFound(Unhandled{event})
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
_, err = l.FireFound(Anything{event})
|
|
return
|
|
}
|
|
|
|
func (l *Loop) Start() (err error) {
|
|
_ = *l // exit early on nil connection
|
|
l.SetHandlerFunctions()
|
|
err = l.Fire(OnStart{})
|
|
for err == nil {
|
|
err = l.Fire(Tick{})
|
|
if err != nil {
|
|
break
|
|
}
|
|
}
|
|
if errors.Is(err, HandlerDoneErr{}) {
|
|
err = errors.Unwrap(err)
|
|
}
|
|
return
|
|
}
|