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

28
bus.go
View file

@ -8,7 +8,7 @@ func New() *Bus {
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
t, hasTopic := d.topics[event]
if !hasTopic {
@ -18,25 +18,41 @@ func (d *Bus) On(event string, receiver interface{}) error {
sub, err := newSubscriptionFromFunc(receiver)
if err != nil {
return err
panic(err)
}
t.addSubscriber(sub)
return nil
}
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
topic, hasTopic := d.topics[event]
if !hasTopic {
return
return nil
}
preparedArgs := prepareArgs(args)
var errs []error
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 {
@ -65,4 +81,4 @@ func newSubscriptionFromFunc(fn interface{}) (*subscription, error) {
return nil, err
}
return &subscription{handler: handler, next: nil}, nil
}
}

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
package events
@ -21,7 +22,7 @@ func newReceiptHandler(receiver interface{}) (receiptHandler, error) {
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())
for i := range args {
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
@ -43,4 +59,4 @@ func prepareArgs(argIfacess []interface{}) preparedArgs {
values[i] = reflect.ValueOf(a)
}
return values
}
}