Added TryFire

This allows handlers to return errors which will be returned to the caller.
Also replaced error returned from On with a panic.
This commit is contained in:
Leon Mika 2024-03-09 00:40:42 +00:00
parent a2269cd439
commit 91d1562e0e
3 changed files with 57 additions and 9 deletions

26
bus.go
View File

@ -8,7 +8,7 @@ func New() *Bus {
return &Bus{topics: make(map[string]*topic)} return &Bus{topics: make(map[string]*topic)}
} }
func (d *Bus) On(event string, receiver interface{}) error { func (d *Bus) On(event string, receiver interface{}) {
// TODO: make thread safe // TODO: make thread safe
t, hasTopic := d.topics[event] t, hasTopic := d.topics[event]
if !hasTopic { if !hasTopic {
@ -18,27 +18,43 @@ func (d *Bus) On(event string, receiver interface{}) error {
sub, err := newSubscriptionFromFunc(receiver) sub, err := newSubscriptionFromFunc(receiver)
if err != nil { if err != nil {
return err panic(err)
} }
t.addSubscriber(sub) t.addSubscriber(sub)
return nil
} }
func (d *Bus) Fire(event string, args ...interface{}) { func (d *Bus) Fire(event string, args ...interface{}) {
_ = d.TryFire(event, args...)
}
func (d *Bus) TryFire(event string, args ...interface{}) error {
// TODO: make thead safe // TODO: make thead safe
topic, hasTopic := d.topics[event] topic, hasTopic := d.topics[event]
if !hasTopic { if !hasTopic {
return return nil
} }
preparedArgs := prepareArgs(args) preparedArgs := prepareArgs(args)
var errs []error
for sub := topic.head; sub != nil; sub = sub.next { for sub := topic.head; sub != nil; sub = sub.next {
sub.handler.invoke(preparedArgs) err := sub.handler.invoke(preparedArgs)
if err != nil {
errs = append(errs, err)
} }
} }
switch len(errs) {
case 0:
return nil
case 1:
return errs[0]
}
return HandlerError{topic: event, errs: errs}
}
type topic struct { type topic struct {
head *subscription head *subscription
tail *subscription tail *subscription

16
errors.go Normal file
View File

@ -0,0 +1,16 @@
package events
import "fmt"
type HandlerError struct {
topic string
errs []error
}
func (h HandlerError) Error() string {
return fmt.Sprintf("caught %d errors from topic '%v': %v", len(h.errs), h.topic, h.errs)
}
func (h HandlerError) Unwrap() []error {
return h.errs
}

View File

@ -1,3 +1,4 @@
//go:build !tinygo
// +build !tinygo // +build !tinygo
package events package events
@ -21,7 +22,7 @@ func newReceiptHandler(receiver interface{}) (receiptHandler, error) {
return receiptHandler{receiverFunc: val, funcType: val.Type()}, nil return receiptHandler{receiverFunc: val, funcType: val.Type()}, nil
} }
func (rh *receiptHandler) invoke(values preparedArgs) { func (rh *receiptHandler) invoke(values preparedArgs) error {
args := make([]reflect.Value, rh.funcType.NumIn()) args := make([]reflect.Value, rh.funcType.NumIn())
for i := range args { for i := range args {
args[i] = reflect.Zero(rh.funcType.In(i)) args[i] = reflect.Zero(rh.funcType.In(i))
@ -32,7 +33,22 @@ func (rh *receiptHandler) invoke(values preparedArgs) {
} }
} }
rh.receiverFunc.Call(args) rets := rh.receiverFunc.Call(args)
if len(rets) == 0 {
return nil
}
lastRet := rets[len(rets)-1]
switch {
case lastRet.IsNil():
return nil
case lastRet.CanInterface():
err, ok := lastRet.Interface().(error)
if ok {
return err
}
}
return nil
} }
type preparedArgs []reflect.Value type preparedArgs []reflect.Value