github.com/viant/toolbox@v0.34.5/function_util.go (about)

     1  package toolbox
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  )
     8  
     9  //GetFunction returns function for provided owner and name, or error
    10  func GetFunction(owner interface{}, name string) (interface{}, error) {
    11  	if owner == nil {
    12  		return nil, fmt.Errorf("failed to lookup %v on %T, owner was nil", name, owner)
    13  	}
    14  	var ownerType = reflect.TypeOf(owner)
    15  	var method, has = ownerType.MethodByName(name)
    16  	if !has {
    17  		var available = make([]string, 0)
    18  		for i := 0; i < ownerType.NumMethod(); i++ {
    19  			available = append(available, ownerType.Method(i).Name)
    20  		}
    21  		return nil, fmt.Errorf("failed to lookup %T.%v, available:[%v]", owner, name, strings.Join(available, ","))
    22  	}
    23  	return reflect.ValueOf(owner).MethodByName(method.Name).Interface(), nil
    24  }
    25  
    26  //CallFunction calls passed in function with provided parameters,it returns a function result.
    27  func CallFunction(function interface{}, parameters ...interface{}) []interface{} {
    28  	AssertKind(function, reflect.Func, "function")
    29  	var functionParameters = make([]reflect.Value, 0)
    30  	ProcessSlice(parameters, func(item interface{}) bool {
    31  		functionParameters = append(functionParameters, reflect.ValueOf(item))
    32  		return true
    33  	})
    34  
    35  	functionValue := reflect.ValueOf(function)
    36  	var resultValues = functionValue.Call(functionParameters)
    37  	var result = make([]interface{}, len(resultValues))
    38  	for i, resultValue := range resultValues {
    39  		result[i] = resultValue.Interface()
    40  	}
    41  	return result
    42  }
    43  
    44  //AsCompatibleFunctionParameters takes incompatible function parameters and converts then into provided function signature compatible
    45  func AsCompatibleFunctionParameters(function interface{}, parameters []interface{}) ([]interface{}, error) {
    46  	return AsFunctionParameters(function, parameters, map[string]interface{}{})
    47  }
    48  
    49  //AsFunctionParameters takes incompatible function parameters and converts then into provided function signature compatible
    50  func AsFunctionParameters(function interface{}, parameters []interface{}, parametersKV map[string]interface{}) ([]interface{}, error) {
    51  	AssertKind(function, reflect.Func, "function")
    52  	functionValue := reflect.ValueOf(function)
    53  	funcSignature := GetFuncSignature(function)
    54  	actualMethodSignatureLength := len(funcSignature)
    55  	converter := Converter{}
    56  	if actualMethodSignatureLength != len(parameters) {
    57  		return nil, fmt.Errorf("invalid number of parameters wanted: [%T],  had: %v", function, len(parameters))
    58  	}
    59  	var functionParameters = make([]interface{}, 0)
    60  	for i, parameterValue := range parameters {
    61  		isStruct := IsStruct(funcSignature[i])
    62  		if isStruct && parameterValue == nil {
    63  			parameterValue = make(map[string]interface{})
    64  		}
    65  		reflectValue := reflect.ValueOf(parameterValue)
    66  		if !isStruct {
    67  			if parameterValue == nil {
    68  				return nil, fmt.Errorf("parameter[%v] was empty", i)
    69  			}
    70  			if reflectValue.Kind() == reflect.Slice && funcSignature[i].Kind() != reflectValue.Kind() {
    71  				return nil, fmt.Errorf("incompatible types expected: %v, but had %v", funcSignature[i].Kind(), reflectValue.Kind())
    72  			} else if !reflectValue.IsValid() {
    73  				if funcSignature[i].Kind() == reflect.Slice {
    74  					parameterValue = reflect.New(funcSignature[i]).Interface()
    75  					reflectValue = reflect.ValueOf(parameterValue)
    76  				}
    77  			}
    78  		}
    79  		if reflectValue.Type() != funcSignature[i] {
    80  			newValuePointer := reflect.New(funcSignature[i])
    81  			var err error
    82  			if IsStruct(funcSignature[i]) && !(IsStruct(parameterValue) || IsMap(parameterValue)) {
    83  				err = converter.AssignConverted(newValuePointer.Interface(), parametersKV)
    84  			} else {
    85  				err = converter.AssignConverted(newValuePointer.Interface(), parameterValue)
    86  			}
    87  			if err != nil {
    88  				return nil, fmt.Errorf("failed to assign convert %v to %v due to %v", parametersKV, newValuePointer.Interface(), err)
    89  			}
    90  			reflectValue = newValuePointer.Elem()
    91  		}
    92  		if functionValue.Type().IsVariadic() && funcSignature[i].Kind() == reflect.Slice && i+1 == len(funcSignature) {
    93  			ProcessSlice(reflectValue.Interface(), func(item interface{}) bool {
    94  				functionParameters = append(functionParameters, item)
    95  				return true
    96  			})
    97  		} else {
    98  			functionParameters = append(functionParameters, reflectValue.Interface())
    99  		}
   100  	}
   101  	return functionParameters, nil
   102  }
   103  
   104  //BuildFunctionParameters builds function parameters provided in the parameterValues.
   105  // Parameters value will be converted if needed to expected by the function signature type. It returns function parameters , or error
   106  func BuildFunctionParameters(function interface{}, parameters []string, parameterValues map[string]interface{}) ([]interface{}, error) {
   107  	var functionParameters = make([]interface{}, 0)
   108  	for _, name := range parameters {
   109  		functionParameters = append(functionParameters, parameterValues[name])
   110  	}
   111  	return AsFunctionParameters(function, functionParameters, parameterValues)
   112  }
   113  
   114  //GetFuncSignature returns a function signature
   115  func GetFuncSignature(function interface{}) []reflect.Type {
   116  	AssertKind(function, reflect.Func, "function")
   117  	functionValue := reflect.ValueOf(function)
   118  	var result = make([]reflect.Type, 0)
   119  	functionType := functionValue.Type()
   120  	for i := 0; i < functionType.NumIn(); i++ {
   121  		result = append(result, functionType.In(i))
   122  	}
   123  	return result
   124  }