mirror of
https://github.com/diamondburned/arikawa.git
synced 2025-11-27 06:35:48 +00:00
Handler: Added support for channel event handlers
This commit is contained in:
parent
9d7f5cb953
commit
18024526fe
|
|
@ -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{}) {
|
||||
var evV = reflect.ValueOf(ev)
|
||||
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
|
||||
// the callback given. The cancel() function removes the handler and drops all
|
||||
// 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()) {
|
||||
result := make(chan interface{})
|
||||
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
|
||||
// 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{}
|
||||
// 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.
|
||||
// Function
|
||||
//
|
||||
// A handler can be a function with a single argument that is the expected event
|
||||
// type. It must not have any returns or any other number of arguments.
|
||||
//
|
||||
// Channel
|
||||
//
|
||||
// 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()) {
|
||||
rm, err := h.addHandler(handler)
|
||||
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
|
||||
// recoverer, returning the error.
|
||||
//
|
||||
// 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.
|
||||
// recoverer, returning the error. Refer to AddHandler for more information.
|
||||
func (h *Handler) AddHandlerCheck(handler interface{}) (rm func(), err error) {
|
||||
// Reflect would actually panic if anything goes wrong, so this is just in
|
||||
// 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) {
|
||||
// Reflect the handler
|
||||
r, err := reflectFn(fn)
|
||||
r, err := newHandler(fn)
|
||||
if err != nil {
|
||||
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 {
|
||||
event reflect.Type
|
||||
event reflect.Type // underlying type; arg0 or chan underlying type
|
||||
callback reflect.Value
|
||||
isChan bool
|
||||
isIface bool
|
||||
}
|
||||
|
||||
func reflectFn(function interface{}) (*handler, error) {
|
||||
fnV := reflect.ValueOf(function)
|
||||
// newHandler reflects either a channel or a function into a handler. A 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()
|
||||
|
||||
if fnT.Kind() != reflect.Func {
|
||||
return nil, errors.New("given interface is not a function")
|
||||
}
|
||||
// underlying event type
|
||||
var argT reflect.Type
|
||||
var isch bool
|
||||
|
||||
switch fnT.Kind() {
|
||||
case reflect.Func:
|
||||
if fnT.NumIn() != 1 {
|
||||
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")
|
||||
}
|
||||
|
||||
argT := fnT.In(0)
|
||||
kind := argT.Kind()
|
||||
argT = fnT.In(0)
|
||||
|
||||
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
|
||||
if kind != reflect.Ptr && kind != reflect.Interface {
|
||||
|
|
@ -272,6 +292,7 @@ func reflectFn(function interface{}) (*handler, error) {
|
|||
return &handler{
|
||||
event: argT,
|
||||
callback: fnV,
|
||||
isChan: isch,
|
||||
isIface: kind == reflect.Interface,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -285,5 +306,9 @@ func (h handler) not(event reflect.Type) bool {
|
|||
}
|
||||
|
||||
func (h handler) call(event reflect.Value) {
|
||||
if h.isChan {
|
||||
h.callback.Send(event)
|
||||
} else {
|
||||
h.callback.Call([]reflect.Value{event})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ func TestCall(t *testing.T) {
|
|||
func TestHandler(t *testing.T) {
|
||||
var results = make(chan string)
|
||||
|
||||
h, err := reflectFn(func(m *gateway.MessageCreateEvent) {
|
||||
h, err := newHandler(func(m *gateway.MessageCreateEvent) {
|
||||
results <- m.Content
|
||||
})
|
||||
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) {
|
||||
var results = make(chan interface{})
|
||||
|
||||
h, err := reflectFn(func(m interface{}) {
|
||||
h, err := newHandler(func(m interface{}) {
|
||||
results <- m
|
||||
})
|
||||
if err != nil {
|
||||
|
|
@ -121,7 +146,7 @@ func TestHandlerInterface(t *testing.T) {
|
|||
t.Fatal("Assertion failed:", recv)
|
||||
}
|
||||
|
||||
func TestHandlerWait(t *testing.T) {
|
||||
func TestHandlerWaitFor(t *testing.T) {
|
||||
inc := make(chan interface{}, 1)
|
||||
|
||||
h := New()
|
||||
|
|
@ -173,7 +198,7 @@ func TestHandlerWait(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHandlerChan(t *testing.T) {
|
||||
func TestHandlerChanFor(t *testing.T) {
|
||||
h := New()
|
||||
|
||||
wanted := &gateway.TypingStartEvent{
|
||||
|
|
@ -208,7 +233,7 @@ func TestHandlerChan(t *testing.T) {
|
|||
}
|
||||
|
||||
func BenchmarkReflect(b *testing.B) {
|
||||
h, err := reflectFn(func(m *gateway.MessageCreateEvent) {})
|
||||
h, err := newHandler(func(m *gateway.MessageCreateEvent) {})
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue