1
0
Fork 0
mirror of https://github.com/diamondburned/arikawa.git synced 2025-11-29 15:56:48 +00:00

Handler: Added support for channel event handlers

This commit is contained in:
diamondburned 2020-07-14 23:57:50 -07:00
parent 9d7f5cb953
commit 18024526fe
2 changed files with 89 additions and 39 deletions

View file

@ -49,6 +49,8 @@ func New() *Handler {
} }
} }
// Call calls all handlers with the given event. This is an internal method; use
// with care.
func (h *Handler) Call(ev interface{}) { func (h *Handler) Call(ev interface{}) {
var evV = reflect.ValueOf(ev) var evV = reflect.ValueOf(ev)
var evT = evV.Type() var evT = evV.Type()
@ -127,6 +129,9 @@ func (h *Handler) WaitFor(ctx context.Context, fn func(interface{}) bool) interf
// ChanFor returns a channel that would receive all incoming events that match // ChanFor returns a channel that would receive all incoming events that match
// the callback given. The cancel() function removes the handler and drops all // the callback given. The cancel() function removes the handler and drops all
// hanging goroutines. // hanging goroutines.
//
// This method is more intended to be used as a filter. For a persistent event
// channel, consider adding it directly as a handler with AddHandler.
func (h *Handler) ChanFor(fn func(interface{}) bool) (out <-chan interface{}, cancel func()) { func (h *Handler) ChanFor(fn func(interface{}) bool) (out <-chan interface{}, cancel func()) {
result := make(chan interface{}) result := make(chan interface{})
closer := make(chan struct{}) closer := make(chan struct{})
@ -154,15 +159,23 @@ func (h *Handler) ChanFor(fn func(interface{}) bool) (out <-chan interface{}, ca
} }
// AddHandler adds the handler, returning a function that would remove this // AddHandler adds the handler, returning a function that would remove this
// handler when called. // handler when called. A handler type is either a single-argument no-return
// function or a channel.
// //
// GuildCreateEvents and GuildDeleteEvents will not be called on interface{} // Function
// events. Instead their situation-specific version will be fired, as they //
// provides more information about the context of the event: // A handler can be a function with a single argument that is the expected event
// GuildReadyEvent, GuildAvailableEvent, GuildJoinEvent, GuildUnavailableEvent // type. It must not have any returns or any other number of arguments.
// or GuildLeaveEvent //
// Listening to directly to GuildCreateEvent or GuildDeleteEvent will still // Channel
// work, however. //
// A handler can also be a channel. The underlying type that the channel wraps
// around will be the event type. As such, the type rules are the same as
// function handlers.
//
// Keep in mind that the user must NOT close the channel. In fact, the channel
// should not be closed at all. The caller function WILL PANIC if the channel is
// closed!
func (h *Handler) AddHandler(handler interface{}) (rm func()) { func (h *Handler) AddHandler(handler interface{}) (rm func()) {
rm, err := h.addHandler(handler) rm, err := h.addHandler(handler)
if err != nil { if err != nil {
@ -172,15 +185,7 @@ func (h *Handler) AddHandler(handler interface{}) (rm func()) {
} }
// AddHandlerCheck adds the handler, but safe-guards reflect panics with a // AddHandlerCheck adds the handler, but safe-guards reflect panics with a
// recoverer, returning the error. // recoverer, returning the error. Refer to AddHandler for more information.
//
// GuildCreateEvents and GuildDeleteEvents will not be called on interface{}
// events. Instead their situation-specific version will be fired, as they
// provides more information about the context of the event:
// GuildReadyEvent, GuildAvailableEvent, GuildJoinEvent, GuildUnavailableEvent
// or GuildLeaveEvent
// Listening to directly to GuildCreateEvent or GuildDeleteEvent will still
// work, however.
func (h *Handler) AddHandlerCheck(handler interface{}) (rm func(), err error) { func (h *Handler) AddHandlerCheck(handler interface{}) (rm func(), err error) {
// Reflect would actually panic if anything goes wrong, so this is just in // Reflect would actually panic if anything goes wrong, so this is just in
// case. // case.
@ -199,7 +204,7 @@ func (h *Handler) AddHandlerCheck(handler interface{}) (rm func(), err error) {
func (h *Handler) addHandler(fn interface{}) (rm func(), err error) { func (h *Handler) addHandler(fn interface{}) (rm func(), err error) {
// Reflect the handler // Reflect the handler
r, err := reflectFn(fn) r, err := newHandler(fn)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "handler reflect failed") return nil, errors.Wrap(err, "handler reflect failed")
} }
@ -240,19 +245,25 @@ func (h *Handler) addHandler(fn interface{}) (rm func(), err error) {
} }
type handler struct { type handler struct {
event reflect.Type event reflect.Type // underlying type; arg0 or chan underlying type
callback reflect.Value callback reflect.Value
isChan bool
isIface bool isIface bool
} }
func reflectFn(function interface{}) (*handler, error) { // newHandler reflects either a channel or a function into a handler. A function
fnV := reflect.ValueOf(function) // must only have a single argument being the event and no return, and a channel
// must have the event type as the underlying type.
func newHandler(unknown interface{}) (*handler, error) {
fnV := reflect.ValueOf(unknown)
fnT := fnV.Type() fnT := fnV.Type()
if fnT.Kind() != reflect.Func { // underlying event type
return nil, errors.New("given interface is not a function") var argT reflect.Type
} var isch bool
switch fnT.Kind() {
case reflect.Func:
if fnT.NumIn() != 1 { if fnT.NumIn() != 1 {
return nil, errors.New("function can only accept 1 event as argument") return nil, errors.New("function can only accept 1 event as argument")
} }
@ -261,8 +272,17 @@ func reflectFn(function interface{}) (*handler, error) {
return nil, errors.New("function can't accept returns") return nil, errors.New("function can't accept returns")
} }
argT := fnT.In(0) argT = fnT.In(0)
kind := argT.Kind()
case reflect.Chan:
argT = fnT.Elem()
isch = true
default:
return nil, errors.New("given interface is not a function or channel")
}
var kind = argT.Kind()
// Accept either pointer type or interface{} type // Accept either pointer type or interface{} type
if kind != reflect.Ptr && kind != reflect.Interface { if kind != reflect.Ptr && kind != reflect.Interface {
@ -272,6 +292,7 @@ func reflectFn(function interface{}) (*handler, error) {
return &handler{ return &handler{
event: argT, event: argT,
callback: fnV, callback: fnV,
isChan: isch,
isIface: kind == reflect.Interface, isIface: kind == reflect.Interface,
}, nil }, nil
} }
@ -285,5 +306,9 @@ func (h handler) not(event reflect.Type) bool {
} }
func (h handler) call(event reflect.Value) { func (h handler) call(event reflect.Value) {
if h.isChan {
h.callback.Send(event)
} else {
h.callback.Call([]reflect.Value{event}) h.callback.Call([]reflect.Value{event})
}
} }

View file

@ -63,7 +63,7 @@ func TestCall(t *testing.T) {
func TestHandler(t *testing.T) { func TestHandler(t *testing.T) {
var results = make(chan string) var results = make(chan string)
h, err := reflectFn(func(m *gateway.MessageCreateEvent) { h, err := newHandler(func(m *gateway.MessageCreateEvent) {
results <- m.Content results <- m.Content
}) })
if err != nil { if err != nil {
@ -87,10 +87,35 @@ func TestHandler(t *testing.T) {
} }
} }
func TestHandlerChan(t *testing.T) {
var results = make(chan *gateway.MessageCreateEvent)
h, err := newHandler(results)
if err != nil {
t.Fatal(err)
}
const result = "Hime Arikawa"
var msg = newMessage(result)
var msgV = reflect.ValueOf(msg)
var msgT = msgV.Type()
if h.not(msgT) {
t.Fatal("Event type mismatch")
}
go h.call(msgV)
if results := <-results; results.Content != result {
t.Fatal("Unexpected results:", results)
}
}
func TestHandlerInterface(t *testing.T) { func TestHandlerInterface(t *testing.T) {
var results = make(chan interface{}) var results = make(chan interface{})
h, err := reflectFn(func(m interface{}) { h, err := newHandler(func(m interface{}) {
results <- m results <- m
}) })
if err != nil { if err != nil {
@ -121,7 +146,7 @@ func TestHandlerInterface(t *testing.T) {
t.Fatal("Assertion failed:", recv) t.Fatal("Assertion failed:", recv)
} }
func TestHandlerWait(t *testing.T) { func TestHandlerWaitFor(t *testing.T) {
inc := make(chan interface{}, 1) inc := make(chan interface{}, 1)
h := New() h := New()
@ -173,7 +198,7 @@ func TestHandlerWait(t *testing.T) {
} }
} }
func TestHandlerChan(t *testing.T) { func TestHandlerChanFor(t *testing.T) {
h := New() h := New()
wanted := &gateway.TypingStartEvent{ wanted := &gateway.TypingStartEvent{
@ -208,7 +233,7 @@ func TestHandlerChan(t *testing.T) {
} }
func BenchmarkReflect(b *testing.B) { func BenchmarkReflect(b *testing.B) {
h, err := reflectFn(func(m *gateway.MessageCreateEvent) {}) h, err := newHandler(func(m *gateway.MessageCreateEvent) {})
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }