diff --git a/parser.go b/parser.go index 71617b54..71bb243e 100644 --- a/parser.go +++ b/parser.go @@ -55,6 +55,12 @@ func parseData(raw interface{}, variablesMapping map[string]interface{}) interfa value := rawValue.String() value = strings.TrimSpace(value) return parseString(value, variablesMapping) + case reflect.Slice: + parsedSlice := make([]interface{}, rawValue.Len()) + for i := 0; i < rawValue.Len(); i++ { + parsedSlice[i] = parseData(rawValue.Index(i).Interface(), variablesMapping) + } + return parsedSlice case reflect.Map: // convert any map to map[string]interface{} parsedMap := make(map[string]interface{}) for _, k := range rawValue.MapKeys() { @@ -109,6 +115,9 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ parsedString += remainedString[0:startPosition] remainedString = remainedString[startPosition:] + // Notice: notation priority + // $$ > ${func($a, $b)} > $var + // search $$, use $$ to escape $ notation if strings.HasPrefix(remainedString, "$$") { // found $$ matchStartPosition += 2 @@ -117,6 +126,35 @@ func parseString(raw string, variablesMapping map[string]interface{}) interface{ continue } + // search function like ${func($a, $b)} + funcMatched := regexCompileFunction.FindStringSubmatch(remainedString) + if len(funcMatched) == 3 { + funcName := funcMatched[1] + argsStr := funcMatched[2] + arguments, err := parseFunctionArguments(argsStr) + if err != nil { + return raw + } + parsedArgs := parseData(arguments, variablesMapping).([]interface{}) + + result, err := callFunc(funcName, parsedArgs...) + if err != nil { + return raw + } + + if funcMatched[0] == raw { + // raw_string is a function, e.g. "${add_one(3)}", return its eval value directly + return result + } + + // raw_string contains one or many functions, e.g. "abc${add_one(3)}def" + matchStartPosition += len(funcMatched[0]) + parsedString += fmt.Sprintf("%v", result) + remainedString = raw[matchStartPosition:] + log.Printf("[parseString] parsedString: %v, matchStartPosition: %v", parsedString, matchStartPosition) + continue + } + // search variable like ${var} or $var varMatched := regexCompileVariable.FindStringSubmatch(remainedString) if len(varMatched) == 3 { diff --git a/parser_test.go b/parser_test.go index 2d3e4063..6793c9a0 100644 --- a/parser_test.go +++ b/parser_test.go @@ -166,6 +166,7 @@ func TestParseDataStringWithVariables(t *testing.T) { } } } + func TestParseDataStringWithVariablesAbnormal(t *testing.T) { variablesMapping := map[string]interface{}{ "var_1": "abc", @@ -374,3 +375,35 @@ func TestParseFunctionArguments(t *testing.T) { } } } + +func TestParseDataStringWithFunctions(t *testing.T) { + variablesMapping := map[string]interface{}{ + "n": 5, + "a": 12.3, + "b": 3.45, + } + + if !assert.Len(t, parseData("${gen_random_string(5)}", variablesMapping), 5) { + t.Fail() + } + if !assert.Len(t, parseData("${gen_random_string($n)}", variablesMapping), 5) { + t.Fail() + } + + if !assert.Len(t, parseData("123${gen_random_string(5)}abc", variablesMapping), 11) { + t.Fail() + } + if !assert.Len(t, parseData("123${gen_random_string($n)}abc", variablesMapping), 11) { + t.Fail() + } + + if !assert.Equal(t, parseData("${max($a, $b)}", variablesMapping), 12.3) { + t.Fail() + } + if !assert.Equal(t, parseData("abc${max($a, $b)}123", variablesMapping), "abc12.3123") { + t.Fail() + } + if !assert.Equal(t, parseData("abc${max($a, 3.45)}123", variablesMapping), "abc12.3123") { + t.Fail() + } +}