Skip to content

Commit

Permalink
Merge pull request bwmarrin#294 from iopred/reflect
Browse files Browse the repository at this point in the history
Remove the use of reflect.
  • Loading branch information
bwmarrin committed Dec 8, 2016
2 parents e650021 + 3660125 commit 0759785
Show file tree
Hide file tree
Showing 9 changed files with 1,505 additions and 389 deletions.
172 changes: 1 addition & 171 deletions discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@
// Package discordgo provides Discord binding for Go
package discordgo

import (
"fmt"
"reflect"
)
import "fmt"

// VERSION of Discordgo, follows Symantic Versioning. (http://semver.org/)
const VERSION = "0.14.0-dev"
Expand Down Expand Up @@ -126,170 +123,3 @@ func New(args ...interface{}) (s *Session, err error) {

return
}

// validateHandler takes an event handler func, and returns the type of event.
// eg.
// Session.validateHandler(func (s *discordgo.Session, m *discordgo.MessageCreate))
// will return the reflect.Type of *discordgo.MessageCreate
func (s *Session) validateHandler(handler interface{}) reflect.Type {

handlerType := reflect.TypeOf(handler)

if handlerType.NumIn() != 2 {
panic("Unable to add event handler, handler must be of the type func(*discordgo.Session, *discordgo.EventType).")
}

if handlerType.In(0) != reflect.TypeOf(s) {
panic("Unable to add event handler, first argument must be of type *discordgo.Session.")
}

eventType := handlerType.In(1)

// Support handlers of type interface{}, this is a special handler, which is triggered on every event.
if eventType.Kind() == reflect.Interface {
eventType = nil
}

return eventType
}

// AddHandler allows you to add an event handler that will be fired anytime
// the Discord WSAPI event that matches the interface fires.
// eventToInterface in events.go has a list of all the Discord WSAPI events
// and their respective interface.
// eg:
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
// })
//
// or:
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
// })
// The return value of this method is a function, that when called will remove the
// event handler.
func (s *Session) AddHandler(handler interface{}) func() {

s.initialize()

eventType := s.validateHandler(handler)

s.handlersMu.Lock()
defer s.handlersMu.Unlock()

h := reflect.ValueOf(handler)

s.handlers[eventType] = append(s.handlers[eventType], h)

// This must be done as we need a consistent reference to the
// reflected value, otherwise a RemoveHandler method would have
// been nice.
return func() {
s.handlersMu.Lock()
defer s.handlersMu.Unlock()

handlers := s.handlers[eventType]
for i, v := range handlers {
if h == v {
s.handlers[eventType] = append(handlers[:i], handlers[i+1:]...)
return
}
}
}
}

// handle calls any handlers that match the event type and any handlers of
// interface{}.
func (s *Session) handle(event interface{}) {

s.handlersMu.RLock()
defer s.handlersMu.RUnlock()

if s.handlers == nil {
return
}

handlerParameters := []reflect.Value{reflect.ValueOf(s), reflect.ValueOf(event)}

s.onInterface(event)

if handlers, ok := s.handlers[nil]; ok {
for _, handler := range handlers {
go handler.Call(handlerParameters)
}
}

if handlers, ok := s.handlers[reflect.TypeOf(event)]; ok {
for _, handler := range handlers {
go handler.Call(handlerParameters)
}
}
}

// initialize adds all internal handlers and state tracking handlers.
func (s *Session) initialize() {

s.log(LogInformational, "called")

s.handlersMu.Lock()
defer s.handlersMu.Unlock()

if s.handlers != nil {
return
}

s.handlers = map[interface{}][]reflect.Value{}
}

// setGuildIds will set the GuildID on all the members of a guild.
// This is done as event data does not have it set.
func setGuildIds(g *Guild) {
for _, c := range g.Channels {
c.GuildID = g.ID
}

for _, m := range g.Members {
m.GuildID = g.ID
}

for _, vs := range g.VoiceStates {
vs.GuildID = g.ID
}
}

// onInterface handles all internal events and routes them to the appropriate internal handler.
func (s *Session) onInterface(i interface{}) {
switch t := i.(type) {
case *Ready:
for _, g := range t.Guilds {
setGuildIds(g)
}
s.onReady(t)
case *GuildCreate:
setGuildIds(t.Guild)
case *GuildUpdate:
setGuildIds(t.Guild)
case *Resumed:
s.onResumed(t)
case *VoiceServerUpdate:
go s.onVoiceServerUpdate(t)
case *VoiceStateUpdate:
go s.onVoiceStateUpdate(t)
}
s.State.onInterface(s, i)
}

// onReady handles the ready event.
func (s *Session) onReady(r *Ready) {

// Store the SessionID within the Session struct.
s.sessionID = r.SessionID

// Start the heartbeat to keep the connection alive.
go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
}

// onResumed handles the resumed event.
func (s *Session) onResumed(r *Resumed) {

// Start the heartbeat to keep the connection alive.
go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
}
8 changes: 4 additions & 4 deletions discord_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,8 @@ func TestAddHandler(t *testing.T) {
d.AddHandler(interfaceHandler)
d.AddHandler(bogusHandler)

d.handle(&MessageCreate{})
d.handle(&MessageDelete{})
d.handleEvent(messageCreateEventType, &MessageCreate{})
d.handleEvent(messageDeleteEventType, &MessageDelete{})

<-time.After(500 * time.Millisecond)

Expand Down Expand Up @@ -253,11 +253,11 @@ func TestRemoveHandler(t *testing.T) {
d := Session{}
r := d.AddHandler(testHandler)

d.handle(&MessageCreate{})
d.handleEvent(messageCreateEventType, &MessageCreate{})

r()

d.handle(&MessageCreate{})
d.handleEvent(messageCreateEventType, &MessageCreate{})

<-time.After(500 * time.Millisecond)

Expand Down
Loading

0 comments on commit 0759785

Please sign in to comment.