diff options
Diffstat (limited to 'tpl/tplimpl/template_funcs.go')
-rw-r--r-- | tpl/tplimpl/template_funcs.go | 2217 |
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 |