Initial commit
This commit is contained in:
commit
5b42d91781
68
dispatcher.go
Normal file
68
dispatcher.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
package events
|
||||
|
||||
type Dispatcher struct {
|
||||
topics map[string]*topic
|
||||
}
|
||||
|
||||
func New() *Dispatcher {
|
||||
return &Dispatcher{topics: make(map[string]*topic)}
|
||||
}
|
||||
|
||||
func (d *Dispatcher) On(event string, receiver interface{}) error {
|
||||
// TODO: make thread safe
|
||||
t, hasTopic := d.topics[event]
|
||||
if !hasTopic {
|
||||
t = &topic{}
|
||||
d.topics[event] = t
|
||||
}
|
||||
|
||||
sub, err := newSubscriptionFromFunc(receiver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.addSubscriber(sub)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Dispatcher) Fire(event string, args ...interface{}) {
|
||||
// TODO: make thead safe
|
||||
topic, hasTopic := d.topics[event]
|
||||
if !hasTopic {
|
||||
return
|
||||
}
|
||||
|
||||
preparedArgs := prepareArgs(args)
|
||||
|
||||
for sub := topic.head; sub != nil; sub = sub.next {
|
||||
sub.handler.invoke(preparedArgs)
|
||||
}
|
||||
}
|
||||
|
||||
type topic struct {
|
||||
head *subscription
|
||||
tail *subscription
|
||||
}
|
||||
|
||||
func (t *topic) addSubscriber(sub *subscription) {
|
||||
if t.head == nil {
|
||||
t.head = sub
|
||||
}
|
||||
if t.tail != nil {
|
||||
t.tail.next = sub
|
||||
}
|
||||
t.tail = sub
|
||||
}
|
||||
|
||||
type subscription struct {
|
||||
handler receiptHandler
|
||||
next *subscription
|
||||
}
|
||||
|
||||
func newSubscriptionFromFunc(fn interface{}) (*subscription, error) {
|
||||
handler, err := newReceiptHandler(fn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &subscription{handler: handler, next: nil}, nil
|
||||
}
|
38
dispatcher_test.go
Normal file
38
dispatcher_test.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package events
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNew_Lifecycle(t *testing.T) {
|
||||
receives := make([][]int, 0)
|
||||
|
||||
d := New()
|
||||
|
||||
d.On("event", func(x int, y int) { receives = append(receives, []int{1, x, y}) })
|
||||
d.On("event", func(x int) { receives = append(receives, []int{2, x}) })
|
||||
d.On("event", func(x int, y string, z string) { receives = append(receives, []int{3, x, len(y)}) })
|
||||
|
||||
d.Fire("event", 123, 123)
|
||||
d.Fire("event", 234, 234)
|
||||
d.Fire("event", "string", "value")
|
||||
|
||||
assertEquals(t, [][]int{
|
||||
{1, 123, 123},
|
||||
{2, 123},
|
||||
{3, 123, 0},
|
||||
{1, 234, 234},
|
||||
{2, 234},
|
||||
{3, 234, 0},
|
||||
{1, 0, 0},
|
||||
{2, 0},
|
||||
{3, 0, 5},
|
||||
}, receives)
|
||||
}
|
||||
|
||||
func assertEquals(t testing.TB, expected interface{}, actual interface{}) {
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("Expected %v but was %v", expected, actual)
|
||||
}
|
||||
}
|
10
go.sum
Normal file
10
go.sum
Normal file
|
@ -0,0 +1,10 @@
|
|||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
46
handler.go
Normal file
46
handler.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
// +build !tinygo
|
||||
|
||||
package events
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type receiptHandler struct {
|
||||
receiverFunc reflect.Value
|
||||
funcType reflect.Type
|
||||
}
|
||||
|
||||
func newReceiptHandler(receiver interface{}) (receiptHandler, error) {
|
||||
val := reflect.ValueOf(receiver)
|
||||
if val.Type().Kind() != reflect.Func {
|
||||
return receiptHandler{}, errors.New("not a function")
|
||||
}
|
||||
|
||||
return receiptHandler{receiverFunc: val, funcType: val.Type()}, nil
|
||||
}
|
||||
|
||||
func (rh *receiptHandler) invoke(values preparedArgs) {
|
||||
args := make([]reflect.Value, rh.funcType.NumIn())
|
||||
for i := range args {
|
||||
args[i] = reflect.Zero(rh.funcType.In(i))
|
||||
if i < len(values) {
|
||||
if rh.funcType.In(i).AssignableTo(values[i].Type()) {
|
||||
args[i] = values[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rh.receiverFunc.Call(args)
|
||||
}
|
||||
|
||||
type preparedArgs []reflect.Value
|
||||
|
||||
func prepareArgs(argIfacess []interface{}) preparedArgs {
|
||||
values := make([]reflect.Value, len(argIfacess))
|
||||
for i, a := range argIfacess {
|
||||
values[i] = reflect.ValueOf(a)
|
||||
}
|
||||
return values
|
||||
}
|
42
handler_tinygo.go
Normal file
42
handler_tinygo.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
// +build tinygo
|
||||
|
||||
package events
|
||||
|
||||
import "errors"
|
||||
|
||||
type receiverType int
|
||||
const (
|
||||
receiverTypeUnknown receiverType = iota
|
||||
receiverTypeFuncNoArgs
|
||||
)
|
||||
|
||||
type receiptHandler struct {
|
||||
receiver interface{}
|
||||
rt receiverType
|
||||
}
|
||||
|
||||
func newReceiptHandler(receiver interface{}) (receiptHandler, error) {
|
||||
var rt receiverType = 0
|
||||
|
||||
switch receiver.(type) {
|
||||
case func():
|
||||
rt = receiverTypeFuncNoArgs
|
||||
default:
|
||||
return receiptHandler{}, errors.New("unsupported receiver type")
|
||||
}
|
||||
|
||||
return receiptHandler{receiver: receiver, rt: rt}, nil
|
||||
}
|
||||
|
||||
func (rh *receiptHandler) invoke(values preparedArgs) {
|
||||
switch rh.rt {
|
||||
case receiverTypeFuncNoArgs:
|
||||
rh.receiver.(func())()
|
||||
}
|
||||
}
|
||||
|
||||
type preparedArgs []interface{}
|
||||
|
||||
func prepareArgs(argIfacess []interface{}) preparedArgs {
|
||||
return argIfacess
|
||||
}
|
Loading…
Reference in a new issue