2015-10-14 00:18:36 +00:00
|
|
|
// Package caller is used to dynamically call functions with data unmarshalled
|
|
|
|
// into the functions' first argument. Its main purpose is to hide common
|
|
|
|
// unmarshalling code from each function implementation thus reducing
|
|
|
|
// boilerplate and making package interaction code sexier.
|
2015-10-13 21:08:45 +00:00
|
|
|
package caller
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"reflect"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Caller wraps a function and makes it ready to be dynamically called.
|
|
|
|
type Caller struct {
|
2015-10-16 22:08:14 +00:00
|
|
|
// Unmarshaller is a BYOB unmarshaller function. By default it uses JSON.
|
|
|
|
Unmarshaller func(data []byte, v interface{}) error
|
|
|
|
fun reflect.Value
|
|
|
|
argtyp reflect.Type
|
2015-10-13 21:08:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ErrInvalidFunctionType is an error that is returned by the New function
|
|
|
|
// when its argument is not a function.
|
|
|
|
ErrInvalidFunctionType = errors.New("argument must be function")
|
|
|
|
// ErrInvalidFunctionInArguments is an error that is returned by the New
|
|
|
|
// function when its argument-function has a number of input arguments other
|
|
|
|
// than 1.
|
|
|
|
ErrInvalidFunctionInArguments = errors.New("function must have only one input argument")
|
|
|
|
// ErrInvalidFunctionOutArguments is an error that is returned by the New
|
|
|
|
// function when its argument-function returs any values.
|
|
|
|
ErrInvalidFunctionOutArguments = errors.New("function must not have output arguments")
|
|
|
|
)
|
|
|
|
|
|
|
|
// New creates a new Caller instance using the function given as an argument.
|
|
|
|
// It returns the Caller instance and an error if something is wrong with the
|
|
|
|
// argument-function.
|
|
|
|
func New(fun interface{}) (c *Caller, err error) {
|
|
|
|
fval := reflect.ValueOf(fun)
|
|
|
|
ftyp := reflect.TypeOf(fun)
|
|
|
|
if ftyp.Kind() != reflect.Func {
|
|
|
|
return nil, ErrInvalidFunctionType
|
|
|
|
}
|
|
|
|
if ftyp.NumIn() != 1 {
|
|
|
|
return nil, ErrInvalidFunctionInArguments
|
|
|
|
}
|
|
|
|
if ftyp.NumOut() != 0 {
|
|
|
|
return nil, ErrInvalidFunctionOutArguments
|
|
|
|
}
|
|
|
|
|
|
|
|
c = &Caller{
|
2015-10-16 22:08:14 +00:00
|
|
|
Unmarshaller: json.Unmarshal,
|
|
|
|
fun: fval,
|
|
|
|
argtyp: ftyp.In(0),
|
2015-10-13 21:08:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call creates an instance of the Caller function's argument type, unmarshalls
|
2015-10-16 22:08:14 +00:00
|
|
|
// the payload into it and dynamically calls the Caller function with this
|
2015-10-13 21:08:45 +00:00
|
|
|
// instance.
|
|
|
|
func (c *Caller) Call(data []byte) error {
|
|
|
|
val, err := c.unmarshal(data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
c.makeDynamicCall(val)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Caller) unmarshal(data []byte) (val reflect.Value, err error) {
|
|
|
|
val = c.newValue()
|
2015-10-16 22:08:14 +00:00
|
|
|
err = c.Unmarshaller(data, val.Interface())
|
2015-10-13 21:08:45 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Caller) makeDynamicCall(val reflect.Value) {
|
|
|
|
c.fun.Call([]reflect.Value{val.Elem()})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Caller) newValue() reflect.Value {
|
|
|
|
return reflect.New(c.argtyp)
|
|
|
|
}
|