summaryrefslogtreecommitdiffstats
path: root/tpl/tplimpl/template_funcs.go
diff options
context:
space:
mode:
authorCameron Moore <moorereason@gmail.com>2017-03-13 17:55:02 -0500
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2017-04-30 10:56:38 +0200
commitde7c32a1a880820252e922e0c9fcf69e109c0d1b (patch)
tree07d813f2617dd4a889aaebb885a9c1281a229960 /tpl/tplimpl/template_funcs.go
parent154e18ddb9ad205055d5bd4827c87f3f0daf499f (diff)
tpl: Add template function namespaces
This commit moves almost all of the template functions into separate packages under tpl/ and adds a namespace framework. All changes should be backward compatible for end users, as all existing function names in the template funcMap are left intact. Seq and DoArithmatic have been moved out of the helpers package and into template namespaces. Most of the tests involved have been refactored, and many new tests have been written. There's still work to do, but this is a big improvement. I got a little overzealous and added some new functions along the way: - strings.Contains - strings.ContainsAny - strings.HasSuffix - strings.TrimPrefix - strings.TrimSuffix Documentation is forthcoming. Fixes #3042
Diffstat (limited to 'tpl/tplimpl/template_funcs.go')
-rw-r--r--tpl/tplimpl/template_funcs.go2256
1 files changed, 111 insertions, 2145 deletions
diff --git a/tpl/tplimpl/template_funcs.go b/tpl/tplimpl/template_funcs.go
index 54cff81c6..8d86dfa54 100644
--- a/tpl/tplimpl/template_funcs.go
+++ b/tpl/tplimpl/template_funcs.go
@@ -16,1523 +16,14 @@
package tplimpl
import (
- "bytes"
- _md5 "crypto/md5"
- _sha1 "crypto/sha1"
- _sha256 "crypto/sha256"
- "encoding/base64"
- "encoding/hex"
- "encoding/json"
- "errors"
"fmt"
- "html"
"html/template"
- "image"
- "math/rand"
- "net/url"
- "os"
- "reflect"
- "regexp"
- "sort"
- "strconv"
- "strings"
"sync"
- "time"
- "unicode/utf8"
- "github.com/spf13/hugo/hugofs"
-
- "github.com/bep/inflect"
- "github.com/spf13/afero"
"github.com/spf13/cast"
- "github.com/spf13/hugo/helpers"
-
- // Importing image codecs for image.DecodeConfig
- _ "image/gif"
- _ "image/jpeg"
- _ "image/png"
+ "github.com/spf13/hugo/tpl/compare"
)
-// eq returns the boolean truth of arg1 == arg2.
-func eq(x, y interface{}) bool {
- normalize := func(v interface{}) interface{} {
- vv := reflect.ValueOf(v)
- switch vv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return vv.Int()
- case reflect.Float32, reflect.Float64:
- return vv.Float()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- return vv.Uint()
- default:
- return v
- }
- }
- x = normalize(x)
- y = normalize(y)
- return reflect.DeepEqual(x, y)
-}
-
-// ne returns the boolean truth of arg1 != arg2.
-func ne(x, y interface{}) bool {
- return !eq(x, y)
-}
-
-// ge returns the boolean truth of arg1 >= arg2.
-func ge(a, b interface{}) bool {
- left, right := compareGetFloat(a, b)
- return left >= right
-}
-
-// gt returns the boolean truth of arg1 > arg2.
-func gt(a, b interface{}) bool {
- left, right := compareGetFloat(a, b)
- return left > right
-}
-
-// le returns the boolean truth of arg1 <= arg2.
-func le(a, b interface{}) bool {
- left, right := compareGetFloat(a, b)
- return left <= right
-}
-
-// lt returns the boolean truth of arg1 < arg2.
-func lt(a, b interface{}) bool {
- left, right := compareGetFloat(a, b)
- return left < right
-}
-
-// dictionary creates a map[string]interface{} from the given parameters by
-// walking the parameters and treating them as key-value pairs. The number
-// of parameters must be even.
-func dictionary(values ...interface{}) (map[string]interface{}, error) {
- if len(values)%2 != 0 {
- return nil, errors.New("invalid dict call")
- }
- dict := make(map[string]interface{}, len(values)/2)
- for i := 0; i < len(values); i += 2 {
- key, ok := values[i].(string)
- if !ok {
- return nil, errors.New("dict keys must be strings")
- }
- dict[key] = values[i+1]
- }
- return dict, nil
-}
-
-// slice returns a slice of all passed arguments
-func slice(args ...interface{}) []interface{} {
- return args
-}
-
-func compareGetFloat(a interface{}, b interface{}) (float64, float64) {
- var left, right float64
- var leftStr, rightStr *string
- av := reflect.ValueOf(a)
-
- switch av.Kind() {
- case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
- left = float64(av.Len())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- left = float64(av.Int())
- case reflect.Float32, reflect.Float64:
- left = av.Float()
- case reflect.String:
- var err error
- left, err = strconv.ParseFloat(av.String(), 64)
- if err != nil {
- str := av.String()
- leftStr = &str
- }
- case reflect.Struct:
- switch av.Type() {
- case timeType:
- left = float64(toTimeUnix(av))
- }
- }
-
- bv := reflect.ValueOf(b)
-
- switch bv.Kind() {
- case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
- right = float64(bv.Len())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- right = float64(bv.Int())
- case reflect.Float32, reflect.Float64:
- right = bv.Float()
- case reflect.String:
- var err error
- right, err = strconv.ParseFloat(bv.String(), 64)
- if err != nil {
- str := bv.String()
- rightStr = &str
- }
- case reflect.Struct:
- switch bv.Type() {
- case timeType:
- right = float64(toTimeUnix(bv))
- }
- }
-
- switch {
- case leftStr == nil || rightStr == nil:
- case *leftStr < *rightStr:
- return 0, 1
- case *leftStr > *rightStr:
- return 1, 0
- default:
- return 0, 0
- }
-
- return left, right
-}
-
-// slicestr slices a string by specifying a half-open range with
-// two indices, start and end. 1 and 4 creates a slice including elements 1 through 3.
-// The end index can be omitted, it defaults to the string's length.
-func slicestr(a interface{}, startEnd ...interface{}) (string, error) {
- aStr, err := cast.ToStringE(a)
- if err != nil {
- return "", err
- }
-
- var argStart, argEnd int
-
- argNum := len(startEnd)
-
- if argNum > 0 {
- if argStart, err = cast.ToIntE(startEnd[0]); err != nil {
- return "", errors.New("start argument must be integer")
- }
- }
- if argNum > 1 {
- if argEnd, err = cast.ToIntE(startEnd[1]); err != nil {
- return "", errors.New("end argument must be integer")
- }
- }
-
- if argNum > 2 {
- return "", errors.New("too many arguments")
- }
-
- asRunes := []rune(aStr)
-
- if argNum > 0 && (argStart < 0 || argStart >= len(asRunes)) {
- return "", errors.New("slice bounds out of range")
- }
-
- if argNum == 2 {
- if argEnd < 0 || argEnd > len(asRunes) {
- return "", errors.New("slice bounds out of range")
- }
- return string(asRunes[argStart:argEnd]), nil
- } else if argNum == 1 {
- return string(asRunes[argStart:]), nil
- } else {
- return string(asRunes[:]), nil
- }
-
-}
-
-// hasPrefix tests whether the input s begins with prefix.
-func hasPrefix(s, prefix interface{}) (bool, error) {
- ss, err := cast.ToStringE(s)
- if err != nil {
- return false, err
- }
-
- sp, err := cast.ToStringE(prefix)
- if err != nil {
- return false, err
- }
-
- return strings.HasPrefix(ss, sp), nil
-}
-
-// substr extracts parts of a string, beginning at the character at the specified
-// position, and returns the specified number of characters.
-//
-// It normally takes two parameters: start and length.
-// It can also take one parameter: start, i.e. length is omitted, in which case
-// the substring starting from start until the end of the string will be returned.
-//
-// To extract characters from the end of the string, use a negative start number.
-//
-// In addition, borrowing from the extended behavior described at http://php.net/substr,
-// if length is given and is negative, then that many characters will be omitted from
-// the end of string.
-func substr(a interface{}, nums ...interface{}) (string, error) {
- aStr, err := cast.ToStringE(a)
- if err != nil {
- return "", err
- }
-
- var start, length int
-
- asRunes := []rune(aStr)
-
- switch len(nums) {
- case 0:
- return "", errors.New("too less arguments")
- case 1:
- if start, err = cast.ToIntE(nums[0]); err != nil {
- return "", errors.New("start argument must be integer")
- }
- length = len(asRunes)
- case 2:
- if start, err = cast.ToIntE(nums[0]); err != nil {
- return "", errors.New("start argument must be integer")
- }
- if length, err = cast.ToIntE(nums[1]); err != nil {
- return "", errors.New("length argument must be integer")
- }
- default:
- return "", errors.New("too many arguments")
- }
-
- if start < -len(asRunes) {
- start = 0
- }
- if start > len(asRunes) {
- return "", fmt.Errorf("start position out of bounds for %d-byte string", len(aStr))
- }
-
- var s, e int
- if start >= 0 && length >= 0 {
- s = start
- e = start + length
- } else if start < 0 && length >= 0 {
- s = len(asRunes) + start - length + 1
- e = len(asRunes) + start + 1
- } else if start >= 0 && length < 0 {
- s = start
- e = len(asRunes) + length
- } else {
- s = len(asRunes) + start
- e = len(asRunes) + length
- }
-
- if s > e {
- return "", fmt.Errorf("calculated start position greater than end position: %d > %d", s, e)
- }
- if e > len(asRunes) {
- e = len(asRunes)
- }
-
- return string(asRunes[s:e]), nil
-}
-
-// split slices an input string into all substrings separated by delimiter.
-func split(a interface{}, delimiter string) ([]string, error) {
- aStr, err := cast.ToStringE(a)
- if err != nil {
- return []string{}, err
- }
- return strings.Split(aStr, delimiter), nil
-}
-
-// intersect returns the common elements in the given sets, l1 and l2. l1 and
-// l2 must be of the same type and may be either arrays or slices.
-func intersect(l1, l2 interface{}) (interface{}, error) {
- if l1 == nil || l2 == nil {
- return make([]interface{}, 0), nil
- }
-
- l1v := reflect.ValueOf(l1)
- l2v := reflect.ValueOf(l2)
-
- switch l1v.Kind() {
- case reflect.Array, reflect.Slice:
- switch l2v.Kind() {
- case reflect.Array, reflect.Slice:
- r := reflect.MakeSlice(l1v.Type(), 0, 0)
- for i := 0; i < l1v.Len(); i++ {
- l1vv := l1v.Index(i)
- for j := 0; j < l2v.Len(); j++ {
- l2vv := l2v.Index(j)
- switch l1vv.Kind() {
- case reflect.String:
- if l1vv.Type() == l2vv.Type() && l1vv.String() == l2vv.String() && !in(r.Interface(), l2vv.Interface()) {
- r = reflect.Append(r, l2vv)
- }
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- switch l2vv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- if l1vv.Int() == l2vv.Int() && !in(r.Interface(), l2vv.Interface()) {
- r = reflect.Append(r, l2vv)
- }
- }
- case reflect.Float32, reflect.Float64:
- switch l2vv.Kind() {
- case reflect.Float32, reflect.Float64:
- if l1vv.Float() == l2vv.Float() && !in(r.Interface(), l2vv.Interface()) {
- r = reflect.Append(r, l2vv)
- }
- }
- }
- }
- }
- return r.Interface(), nil
- default:
- return nil, errors.New("can't iterate over " + reflect.ValueOf(l2).Type().String())
- }
- default:
- return nil, errors.New("can't iterate over " + reflect.ValueOf(l1).Type().String())
- }
-}
-
-// union returns the union of the given sets, l1 and l2. l1 and
-// l2 must be of the same type and may be either arrays or slices.
-// If l1 and l2 aren't of the same type then l1 will be returned.
-// If either l1 or l2 is nil then the non-nil list will be returned.
-func union(l1, l2 interface{}) (interface{}, error) {
- if l1 == nil && l2 == nil {
- return nil, errors.New("both arrays/slices have to be of the same type")
- } else if l1 == nil && l2 != nil {
- return l2, nil
- } else if l1 != nil && l2 == nil {
- return l1, nil
- }
-
- l1v := reflect.ValueOf(l1)
- l2v := reflect.ValueOf(l2)
-
- switch l1v.Kind() {
- case reflect.Array, reflect.Slice:
- switch l2v.Kind() {
- case reflect.Array, reflect.Slice:
- r := reflect.MakeSlice(l1v.Type(), 0, 0)
-
- if l1v.Type() != l2v.Type() {
- return r.Interface(), nil
- }
-
- for i := 0; i < l1v.Len(); i++ {
- elem := l1v.Index(i)
- if !in(r.Interface(), elem.Interface()) {
- r = reflect.Append(r, elem)
- }
- }
-
- for j := 0; j < l2v.Len(); j++ {
- elem := l2v.Index(j)
- if !in(r.Interface(), elem.Interface()) {
- r = reflect.Append(r, elem)
- }
- }
-
- return r.Interface(), nil
- default:
- return nil, errors.New("can't iterate over " + reflect.ValueOf(l2).Type().String())
- }
- default:
- return nil, errors.New("can't iterate over " + reflect.ValueOf(l1).Type().String())
- }
-}
-
-type imageHandler struct {
- imageConfigCache map[string]image.Config
- sync.RWMutex
- fs *hugofs.Fs
-}
-
-// imageConfig returns the image.Config for the specified path relative to the
-// working directory.
-func (ic *imageHandler) config(path interface{}) (image.Config, error) {
- filename, err := cast.ToStringE(path)
- if err != nil {
- return image.Config{}, err
- }
-
- if filename == "" {
- return image.Config{}, errors.New("config needs a filename")
- }
-
- // Check cache for image config.
- ic.RLock()
- config, ok := ic.imageConfigCache[filename]
- ic.RUnlock()
-
- if ok {
- return config, nil
- }
-
- f, err := ic.fs.WorkingDir.Open(filename)
- if err != nil {
- return image.Config{}, err
- }
-
- config, _, err = image.DecodeConfig(f)
-
- ic.Lock()
- ic.imageConfigCache[filename] = config
- ic.Unlock()
-
- return config, err
-}
-
-// in returns whether v is in the set l. l may be an array or slice.
-func in(l interface{}, v interface{}) bool {
- lv := reflect.ValueOf(l)
- vv := reflect.ValueOf(v)
-
- switch lv.Kind() {
- case reflect.Array, reflect.Slice:
- for i := 0; i < lv.Len(); i++ {
- lvv := lv.Index(i)
- lvv, isNil := indirect(lvv)
- if isNil {
- continue
- }
- switch lvv.Kind() {
- case reflect.String:
- if vv.Type() == lvv.Type() && vv.String() == lvv.String() {
- return true
- }
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- switch vv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- if vv.Int() == lvv.Int() {
- return true
- }
- }
- case reflect.Float32, reflect.Float64:
- switch vv.Kind() {
- case reflect.Float32, reflect.Float64:
- if vv.Float() == lvv.Float() {
- return true
- }
- }
- }
- }
- case reflect.String:
- if vv.Type() == lv.Type() && strings.Contains(lv.String(), vv.String()) {
- return true
- }
- }
- return false
-}
-
-// first returns the first N items in a rangeable list.
-func first(limit interface{}, seq interface{}) (interface{}, error) {
- if limit == nil || seq == nil {
- return nil, errors.New("both limit and seq must be provided")
- }
-
- limitv, err := cast.ToIntE(limit)
-
- if err != nil {
- return nil, err
- }
-
- if limitv < 1 {
- return nil, errors.New("can't return negative/empty count of items from sequence")
- }
-
- seqv := reflect.ValueOf(seq)
- seqv, isNil := indirect(seqv)
- if isNil {
- return nil, errors.New("can't iterate over a nil value")
- }
-
- switch seqv.Kind() {
- case reflect.Array, reflect.Slice, reflect.String:
- // okay
- default:
- return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
- }
- if limitv > seqv.Len() {
- limitv = seqv.Len()
- }
- return seqv.Slice(0, limitv).Interface(), nil
-}
-
-// findRE returns a list of strings that match the regular expression. By default all matches
-// will be included. The number of matches can be limited with an optional third parameter.
-func findRE(expr string, content interface{}, limit ...interface{}) ([]string, error) {
- re, err := reCache.Get(expr)
- if err != nil {
- return nil, err
- }
-
- conv, err := cast.ToStringE(content)
- if err != nil {
- return nil, err
- }
-
- if len(limit) == 0 {
- return re.FindAllString(conv, -1), nil
- }
-
- lim, err := cast.ToIntE(limit[0])
- if err != nil {
- return nil, err
- }
-
- return re.FindAllString(conv, lim), nil
-}
-
-// last returns the last N items in a rangeable list.
-func last(limit interface{}, seq interface{}) (interface{}, error) {
- if limit == nil || seq == nil {
- return nil, errors.New("both limit and seq must be provided")
- }
-
- limitv, err := cast.ToIntE(limit)
-
- if err != nil {
- return nil, err
- }
-
- if limitv < 1 {
- return nil, errors.New("can't return negative/empty count of items from sequence")
- }
-
- seqv := reflect.ValueOf(seq)
- seqv, isNil := indirect(seqv)
- if isNil {
- return nil, errors.New("can't iterate over a nil value")
- }
-
- switch seqv.Kind() {
- case reflect.Array, reflect.Slice, reflect.String:
- // okay
- default:
- return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
- }
- if limitv > seqv.Len() {
- limitv = seqv.Len()
- }
- return seqv.Slice(seqv.Len()-limitv, seqv.Len()).Interface(), nil
-}
-
-// after returns all the items after the first N in a rangeable list.
-func after(index interface{}, seq interface{}) (interface{}, error) {
- if index == nil || seq == nil {
- return nil, errors.New("both limit and seq must be provided")
- }
-
- indexv, err := cast.ToIntE(index)
-
- if err != nil {
- return nil, err
- }
-
- if indexv < 1 {
- return nil, errors.New("can't return negative/empty count of items from sequence")
- }
-
- seqv := reflect.ValueOf(seq)
- seqv, isNil := indirect(seqv)
- if isNil {
- return nil, errors.New("can't iterate over a nil value")
- }
-
- switch seqv.Kind() {
- case reflect.Array, reflect.Slice, reflect.String:
- // okay
- default:
- return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
- }
- if indexv >= seqv.Len() {
- return nil, errors.New("no items left")
- }
- return seqv.Slice(indexv, seqv.Len()).Interface(), nil
-}
-
-// shuffle returns the given rangeable list in a randomised order.
-func shuffle(seq interface{}) (interface{}, error) {
- if seq == nil {
- return nil, errors.New("both count and seq must be provided")
- }
-
- seqv := reflect.ValueOf(seq)
- seqv, isNil := indirect(seqv)
- if isNil {
- return nil, errors.New("can't iterate over a nil value")
- }
-
- switch seqv.Kind() {
- case reflect.Array, reflect.Slice, reflect.String:
- // okay
- default:
- return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
- }
-
- shuffled := reflect.MakeSlice(reflect.TypeOf(seq), seqv.Len(), seqv.Len())
-
- rand.Seed(time.Now().UTC().UnixNano())
- randomIndices := rand.Perm(seqv.Len())
-
- for index, value := range randomIndices {
- shuffled.Index(value).Set(seqv.Index(index))
- }
-
- return shuffled.Interface(), nil
-}
-
-func evaluateSubElem(obj reflect.Value, elemName string) (reflect.Value, error) {
- if !obj.IsValid() {
- return zero, errors.New("can't evaluate an invalid value")
- }
- typ := obj.Type()
- obj, isNil := indirect(obj)
-
- // first, check whether obj has a method. In this case, obj is
- // an interface, a struct or its pointer. If obj is a struct,
- // to check all T and *T method, use obj pointer type Value
- objPtr := obj
- if objPtr.Kind() != reflect.Interface && objPtr.CanAddr() {
- objPtr = objPtr.Addr()
- }
- mt, ok := objPtr.Type().MethodByName(elemName)
- if ok {
- if mt.PkgPath != "" {
- return zero, fmt.Errorf("%s is an unexported method of type %s", elemName, typ)
- }
- // struct pointer has one receiver argument and interface doesn't have an argument
- if mt.Type.NumIn() > 1 || mt.Type.NumOut() == 0 || mt.Type.NumOut() > 2 {
- return zero, fmt.Errorf("%s is a method of type %s but doesn't satisfy requirements", elemName, typ)
- }
- if mt.Type.NumOut() == 1 && mt.Type.Out(0).Implements(errorType) {
- return zero, fmt.Errorf("%s is a method of type %s but doesn't satisfy requirements", elemName, typ)
- }
- if mt.Type.NumOut() == 2 && !mt.Type.Out(1).Implements(errorType) {
- return zero, fmt.Errorf("%s is a method of type %s but doesn't satisfy requirements", elemName, typ)
- }
- res := objPtr.Method(mt.Index).Call([]reflect.Value{})
- if len(res) == 2 && !res[1].IsNil() {
- return zero, fmt.Errorf("error at calling a method %s of type %s: %s", elemName, typ, res[1].Interface().(error))
- }
- return res[0], nil
- }
-
- // elemName isn't a method so next start to check whether it is
- // a struct field or a map value. In both cases, it mustn't be
- // a nil value
- if isNil {
- return zero, fmt.Errorf("can't evaluate a nil pointer of type %s by a struct field or map key name %s", typ, elemName)
- }
- switch obj.Kind() {
- case reflect.Struct:
- ft, ok := obj.Type().FieldByName(elemName)
- if ok {
- if ft.PkgPath != "" && !ft.Anonymous {
- return zero, fmt.Errorf("%s is an unexported field of struct type %s", elemName, typ)
- }
- return obj.FieldByIndex(ft.Index), nil
- }
- return zero, fmt.Errorf("%s isn't a field of struct type %s", elemName, typ)
- case reflect.Map:
- kv := reflect.ValueOf(elemName)
- if kv.Type().AssignableTo(obj.Type().Key()) {
- return obj.MapIndex(kv), nil
- }
- return zero, fmt.Errorf("%s isn't a key of map type %s", elemName, typ)
- }
- return zero, fmt.Errorf("%s is neither a struct field, a method nor a map element of type %s", elemName, typ)
-}
-
-func checkCondition(v, mv reflect.Value, op string) (bool, error) {
- v, vIsNil := indirect(v)
- if !v.IsValid() {
- vIsNil = true
- }
- mv, mvIsNil := indirect(mv)
- if !mv.IsValid() {
- mvIsNil = true
- }
- if vIsNil || mvIsNil {
- switch op {
- case "", "=", "==", "eq":
- return vIsNil == mvIsNil, nil
- case "!=", "<>", "ne":
- return vIsNil != mvIsNil, nil
- }
- return false, nil
- }
-
- if v.Kind() == reflect.Bool && mv.Kind() == reflect.Bool {
- switch op {
- case "", "=", "==", "eq":
- return v.Bool() == mv.Bool(), nil
- case "!=", "<>", "ne":
- return v.Bool() != mv.Bool(), nil
- }
- return false, nil
- }
-
- var ivp, imvp *int64
- var svp, smvp *string
- var slv, slmv interface{}
- var ima []int64
- var sma []string
- if mv.Type() == v.Type() {
- switch v.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- iv := v.Int()
- ivp = &iv
- imv := mv.Int()
- imvp = &imv
- case reflect.String:
- sv := v.String()
- svp = &sv
- smv := mv.String()
- smvp = &smv
- case reflect.Struct:
- switch v.Type() {
- case timeType:
- iv := toTimeUnix(v)
- ivp = &iv
- imv := toTimeUnix(mv)
- imvp = &imv
- }
- case reflect.Array, reflect.Slice:
- slv = v.Interface()
- slmv = mv.Interface()
- }
- } else {
- if mv.Kind() != reflect.Array && mv.Kind() != reflect.Slice {
- return false, nil
- }
-
- if mv.Len() == 0 {
- return false, nil
- }
-
- if v.Kind() != reflect.Interface && mv.Type().Elem().Kind() != reflect.Interface && mv.Type().Elem() != v.Type() {
- return false, nil
- }
- switch v.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- iv := v.Int()
- ivp = &iv
- for i := 0; i < mv.Len(); i++ {
- if anInt := toInt(mv.Index(i)); anInt != -1 {
- ima = append(ima, anInt)
- }
-
- }
- case reflect.String:
- sv := v.String()
- svp = &sv
- for i := 0; i < mv.Len(); i++ {
- if aString := toString(mv.Index(i)); aString != "" {
- sma = append(sma, aString)
- }
- }
- case reflect.Struct:
- switch v.Type() {
- case timeType:
- iv := toTimeUnix(v)
- ivp = &iv
- for i := 0; i < mv.Len(); i++ {
- ima = append(ima, toTimeUnix(mv.Index(i)))
- }
- }
- }
- }
-
- switch op {
- case "", "=", "==", "eq":
- if ivp != nil && imvp != nil {
- return *ivp == *imvp, nil
- } else if svp != nil && smvp != nil {
- return *svp == *smvp, nil
- }
- case "!=", "<>", "ne":
- if ivp != nil && imvp != nil {
- return *ivp != *imvp, nil
- } else if svp != nil && smvp != nil {
- return *svp != *smvp, nil
- }
- case ">=", "ge":
- if ivp != nil && imvp != nil {
- return *ivp >= *imvp, nil
- } else if svp != nil && smvp != nil {
- return *svp >= *smvp, nil
- }
- case ">", "gt":
- if ivp != nil && imvp != nil {
- return *ivp > *imvp, nil
- } else if svp != nil && smvp != nil {
- return *svp > *smvp, nil
- }
- case "<=", "le":
- if ivp != nil && imvp != nil {
- return *ivp <= *imvp, nil
- } else if svp != nil && smvp != nil {
- return *svp <= *smvp, nil
- }
- case "<", "lt":
- if ivp != nil && imvp != nil {
- return *ivp < *imvp, nil
- } else if svp != nil && smvp != nil {
- return *svp < *smvp, nil
- }
- case "in", "not in":
- var r bool
- if ivp != nil && len(ima) > 0 {
- r = in(ima, *ivp)
- } else if svp != nil {
- if len(sma) > 0 {
- r = in(sma, *svp)
- } else if smvp != nil {
- r = in(*smvp, *svp)
- }
- } else {
- return false, nil
- }
- if op == "not in" {
- return !r, nil
- }
- return r, nil
- case "intersect":
- r, err := intersect(slv, slmv)
- if err != nil {
- return false, err
- }
-
- if reflect.TypeOf(r).Kind() == reflect.Slice {
- s := reflect.ValueOf(r)
-
- if s.Len() > 0 {
- return true, nil
- }
- return false, nil
- }
- return false, errors.New("invalid intersect values")
- default:
- return false, errors.New("no such operator")
- }
- return false, nil
-}
-
-// parseWhereArgs parses the end arguments to the where function. Return a
-// match value and an operator, if one is defined.
-func parseWhereArgs(args ...interface{}) (mv reflect.Value, op string, err error) {
- switch len(args) {
- case 1:
- mv = reflect.ValueOf(args[0])
- case 2:
- var ok bool
- if op, ok = args[0].(string); !ok {
- err = errors.New("operator argument must be string type")
- return
- }
- op = strings.TrimSpace(strings.ToLower(op))
- mv = reflect.ValueOf(args[1])
- default:
- err = errors.New("can't evaluate the array by no match argument or more than or equal to two arguments")
- }
- return
-}
-
-// checkWhereArray handles the where-matching logic when the seqv value is an
-// Array or Slice.
-func checkWhereArray(seqv, kv, mv reflect.Value, path []string, op string) (interface{}, error) {
- rv := reflect.MakeSlice(seqv.Type(), 0, 0)
- for i := 0; i < seqv.Len(); i++ {
- var vvv reflect.Value
- rvv := seqv.Index(i)
- if kv.Kind() == reflect.String {
- vvv = rvv
- for _, elemName := range path {
- var err error
- vvv, err = evaluateSubElem(vvv, elemName)