diff --git a/builtin/function.go b/builtin/function.go index e240fa16..897c5078 100644 --- a/builtin/function.go +++ b/builtin/function.go @@ -1,11 +1,26 @@ package builtin -import "time" +import ( + "math/rand" + "time" +) var Functions = map[string]interface{}{ - "sleep": Sleep, + "sleep": sleep, + "gen_random_string": genRandomString, } -func Sleep(nSecs int) { +func sleep(nSecs int) { time.Sleep(time.Duration(nSecs) * time.Second) } + +func genRandomString(n int) string { + const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" + const lettersLen = len(letters) + + b := make([]byte, n) + for i := range b { + b[i] = letters[rand.Intn(lettersLen)] + } + return string(b) +} diff --git a/parser.go b/parser.go index b9023a9c..c27a8e30 100644 --- a/parser.go +++ b/parser.go @@ -169,7 +169,9 @@ func mergeVariables(variables, overriddenVariables map[string]interface{}) map[s return mergedVariables } -func callFunction(funcName string, params []interface{}) (interface{}, error) { +// callFunction call function with arguments +// only support return at most one result value +func callFunction(funcName string, arguments []interface{}) (interface{}, error) { function, ok := builtin.Functions[funcName] if !ok { // function not found @@ -182,13 +184,25 @@ func callFunction(funcName string, params []interface{}) (interface{}, error) { return nil, fmt.Errorf("function %s is invalid", funcName) } - paramsValue := make([]reflect.Value, len(params)) - for index, param := range params { - paramsValue[index] = reflect.ValueOf(param) + argumentsValue := make([]reflect.Value, len(arguments)) + for index, argument := range arguments { + argumentsValue[index] = reflect.ValueOf(argument) } - log.Printf("[callFunction] function: %v, params: %v", funcName, params) - result := funcValue.Call(paramsValue) - log.Printf("[callFunction] result: %v", result) + log.Printf("[callFunction] func: %v, input arguments: %v", funcName, arguments) + resultValues := funcValue.Call(argumentsValue) + log.Printf("[callFunction] output results: %v", resultValues) + + if len(resultValues) == 0 { + return nil, nil + } else if len(resultValues) > 1 { + // function should return at most one value + err := fmt.Errorf("function %s should return at most one value", funcName) + log.Printf("[callFunction] error: %s", err.Error()) + return nil, err + } + + // convert reflect.Value to interface{} + result := resultValues[0].Interface() return result, nil } diff --git a/parser_test.go b/parser_test.go index 9cd7a026..674971ea 100644 --- a/parser_test.go +++ b/parser_test.go @@ -281,6 +281,7 @@ func TestMergeVariables(t *testing.T) { } func TestCallFunction(t *testing.T) { + // call function without arguments funcName := "sleep" params := []interface{}{1} timeStart := time.Now() @@ -291,4 +292,15 @@ func TestCallFunction(t *testing.T) { if !assert.Greater(t, time.Since(timeStart), time.Duration(1)*time.Second) { t.Fail() } + + // call function with one argument + funcName = "gen_random_string" + params = []interface{}{10} + result, err := callFunction(funcName, params) + if !assert.Nil(t, err) { + t.Fail() + } + if !assert.Equal(t, 10, len(result.(string))) { + t.Fail() + } }