123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- package context
- import (
- "errors"
- "reflect"
- "sync"
- )
- // ErrInvalidArgs fires when the `Context.CallFunc`
- // is called with invalid number of arguments.
- var ErrInvalidArgs = errors.New("invalid arguments")
- // Func represents a function registered by the Context.
- // See its `buildMeta` and `call` internal methods.
- type Func struct {
- RegisterName string // the name of which this function is registered, for information only.
- Raw interface{} // the Raw function, can be used for custom casting.
- PersistenceArgs []interface{} // the persistence input arguments given on registration.
- once sync.Once // guards build once, on first call.
- // Available after the first call.
- Meta *FuncMeta
- }
- func newFunc(name string, fn interface{}, persistenceArgs ...interface{}) *Func {
- return &Func{
- RegisterName: name,
- Raw: fn,
- PersistenceArgs: persistenceArgs,
- }
- }
- // FuncMeta holds the necessary information about a registered
- // context function. Built once by the Func.
- type FuncMeta struct {
- Handler Handler // when it's just a handler.
- HandlerWithErr func(*Context) error // when it's just a handler which returns an error.
- RawFunc func() // when it's just a func.
- RawFuncWithErr func() error // when it's just a func which returns an error.
- RawFuncArgs func(...interface{})
- RawFuncArgsWithErr func(...interface{}) error
- Value reflect.Value
- Type reflect.Type
- ExpectedArgumentsLength int
- PersistenceInputs []reflect.Value
- AcceptsContext bool // the Context, if exists should be always first argument.
- ReturnsError bool // when the function's last output argument is error.
- }
- func (f *Func) buildMeta() {
- switch fn := f.Raw.(type) {
- case Handler:
- f.Meta = &FuncMeta{Handler: fn}
- return
- // case func(*Context):
- // f.Meta = &FuncMeta{Handler: fn}
- // return
- case func(*Context) error:
- f.Meta = &FuncMeta{HandlerWithErr: fn}
- return
- case func():
- f.Meta = &FuncMeta{RawFunc: fn}
- return
- case func() error:
- f.Meta = &FuncMeta{RawFuncWithErr: fn}
- return
- case func(...interface{}):
- f.Meta = &FuncMeta{RawFuncArgs: fn}
- return
- case func(...interface{}) error:
- f.Meta = &FuncMeta{RawFuncArgsWithErr: fn}
- return
- }
- fn := f.Raw
- meta := FuncMeta{}
- if val, ok := fn.(reflect.Value); ok {
- meta.Value = val
- } else {
- meta.Value = reflect.ValueOf(fn)
- }
- meta.Type = meta.Value.Type()
- if meta.Type.Kind() != reflect.Func {
- return
- }
- meta.ExpectedArgumentsLength = meta.Type.NumIn()
- skipInputs := len(meta.PersistenceInputs)
- if meta.ExpectedArgumentsLength > skipInputs {
- meta.AcceptsContext = isContext(meta.Type.In(skipInputs))
- }
- if numOut := meta.Type.NumOut(); numOut > 0 {
- // error should be the last output.
- if isError(meta.Type.Out(numOut - 1)) {
- meta.ReturnsError = true
- }
- }
- persistenceArgs := f.PersistenceArgs
- if len(persistenceArgs) > 0 {
- inputs := make([]reflect.Value, 0, len(persistenceArgs))
- for _, arg := range persistenceArgs {
- if in, ok := arg.(reflect.Value); ok {
- inputs = append(inputs, in)
- } else {
- inputs = append(inputs, reflect.ValueOf(in))
- }
- }
- meta.PersistenceInputs = inputs
- }
- f.Meta = &meta
- }
- func (f *Func) call(ctx *Context, args ...interface{}) ([]reflect.Value, error) {
- f.once.Do(f.buildMeta)
- meta := f.Meta
- if meta.Handler != nil {
- meta.Handler(ctx)
- return nil, nil
- }
- if meta.HandlerWithErr != nil {
- return nil, meta.HandlerWithErr(ctx)
- }
- if meta.RawFunc != nil {
- meta.RawFunc()
- return nil, nil
- }
- if meta.RawFuncWithErr != nil {
- return nil, meta.RawFuncWithErr()
- }
- if meta.RawFuncArgs != nil {
- meta.RawFuncArgs(args...)
- return nil, nil
- }
- if meta.RawFuncArgsWithErr != nil {
- return nil, meta.RawFuncArgsWithErr(args...)
- }
- inputs := make([]reflect.Value, 0, f.Meta.ExpectedArgumentsLength)
- inputs = append(inputs, f.Meta.PersistenceInputs...)
- if f.Meta.AcceptsContext {
- inputs = append(inputs, reflect.ValueOf(ctx))
- }
- for _, arg := range args {
- if in, ok := arg.(reflect.Value); ok {
- inputs = append(inputs, in)
- } else {
- inputs = append(inputs, reflect.ValueOf(arg))
- }
- }
- // keep it here, the inptus may contain the context.
- if f.Meta.ExpectedArgumentsLength != len(inputs) {
- return nil, ErrInvalidArgs
- }
- outputs := f.Meta.Value.Call(inputs)
- return outputs, getError(outputs)
- }
- var contextType = reflect.TypeOf((*Context)(nil))
- // isContext returns true if the "typ" is a type of Context.
- func isContext(typ reflect.Type) bool {
- return typ == contextType
- }
- var errTyp = reflect.TypeOf((*error)(nil)).Elem()
- // isError returns true if "typ" is type of `error`.
- func isError(typ reflect.Type) bool {
- return typ.Implements(errTyp)
- }
- func getError(outputs []reflect.Value) error {
- if n := len(outputs); n > 0 {
- lastOut := outputs[n-1]
- if isError(lastOut.Type()) {
- if lastOut.IsNil() {
- return nil
- }
- return lastOut.Interface().(error)
- }
- }
- return nil
- }
|