246 lines
6.5 KiB
Go
246 lines
6.5 KiB
Go
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
// All rights reserved.
|
|
|
|
// builtin-string.go
|
|
package expr
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
strParamOther = "other"
|
|
)
|
|
|
|
// --- Start of function definitions
|
|
func doJoinStr(funcName string, sep string, it Iterator) (result any, err error) {
|
|
var sb strings.Builder
|
|
var v any
|
|
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
|
if it.Index() > 0 {
|
|
sb.WriteString(sep)
|
|
}
|
|
if s, ok := v.(string); ok {
|
|
sb.WriteString(s)
|
|
} else {
|
|
err = ErrExpectedGot(funcName, TypeString, v)
|
|
return
|
|
}
|
|
}
|
|
if err == io.EOF {
|
|
err = nil
|
|
result = sb.String()
|
|
}
|
|
return
|
|
}
|
|
|
|
func joinStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
|
if sep, ok := args[ParamSeparator].(string); ok {
|
|
if v, exists := args[ParamItem]; exists {
|
|
argv := v.([]any)
|
|
if len(argv) == 1 {
|
|
if ls, ok := argv[0].(*ListType); ok {
|
|
result, err = doJoinStr(name, sep, NewListIterator(ls, nil))
|
|
} else if it, ok := argv[0].(Iterator); ok {
|
|
result, err = doJoinStr(name, sep, it)
|
|
} else if s, ok := argv[0].(string); ok {
|
|
result = s
|
|
} else {
|
|
err = ErrInvalidParameterValue(name, ParamItem, v)
|
|
}
|
|
} else {
|
|
result, err = doJoinStr(name, sep, NewArrayIterator(argv))
|
|
}
|
|
}
|
|
} else {
|
|
err = ErrWrongParamType(name, ParamSeparator, TypeString, args[ParamSeparator])
|
|
}
|
|
return
|
|
}
|
|
|
|
func subStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
|
var start = 0
|
|
var count = -1
|
|
var source string
|
|
var ok bool
|
|
|
|
if source, ok = args[ParamSource].(string); !ok {
|
|
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
|
}
|
|
|
|
if start, err = ToGoInt(args[ParamStart], name+"()"); err != nil {
|
|
return
|
|
}
|
|
|
|
if count, err = ToGoInt(args[ParamCount], name+"()"); err != nil {
|
|
return
|
|
}
|
|
|
|
if start < 0 {
|
|
start = len(source) + start
|
|
}
|
|
|
|
if count < 0 {
|
|
count = len(source) - start
|
|
}
|
|
end := min(start+count, len(source))
|
|
result = source[start:end]
|
|
return
|
|
}
|
|
|
|
func trimStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
|
var source string
|
|
var ok bool
|
|
|
|
if source, ok = args[ParamSource].(string); !ok {
|
|
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
|
}
|
|
result = strings.TrimSpace(source)
|
|
return
|
|
}
|
|
|
|
func startsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
|
var source, prefix string
|
|
var ok bool
|
|
|
|
result = false
|
|
|
|
if source, ok = args[ParamSource].(string); !ok {
|
|
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
|
}
|
|
|
|
if prefix, ok = args[ParamPrefix].(string); !ok {
|
|
return result, ErrWrongParamType(name, ParamPrefix, TypeString, args[ParamPrefix])
|
|
}
|
|
if strings.HasPrefix(source, prefix) {
|
|
result = true
|
|
} else if v, exists := args[strParamOther]; exists {
|
|
argv := v.([]any)
|
|
for i, targetSpec := range argv {
|
|
if target, ok := targetSpec.(string); ok {
|
|
if strings.HasPrefix(source, target) {
|
|
result = true
|
|
break
|
|
}
|
|
} else {
|
|
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, TypeName(targetSpec))
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func endsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
|
var source, suffix string
|
|
var ok bool
|
|
|
|
result = false
|
|
|
|
if source, ok = args[ParamSource].(string); !ok {
|
|
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
|
}
|
|
|
|
if suffix, ok = args[ParamSuffix].(string); !ok {
|
|
return result, ErrWrongParamType(name, ParamSuffix, TypeString, args[ParamSuffix])
|
|
}
|
|
if strings.HasPrefix(source, suffix) {
|
|
result = true
|
|
} else if v, exists := args[strParamOther]; exists {
|
|
argv := v.([]any)
|
|
for i, targetSpec := range argv {
|
|
if target, ok := targetSpec.(string); ok {
|
|
if strings.HasSuffix(source, target) {
|
|
result = true
|
|
break
|
|
}
|
|
} else {
|
|
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, TypeName(targetSpec))
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
|
var source, sep string
|
|
var count int = -1
|
|
var parts []string
|
|
var ok bool
|
|
|
|
if source, ok = args[ParamSource].(string); !ok {
|
|
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
|
}
|
|
|
|
if sep, ok = args[ParamSeparator].(string); !ok {
|
|
return nil, fmt.Errorf("separator param must be string, got %s (%v)", TypeName(args[ParamSeparator]), args[ParamSeparator])
|
|
}
|
|
|
|
if count64, ok := args[ParamCount].(int64); ok { // TODO replace type assertion with toInt()
|
|
count = int(count64)
|
|
} else {
|
|
return nil, fmt.Errorf("part count must be integer, got %s (%v)", TypeName(args[ParamCount]), args[ParamCount])
|
|
}
|
|
|
|
if count > 0 {
|
|
parts = strings.SplitN(source, sep, count)
|
|
} else if count < 0 {
|
|
parts = strings.Split(source, sep)
|
|
} else {
|
|
parts = []string{}
|
|
}
|
|
list := make(ListType, len(parts))
|
|
for i, part := range parts {
|
|
list[i] = part
|
|
}
|
|
result = &list
|
|
return
|
|
}
|
|
|
|
// --- End of function definitions
|
|
|
|
// Import above functions in the context
|
|
func ImportStringFuncs(ctx ExprContext) {
|
|
ctx.RegisterFunc("strJoin", NewGolangFunctor(joinStrFunc), TypeString, []ExprFuncParam{
|
|
NewFuncParam(ParamSeparator),
|
|
NewFuncParamFlag(ParamItem, PfRepeat),
|
|
})
|
|
|
|
ctx.RegisterFunc("strSub", NewGolangFunctor(subStrFunc), TypeString, []ExprFuncParam{
|
|
NewFuncParam(ParamSource),
|
|
NewFuncParamFlagDef(ParamStart, PfDefault, int64(0)),
|
|
NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)),
|
|
})
|
|
|
|
ctx.RegisterFunc("strSplit", NewGolangFunctor(splitStrFunc), "list of "+TypeString, []ExprFuncParam{
|
|
NewFuncParam(ParamSource),
|
|
NewFuncParamFlagDef(ParamSeparator, PfDefault, ""),
|
|
NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)),
|
|
})
|
|
|
|
ctx.RegisterFunc("strTrim", NewGolangFunctor(trimStrFunc), TypeString, []ExprFuncParam{
|
|
NewFuncParam(ParamSource),
|
|
})
|
|
|
|
ctx.RegisterFunc("strStartsWith", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{
|
|
NewFuncParam(ParamSource),
|
|
NewFuncParam(ParamPrefix),
|
|
NewFuncParamFlag(strParamOther, PfRepeat),
|
|
})
|
|
|
|
ctx.RegisterFunc("strEndsWith", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{
|
|
NewFuncParam(ParamSource),
|
|
NewFuncParam(ParamSuffix),
|
|
NewFuncParamFlag(strParamOther, PfRepeat),
|
|
})
|
|
}
|
|
|
|
// Register the import function in the import-register.
|
|
// That will allow to import all function of this module by the "builtin" operator."
|
|
func init() {
|
|
RegisterBuiltinModule("string", ImportStringFuncs, "string utilities")
|
|
}
|