...

Source file src/github.com/PaesslerAG/gval/functions.go

Documentation: github.com/PaesslerAG/gval

     1  package gval
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  )
     8  
     9  type function func(ctx context.Context, arguments ...interface{}) (interface{}, error)
    10  
    11  func toFunc(f interface{}) function {
    12  	if f, ok := f.(func(arguments ...interface{}) (interface{}, error)); ok {
    13  		return function(func(ctx context.Context, arguments ...interface{}) (interface{}, error) {
    14  			var v interface{}
    15  			errCh := make(chan error)
    16  			go func() {
    17  				defer func() {
    18  					if recovered := recover(); recovered != nil {
    19  						errCh <- fmt.Errorf("%v", recovered)
    20  					}
    21  				}()
    22  				result, err := f(arguments...)
    23  				v = result
    24  				errCh <- err
    25  			}()
    26  
    27  			select {
    28  			case <-ctx.Done():
    29  				return nil, ctx.Err()
    30  			case err := <-errCh:
    31  				return v, err
    32  			}
    33  		})
    34  	}
    35  	if f, ok := f.(func(ctx context.Context, arguments ...interface{}) (interface{}, error)); ok {
    36  		return function(f)
    37  	}
    38  
    39  	fun := reflect.ValueOf(f)
    40  	t := fun.Type()
    41  	return func(ctx context.Context, args ...interface{}) (interface{}, error) {
    42  		var v interface{}
    43  		errCh := make(chan error)
    44  		go func() {
    45  			defer func() {
    46  				if recovered := recover(); recovered != nil {
    47  					errCh <- fmt.Errorf("%v", recovered)
    48  				}
    49  			}()
    50  			in, err := createCallArguments(ctx, t, args)
    51  			if err != nil {
    52  				errCh <- err
    53  				return
    54  			}
    55  			out := fun.Call(in)
    56  
    57  			r := make([]interface{}, len(out))
    58  			for i, e := range out {
    59  				r[i] = e.Interface()
    60  			}
    61  
    62  			err = nil
    63  			errorInterface := reflect.TypeOf((*error)(nil)).Elem()
    64  			if len(r) > 0 && t.Out(len(r)-1).Implements(errorInterface) {
    65  				if r[len(r)-1] != nil {
    66  					err = r[len(r)-1].(error)
    67  				}
    68  				r = r[0 : len(r)-1]
    69  			}
    70  
    71  			switch len(r) {
    72  			case 0:
    73  				v = nil
    74  			case 1:
    75  				v = r[0]
    76  			default:
    77  				v = r
    78  			}
    79  			errCh <- err
    80  		}()
    81  
    82  		select {
    83  		case <-ctx.Done():
    84  			return nil, ctx.Err()
    85  		case err := <-errCh:
    86  			return v, err
    87  		}
    88  	}
    89  }
    90  
    91  func createCallArguments(ctx context.Context, t reflect.Type, args []interface{}) ([]reflect.Value, error) {
    92  	variadic := t.IsVariadic()
    93  	numIn := t.NumIn()
    94  
    95  	// if first argument is a context, use the given execution context
    96  	if numIn > 0 {
    97  		thisFun := reflect.ValueOf(createCallArguments)
    98  		thisT := thisFun.Type()
    99  		if t.In(0) == thisT.In(0) {
   100  			args = append([]interface{}{ctx}, args...)
   101  		}
   102  	}
   103  
   104  	if (!variadic && len(args) != numIn) || (variadic && len(args) < numIn-1) {
   105  		return nil, fmt.Errorf("invalid number of parameters")
   106  	}
   107  
   108  	in := make([]reflect.Value, len(args))
   109  	var inType reflect.Type
   110  	for i, arg := range args {
   111  		if !variadic || i < numIn-1 {
   112  			inType = t.In(i)
   113  		} else if i == numIn-1 {
   114  			inType = t.In(numIn - 1).Elem()
   115  		}
   116  		argVal := reflect.ValueOf(arg)
   117  		if arg == nil {
   118  			argVal = reflect.ValueOf(reflect.Interface)
   119  		} else if !argVal.Type().AssignableTo(inType) {
   120  			return nil, fmt.Errorf("expected type %s for parameter %d but got %T",
   121  				inType.String(), i, arg)
   122  		}
   123  		in[i] = argVal
   124  	}
   125  	return in, nil
   126  }
   127  

View as plain text