@ -1,27 +1,41 @@
package bait
import (
"fmt"
"hilbish/util"
rt "github.com/arnodel/golua/runtime"
"github.com/arnodel/golua/lib/packagelib"
"github.com/chuckpreslar/emission"
)
type listenerType int
const (
goListener listenerType = iota
luaListener
)
// Recoverer is a function which is called when a panic occurs in an event.
type Recoverer func ( event string , handler * Listener , err interface { } )
// Listener is a struct that holds the handler for an event.
type Listener struct {
typ listenerType
once bool
caller func ( ... interface { } )
luaCaller * rt . Closure
}
type Bait struct {
Em * emission . Emitter
Loader packagelib . Loader
recoverer Recoverer
handlers map [ string ] [ ] * Listener
rtm * rt . Runtime
}
func New ( ) Bait {
emitter := emission . NewEmitter ( )
emitter . RecoverWith ( func ( hookname , hookfunc interface { } , err error ) {
emitter . Off ( hookname , hookfunc )
fmt . Println ( err )
} )
b := Bait {
Em : emitter ,
// New creates a new Bait instance.
func New ( rtm * rt . Runtime ) * Bait {
b := & Bait {
handlers : make ( map [ string ] [ ] * Listener ) ,
rtm : rtm ,
}
b . Loader = packagelib . Loader {
Load : b . loaderFunc ,
@ -31,11 +45,148 @@ func New() Bait {
return b
}
// Emit throws an event.
func ( b * Bait ) Emit ( event string , args ... interface { } ) {
handles := b . handlers [ event ]
if handles == nil {
return
}
for idx , handle := range handles {
defer func ( ) {
if err := recover ( ) ; err != nil {
b . callRecoverer ( event , handle , err )
}
} ( )
if handle . typ == luaListener {
funcVal := rt . FunctionValue ( handle . luaCaller )
var luaArgs [ ] rt . Value
for _ , arg := range args {
var luarg rt . Value
switch arg . ( type ) {
case rt . Value : luarg = arg . ( rt . Value )
default : luarg = rt . AsValue ( arg )
}
luaArgs = append ( luaArgs , luarg )
}
_ , err := rt . Call1 ( b . rtm . MainThread ( ) , funcVal , luaArgs ... )
if err != nil {
// panicking here won't actually cause hilbish to panic and instead will
// print the error and remove the hook. reference the recoverer function in lua.go
panic ( err )
}
} else {
handle . caller ( args ... )
}
if handle . once {
b . removeListener ( event , idx )
}
}
}
// On adds a Go function handler for an event.
func ( b * Bait ) On ( event string , handler func ( ... interface { } ) ) * Listener {
listener := & Listener {
typ : goListener ,
caller : handler ,
}
b . addListener ( event , listener )
return listener
}
// OnLua adds a Lua function handler for an event.
func ( b * Bait ) OnLua ( event string , handler * rt . Closure ) * Listener {
listener := & Listener {
typ : luaListener ,
luaCaller : handler ,
}
b . addListener ( event , listener )
return listener
}
// Off removes a Go function handler for an event.
func ( b * Bait ) Off ( event string , listener * Listener ) {
handles := b . handlers [ event ]
for i , handle := range handles {
if handle == listener {
b . removeListener ( event , i )
}
}
}
// OffLua removes a Lua function handler for an event.
func ( b * Bait ) OffLua ( event string , handler * rt . Closure ) {
handles := b . handlers [ event ]
for i , handle := range handles {
if handle . luaCaller == handler {
b . removeListener ( event , i )
}
}
}
// Once adds a Go function listener for an event that only runs once.
func ( b * Bait ) Once ( event string , handler func ( ... interface { } ) ) * Listener {
listener := & Listener {
typ : goListener ,
once : true ,
caller : handler ,
}
b . addListener ( event , listener )
return listener
}
// OnceLua adds a Lua function listener for an event that only runs once.
func ( b * Bait ) OnceLua ( event string , handler * rt . Closure ) * Listener {
listener := & Listener {
typ : luaListener ,
once : true ,
luaCaller : handler ,
}
b . addListener ( event , listener )
return listener
}
// SetRecoverer sets the function to be executed when a panic occurs in an event.
func ( b * Bait ) SetRecoverer ( recoverer Recoverer ) {
b . recoverer = recoverer
}
func ( b * Bait ) addListener ( event string , listener * Listener ) {
if b . handlers [ event ] == nil {
b . handlers [ event ] = [ ] * Listener { }
}
b . handlers [ event ] = append ( b . handlers [ event ] , listener )
}
func ( b * Bait ) removeListener ( event string , idx int ) {
b . handlers [ event ] [ idx ] = b . handlers [ event ] [ len ( b . handlers [ event ] ) - 1 ]
b . handlers [ event ] = b . handlers [ event ] [ : len ( b . handlers [ event ] ) - 1 ]
}
func ( b * Bait ) callRecoverer ( event string , handler * Listener , err interface { } ) {
if b . recoverer == nil {
panic ( err )
}
b . recoverer ( event , handler , err )
}
func ( b * Bait ) loaderFunc ( rtm * rt . Runtime ) ( rt . Value , func ( ) ) {
exports := map [ string ] util . LuaExport {
"catch" : util . LuaExport { b . bcatch , 2 , false } ,
"catchOnce" : util . LuaExport { b . bcatchOnce , 2 , false } ,
"throw" : util . LuaExport { b . bthrow , 1 , true } ,
"release" : util . LuaExport { b . brelease , 2 , false } ,
}
mod := rt . NewTable ( )
util . SetExports ( rtm , mod , exports )
@ -89,7 +240,7 @@ func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
for i , v := range c . Etc ( ) {
ifaceSlice [ i ] = v
}
b . Em . Emit ( name , ifaceSlice ... )
b . Emit ( name , ifaceSlice ... )
return c . Next ( ) , nil
}
@ -104,9 +255,7 @@ func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return nil , err
}
b . Em . On ( name , func ( args ... interface { } ) {
handleHook ( t , c , name , catcher , args ... )
} )
b . OnLua ( name , catcher )
return c . Next ( ) , nil
}
@ -121,9 +270,22 @@ func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return nil , err
}
b . Em . Once ( name , func ( args ... interface { } ) {
handleHook ( t , c , name , catcher , args ... )
} )
b . OnceLua ( name , catcher )
return c . Next ( ) , nil
}
// release(name, catcher)
// Removes the `catcher` for the event with `name`
// For this to work, `catcher` has to be the same function used to catch
// an event, like one saved to a variable.
func ( b * Bait ) brelease ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
name , catcher , err := util . HandleStrCallback ( t , c )
if err != nil {
return nil , err
}
b . OffLua ( name , catcher )
return c . Next ( ) , nil
}