refactor event systems

This commit is contained in:
adman
2026-03-08 11:52:25 +01:00
parent 73d4e07b06
commit 2eee7bde24
12 changed files with 100 additions and 33 deletions
+22 -13
View File
@@ -10,9 +10,8 @@ import (
)
type Loop struct {
HandlerFunctions map[reflect.Type][]reflect.Value
handlerFunctions map[reflect.Type][]reflect.Value
Handlers []any
HandlerIndex int
}
type Unhandled struct {
@@ -24,8 +23,8 @@ type Anything struct {
}
type (
OnStart struct{}
Tick struct{}
Start struct{}
Tick struct{}
)
// HandlerDoneErr will stop the event loop and return the error if provided.
@@ -33,6 +32,11 @@ type HandlerDoneErr struct {
Return error
}
// Close is fired when the loop shuts down.
type Close struct {
Reason error
}
func (e HandlerDoneErr) Error() string {
return "HandlerDoneErr"
}
@@ -43,10 +47,11 @@ func (e HandlerDoneErr) Unwrap() error {
// ErrDontForward will stop the event from being forwarded to other handlers, including anything handlers.
var ErrDontForward = errors.New("dont forward")
var WhileClosingErr = errors.New("error while closing")
// 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)
// UpdateHandlerFunctions initializes and populates the handlerFunctions map by reflecting over methods of registered handlers.
func (l *Loop) UpdateHandlerFunctions() {
l.handlerFunctions = make(map[reflect.Type][]reflect.Value)
for _, inst := range l.Handlers {
t := reflect.TypeOf(inst)
v := reflect.ValueOf(inst)
@@ -54,14 +59,14 @@ func (l *Loop) SetHandlerFunctions() {
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)
slog.Warn("Invalid event handler signature", "at", t.String()+"."+method.Name)
continue
}
} else {
continue
}
eventType := method.Type.In(1)
l.HandlerFunctions[eventType] = append(l.HandlerFunctions[eventType], v.Method(i))
l.handlerFunctions[eventType] = append(l.handlerFunctions[eventType], v.Method(i))
slog.Debug("Registered Handler", "at", t.String()+"."+method.Name, "for", eventType.String())
}
}
@@ -70,7 +75,7 @@ func (l *Loop) SetHandlerFunctions() {
// 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 {
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() {
@@ -103,9 +108,9 @@ func (l *Loop) Fire(event any) (err error) {
}
func (l *Loop) Start() (err error) {
_ = *l // exit early on nil connection
l.SetHandlerFunctions()
err = l.Fire(OnStart{})
_ = *l // exit early on nil loop
l.UpdateHandlerFunctions()
err = l.Fire(Start{})
for err == nil {
err = l.Fire(Tick{})
if err != nil {
@@ -115,5 +120,9 @@ func (l *Loop) Start() (err error) {
if errors.Is(err, HandlerDoneErr{}) {
err = errors.Unwrap(err)
}
closeErr := l.Fire(Close{err})
if closeErr != nil {
err = errors.Join(err, errors.Join(WhileClosingErr, closeErr))
}
return
}
+51
View File
@@ -0,0 +1,51 @@
package event
import (
"bufio"
"context"
"os"
)
type Stdin struct {
Val string
}
type StdinHandler struct {
*Loop
stdInChan chan Stdin
ctx context.Context
cancel context.CancelFunc
}
func (s *StdinHandler) OnTick(_ Tick) (err error) {
select {
case stdin := <-s.stdInChan:
err = s.Fire(Stdin{stdin.Val})
default:
}
return
}
func (s *StdinHandler) ScanJob() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
select {
case <-s.ctx.Done():
return
default:
s.stdInChan <- Stdin{scanner.Text()}
}
}
}
func (s *StdinHandler) OnStart(_ Start) (err error) {
s.stdInChan = make(chan Stdin)
s.ctx, s.cancel = context.WithCancel(context.Background())
go s.ScanJob()
return
}
func (s *StdinHandler) OnClose(_ Close) (err error) {
s.cancel()
return
}
+63
View File
@@ -0,0 +1,63 @@
package event
import (
"slices"
"time"
)
type IntervalTask struct {
F func() error
LastTrigger time.Time
Interval time.Duration
}
type OnceTask struct {
F func() error
Trigger time.Time
}
type Timer struct {
Once []OnceTask
Interval []IntervalTask
Active bool
}
func (t *Timer) Tick() (err error) {
if !t.Active {
return
}
for i, task := range t.Interval {
if time.Since(task.LastTrigger) >= task.Interval {
task.LastTrigger = time.Now()
err = task.F()
if err != nil {
return
}
}
t.Interval[i] = task
}
for i, task := range t.Once {
if task.Trigger.After(time.Now()) {
err = task.F()
if err != nil {
return
}
slices.Delete(t.Once, i, i)
}
}
return
}
func (t *Timer) Every(interval time.Duration, f func() error) {
t.Interval = append(t.Interval, IntervalTask{
F: f,
Interval: interval,
})
}
func (t *Timer) In(interval time.Duration, f func() error) {
t.Once = append(t.Once, OnceTask{
F: f,
Trigger: time.Now().Add(interval),
})
}