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