summaryrefslogtreecommitdiffstats
path: root/tpl/tplimpl/template_funcs.go
diff options
context:
space:
mode:
Diffstat (limited to 'tpl/tplimpl/template_funcs.go')
-rw-r--r--tpl/tplimpl/template_funcs.go2217
1 files changed, 2217 insertions, 0 deletions
diff --git a/tpl/tplimpl/template_funcs.go b/tpl/tplimpl/template_funcs.go
new file mode 100644
index 000000000..dae621ac3
--- /dev/null
+++ b/tpl/tplimpl/template_funcs.go
@@ -0,0 +1,2217 @@
+// Copyright 2016 The Hugo Authors. All rights reserved.
+//
+// Portions Copyright The Go Authors.
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+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/bep/inflect"
+ "github.com/spf13/afero"
+ "github.com/spf13/cast"
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/helpers"
+ jww "github.com/spf13/jwalterweatherman"
+
+ // Importing image codecs for image.DecodeConfig
+ _ "image/gif"
+ _ "image/jpeg"
+ _ "image/png"
+)
+
+// Some of the template funcs are'nt entirely stateless.
+type templateFuncster struct {
+ funcMap template.FuncMap
+ cachedPartials partialCache
+ *deps.Deps
+}
+
+func newTemplateFuncster(deps *deps.Deps) *templateFuncster {
+ return &templateFuncster{
+ Deps: deps,
+ cachedPartials: partialCache{p: make(map[string]template.HTML)},
+ }
+}
+
+// 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())
+ }
+}
+
+// ResetCaches resets all caches that might be used during build.
+// TODO(bep) globals move image config cache to funcster
+func ResetCaches() {
+ resetImageConfigCache()
+}
+
+// imageConfigCache is a lockable cache for image.Config objects. It must be
+// locked before reading or writing to config.
+type imageConfigCache struct {
+ config map[string]image.Config
+ sync.RWMutex
+}
+
+var defaultImageConfigCache = imageConfigCache{
+ config: map[string]image.Config{},
+}
+
+// resetImageConfigCache initializes and resets the imageConfig cache for the
+// imageConfig template function. This should be run once before every batch of
+// template renderers so the cache is cleared for new data.
+func resetImageConfigCache() {
+ defaultImageConfigCache.Lock()
+ defer defaultImageConfigCache.Unlock()
+
+ defaultImageConfigCache.config = map[string]image.Config{}
+}
+
+// imageConfig returns the image.Config for the specified path relative to the
+// working directory. resetImageConfigCache must be run beforehand.
+func (t *templateFuncster) imageConfig(path interface{}) (image.Config, error) {
+ filename, err := cast.ToStringE(path)
+ if err != nil {
+ return image.Config{}, err
+ }
+
+ if filename == "" {
+ return image.Config{}, errors.New("imageConfig needs a filename")
+ }
+
+ // Check cache for image config.
+ defaultImageConfigCache.RLock()
+ config, ok := defaultImageConfigCache.config[filename]
+ defaultImageConfigCache.RUnlock()
+
+ if ok {
+ return config, nil
+ }
+
+ f, err := t.Fs.WorkingDir.Open(filename)
+ if err != nil {
+ return image.Config{}, err
+ }
+
+ config, _, err = image.DecodeConfig(f)
+
+ defaultImageConfigCache.Lock()
+ defaultImageConfigCache.config[filename] = config
+ defaultImageConfigCache.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)
+ if err != nil {
+ return nil, err
+ }
+ }
+ } else {
+ vv, _ := indirect(rvv)
+ if vv.Kind() == reflect.Map && kv.Type().AssignableTo(vv.Type().Key()) {
+ vvv = vv.MapIndex(kv)
+ }
+ }
+
+ if ok, err := checkCondition(vvv, mv, op); ok {
+ rv = reflect.Append(rv, rvv)
+ } else if err != nil {
+ return nil, err
+ }
+ }
+ return rv.Interface(), nil
+}
+
+// checkWhereMap handles the where-matching logic when the seqv value is a Map.
+func checkWhereMap(seqv, kv, mv reflect.Value, path []string, op string) (interface{}, error) {
+ rv := reflect.MakeMap(seqv.Type())
+ keys := seqv.MapKeys()
+ for _, k := range keys {
+ elemv := seqv.MapIndex(k)
+ switch elemv.Kind() {
+ case reflect.Array, reflect.Slice:
+ r, err := checkWhereArray(elemv, kv, mv, path, op)
+ if err != nil {
+ return nil, err
+ }
+
+ switch rr := reflect.ValueOf(r); rr.Kind() {
+ case