hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

where.go (14052B)

    1 // Copyright 2017 The Hugo Authors. All rights reserved.
    2 //
    3 // Licensed under the Apache License, Version 2.0 (the "License");
    4 // you may not use this file except in compliance with the License.
    5 // You may obtain a copy of the License at
    6 // http://www.apache.org/licenses/LICENSE-2.0
    7 //
    8 // Unless required by applicable law or agreed to in writing, software
    9 // distributed under the License is distributed on an "AS IS" BASIS,
   10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   11 // See the License for the specific language governing permissions and
   12 // limitations under the License.
   13 
   14 package collections
   15 
   16 import (
   17 	"errors"
   18 	"fmt"
   19 	"reflect"
   20 	"strings"
   21 
   22 	"github.com/gohugoio/hugo/common/hreflect"
   23 	"github.com/gohugoio/hugo/common/maps"
   24 )
   25 
   26 // Where returns a filtered subset of a given data type.
   27 func (ns *Namespace) Where(seq, key any, args ...any) (any, error) {
   28 	seqv, isNil := indirect(reflect.ValueOf(seq))
   29 	if isNil {
   30 		return nil, errors.New("can't iterate over a nil value of type " + reflect.ValueOf(seq).Type().String())
   31 	}
   32 
   33 	mv, op, err := parseWhereArgs(args...)
   34 	if err != nil {
   35 		return nil, err
   36 	}
   37 
   38 	var path []string
   39 	kv := reflect.ValueOf(key)
   40 	if kv.Kind() == reflect.String {
   41 		path = strings.Split(strings.Trim(kv.String(), "."), ".")
   42 	}
   43 
   44 	switch seqv.Kind() {
   45 	case reflect.Array, reflect.Slice:
   46 		return ns.checkWhereArray(seqv, kv, mv, path, op)
   47 	case reflect.Map:
   48 		return ns.checkWhereMap(seqv, kv, mv, path, op)
   49 	default:
   50 		return nil, fmt.Errorf("can't iterate over %v", seq)
   51 	}
   52 }
   53 
   54 func (ns *Namespace) checkCondition(v, mv reflect.Value, op string) (bool, error) {
   55 	v, vIsNil := indirect(v)
   56 	if !v.IsValid() {
   57 		vIsNil = true
   58 	}
   59 
   60 	mv, mvIsNil := indirect(mv)
   61 	if !mv.IsValid() {
   62 		mvIsNil = true
   63 	}
   64 	if vIsNil || mvIsNil {
   65 		switch op {
   66 		case "", "=", "==", "eq":
   67 			return vIsNil == mvIsNil, nil
   68 		case "!=", "<>", "ne":
   69 			return vIsNil != mvIsNil, nil
   70 		}
   71 		return false, nil
   72 	}
   73 
   74 	if v.Kind() == reflect.Bool && mv.Kind() == reflect.Bool {
   75 		switch op {
   76 		case "", "=", "==", "eq":
   77 			return v.Bool() == mv.Bool(), nil
   78 		case "!=", "<>", "ne":
   79 			return v.Bool() != mv.Bool(), nil
   80 		}
   81 		return false, nil
   82 	}
   83 
   84 	var ivp, imvp *int64
   85 	var fvp, fmvp *float64
   86 	var svp, smvp *string
   87 	var slv, slmv any
   88 	var ima []int64
   89 	var fma []float64
   90 	var sma []string
   91 
   92 	if mv.Kind() == v.Kind() {
   93 		switch v.Kind() {
   94 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   95 			iv := v.Int()
   96 			ivp = &iv
   97 			imv := mv.Int()
   98 			imvp = &imv
   99 		case reflect.String:
  100 			sv := v.String()
  101 			svp = &sv
  102 			smv := mv.String()
  103 			smvp = &smv
  104 		case reflect.Float64:
  105 			fv := v.Float()
  106 			fvp = &fv
  107 			fmv := mv.Float()
  108 			fmvp = &fmv
  109 		case reflect.Struct:
  110 			if hreflect.IsTime(v.Type()) {
  111 				iv := ns.toTimeUnix(v)
  112 				ivp = &iv
  113 				imv := ns.toTimeUnix(mv)
  114 				imvp = &imv
  115 			}
  116 		case reflect.Array, reflect.Slice:
  117 			slv = v.Interface()
  118 			slmv = mv.Interface()
  119 		}
  120 	} else if isNumber(v.Kind()) && isNumber(mv.Kind()) {
  121 		fv, err := toFloat(v)
  122 		if err != nil {
  123 			return false, err
  124 		}
  125 		fvp = &fv
  126 		fmv, err := toFloat(mv)
  127 		if err != nil {
  128 			return false, err
  129 		}
  130 		fmvp = &fmv
  131 	} else {
  132 		if mv.Kind() != reflect.Array && mv.Kind() != reflect.Slice {
  133 			return false, nil
  134 		}
  135 
  136 		if mv.Len() == 0 {
  137 			return false, nil
  138 		}
  139 
  140 		if v.Kind() != reflect.Interface && mv.Type().Elem().Kind() != reflect.Interface && mv.Type().Elem() != v.Type() && v.Kind() != reflect.Array && v.Kind() != reflect.Slice {
  141 			return false, nil
  142 		}
  143 		switch v.Kind() {
  144 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  145 			iv := v.Int()
  146 			ivp = &iv
  147 			for i := 0; i < mv.Len(); i++ {
  148 				if anInt, err := toInt(mv.Index(i)); err == nil {
  149 					ima = append(ima, anInt)
  150 				}
  151 			}
  152 		case reflect.String:
  153 			sv := v.String()
  154 			svp = &sv
  155 			for i := 0; i < mv.Len(); i++ {
  156 				if aString, err := toString(mv.Index(i)); err == nil {
  157 					sma = append(sma, aString)
  158 				}
  159 			}
  160 		case reflect.Float64:
  161 			fv := v.Float()
  162 			fvp = &fv
  163 			for i := 0; i < mv.Len(); i++ {
  164 				if aFloat, err := toFloat(mv.Index(i)); err == nil {
  165 					fma = append(fma, aFloat)
  166 				}
  167 			}
  168 		case reflect.Struct:
  169 			if hreflect.IsTime(v.Type()) {
  170 				iv := ns.toTimeUnix(v)
  171 				ivp = &iv
  172 				for i := 0; i < mv.Len(); i++ {
  173 					ima = append(ima, ns.toTimeUnix(mv.Index(i)))
  174 				}
  175 			}
  176 		case reflect.Array, reflect.Slice:
  177 			slv = v.Interface()
  178 			slmv = mv.Interface()
  179 		}
  180 	}
  181 
  182 	switch op {
  183 	case "", "=", "==", "eq":
  184 		switch {
  185 		case ivp != nil && imvp != nil:
  186 			return *ivp == *imvp, nil
  187 		case svp != nil && smvp != nil:
  188 			return *svp == *smvp, nil
  189 		case fvp != nil && fmvp != nil:
  190 			return *fvp == *fmvp, nil
  191 		}
  192 	case "!=", "<>", "ne":
  193 		switch {
  194 		case ivp != nil && imvp != nil:
  195 			return *ivp != *imvp, nil
  196 		case svp != nil && smvp != nil:
  197 			return *svp != *smvp, nil
  198 		case fvp != nil && fmvp != nil:
  199 			return *fvp != *fmvp, nil
  200 		}
  201 	case ">=", "ge":
  202 		switch {
  203 		case ivp != nil && imvp != nil:
  204 			return *ivp >= *imvp, nil
  205 		case svp != nil && smvp != nil:
  206 			return *svp >= *smvp, nil
  207 		case fvp != nil && fmvp != nil:
  208 			return *fvp >= *fmvp, nil
  209 		}
  210 	case ">", "gt":
  211 		switch {
  212 		case ivp != nil && imvp != nil:
  213 			return *ivp > *imvp, nil
  214 		case svp != nil && smvp != nil:
  215 			return *svp > *smvp, nil
  216 		case fvp != nil && fmvp != nil:
  217 			return *fvp > *fmvp, nil
  218 		}
  219 	case "<=", "le":
  220 		switch {
  221 		case ivp != nil && imvp != nil:
  222 			return *ivp <= *imvp, nil
  223 		case svp != nil && smvp != nil:
  224 			return *svp <= *smvp, nil
  225 		case fvp != nil && fmvp != nil:
  226 			return *fvp <= *fmvp, nil
  227 		}
  228 	case "<", "lt":
  229 		switch {
  230 		case ivp != nil && imvp != nil:
  231 			return *ivp < *imvp, nil
  232 		case svp != nil && smvp != nil:
  233 			return *svp < *smvp, nil
  234 		case fvp != nil && fmvp != nil:
  235 			return *fvp < *fmvp, nil
  236 		}
  237 	case "in", "not in":
  238 		var r bool
  239 		switch {
  240 		case ivp != nil && len(ima) > 0:
  241 			r, _ = ns.In(ima, *ivp)
  242 		case fvp != nil && len(fma) > 0:
  243 			r, _ = ns.In(fma, *fvp)
  244 		case svp != nil:
  245 			if len(sma) > 0 {
  246 				r, _ = ns.In(sma, *svp)
  247 			} else if smvp != nil {
  248 				r, _ = ns.In(*smvp, *svp)
  249 			}
  250 		default:
  251 			return false, nil
  252 		}
  253 		if op == "not in" {
  254 			return !r, nil
  255 		}
  256 		return r, nil
  257 	case "intersect":
  258 		r, err := ns.Intersect(slv, slmv)
  259 		if err != nil {
  260 			return false, err
  261 		}
  262 
  263 		if reflect.TypeOf(r).Kind() == reflect.Slice {
  264 			s := reflect.ValueOf(r)
  265 
  266 			if s.Len() > 0 {
  267 				return true, nil
  268 			}
  269 			return false, nil
  270 		}
  271 		return false, errors.New("invalid intersect values")
  272 	default:
  273 		return false, errors.New("no such operator")
  274 	}
  275 	return false, nil
  276 }
  277 
  278 func evaluateSubElem(obj reflect.Value, elemName string) (reflect.Value, error) {
  279 	if !obj.IsValid() {
  280 		return zero, errors.New("can't evaluate an invalid value")
  281 	}
  282 
  283 	typ := obj.Type()
  284 	obj, isNil := indirect(obj)
  285 
  286 	if obj.Kind() == reflect.Interface {
  287 		// If obj is an interface, we need to inspect the value it contains
  288 		// to see the full set of methods and fields.
  289 		// Indirect returns the value that it points to, which is what's needed
  290 		// below to be able to reflect on its fields.
  291 		obj = reflect.Indirect(obj.Elem())
  292 	}
  293 
  294 	// first, check whether obj has a method. In this case, obj is
  295 	// a struct or its pointer. If obj is a struct,
  296 	// to check all T and *T method, use obj pointer type Value
  297 	objPtr := obj
  298 	if objPtr.Kind() != reflect.Interface && objPtr.CanAddr() {
  299 		objPtr = objPtr.Addr()
  300 	}
  301 
  302 	index := hreflect.GetMethodIndexByName(objPtr.Type(), elemName)
  303 	if index != -1 {
  304 		mt := objPtr.Type().Method(index)
  305 		switch {
  306 		case mt.PkgPath != "":
  307 			return zero, fmt.Errorf("%s is an unexported method of type %s", elemName, typ)
  308 		case mt.Type.NumIn() > 1:
  309 			return zero, fmt.Errorf("%s is a method of type %s but requires more than 1 parameter", elemName, typ)
  310 		case mt.Type.NumOut() == 0:
  311 			return zero, fmt.Errorf("%s is a method of type %s but returns no output", elemName, typ)
  312 		case mt.Type.NumOut() > 2:
  313 			return zero, fmt.Errorf("%s is a method of type %s but returns more than 2 outputs", elemName, typ)
  314 		case mt.Type.NumOut() == 1 && mt.Type.Out(0).Implements(errorType):
  315 			return zero, fmt.Errorf("%s is a method of type %s but only returns an error type", elemName, typ)
  316 		case mt.Type.NumOut() == 2 && !mt.Type.Out(1).Implements(errorType):
  317 			return zero, fmt.Errorf("%s is a method of type %s returning two values but the second value is not an error type", elemName, typ)
  318 		}
  319 		res := objPtr.Method(mt.Index).Call([]reflect.Value{})
  320 		if len(res) == 2 && !res[1].IsNil() {
  321 			return zero, fmt.Errorf("error at calling a method %s of type %s: %s", elemName, typ, res[1].Interface().(error))
  322 		}
  323 		return res[0], nil
  324 	}
  325 
  326 	// elemName isn't a method so next start to check whether it is
  327 	// a struct field or a map value. In both cases, it mustn't be
  328 	// a nil value
  329 	if isNil {
  330 		return zero, fmt.Errorf("can't evaluate a nil pointer of type %s by a struct field or map key name %s", typ, elemName)
  331 	}
  332 	switch obj.Kind() {
  333 	case reflect.Struct:
  334 		ft, ok := obj.Type().FieldByName(elemName)
  335 		if ok {
  336 			if ft.PkgPath != "" && !ft.Anonymous {
  337 				return zero, fmt.Errorf("%s is an unexported field of struct type %s", elemName, typ)
  338 			}
  339 			return obj.FieldByIndex(ft.Index), nil
  340 		}
  341 		return zero, fmt.Errorf("%s isn't a field of struct type %s", elemName, typ)
  342 	case reflect.Map:
  343 		kv := reflect.ValueOf(elemName)
  344 		if kv.Type().AssignableTo(obj.Type().Key()) {
  345 			return obj.MapIndex(kv), nil
  346 		}
  347 		return zero, fmt.Errorf("%s isn't a key of map type %s", elemName, typ)
  348 	}
  349 	return zero, fmt.Errorf("%s is neither a struct field, a method nor a map element of type %s", elemName, typ)
  350 }
  351 
  352 // parseWhereArgs parses the end arguments to the where function.  Return a
  353 // match value and an operator, if one is defined.
  354 func parseWhereArgs(args ...any) (mv reflect.Value, op string, err error) {
  355 	switch len(args) {
  356 	case 1:
  357 		mv = reflect.ValueOf(args[0])
  358 	case 2:
  359 		var ok bool
  360 		if op, ok = args[0].(string); !ok {
  361 			err = errors.New("operator argument must be string type")
  362 			return
  363 		}
  364 		op = strings.TrimSpace(strings.ToLower(op))
  365 		mv = reflect.ValueOf(args[1])
  366 	default:
  367 		err = errors.New("can't evaluate the array by no match argument or more than or equal to two arguments")
  368 	}
  369 	return
  370 }
  371 
  372 // checkWhereArray handles the where-matching logic when the seqv value is an
  373 // Array or Slice.
  374 func (ns *Namespace) checkWhereArray(seqv, kv, mv reflect.Value, path []string, op string) (any, error) {
  375 	rv := reflect.MakeSlice(seqv.Type(), 0, 0)
  376 
  377 	for i := 0; i < seqv.Len(); i++ {
  378 		var vvv reflect.Value
  379 		rvv := seqv.Index(i)
  380 
  381 		if kv.Kind() == reflect.String {
  382 			if params, ok := rvv.Interface().(maps.Params); ok {
  383 				vvv = reflect.ValueOf(params.Get(path...))
  384 			} else {
  385 				vvv = rvv
  386 				for i, elemName := range path {
  387 					var err error
  388 					vvv, err = evaluateSubElem(vvv, elemName)
  389 
  390 					if err != nil {
  391 						continue
  392 					}
  393 
  394 					if i < len(path)-1 && vvv.IsValid() {
  395 						if params, ok := vvv.Interface().(maps.Params); ok {
  396 							// The current path element is the map itself, .Params.
  397 							vvv = reflect.ValueOf(params.Get(path[i+1:]...))
  398 							break
  399 						}
  400 					}
  401 				}
  402 			}
  403 		} else {
  404 			vv, _ := indirect(rvv)
  405 			if vv.Kind() == reflect.Map && kv.Type().AssignableTo(vv.Type().Key()) {
  406 				vvv = vv.MapIndex(kv)
  407 			}
  408 		}
  409 
  410 		if ok, err := ns.checkCondition(vvv, mv, op); ok {
  411 			rv = reflect.Append(rv, rvv)
  412 		} else if err != nil {
  413 			return nil, err
  414 		}
  415 	}
  416 	return rv.Interface(), nil
  417 }
  418 
  419 // checkWhereMap handles the where-matching logic when the seqv value is a Map.
  420 func (ns *Namespace) checkWhereMap(seqv, kv, mv reflect.Value, path []string, op string) (any, error) {
  421 	rv := reflect.MakeMap(seqv.Type())
  422 	keys := seqv.MapKeys()
  423 	for _, k := range keys {
  424 		elemv := seqv.MapIndex(k)
  425 		switch elemv.Kind() {
  426 		case reflect.Array, reflect.Slice:
  427 			r, err := ns.checkWhereArray(elemv, kv, mv, path, op)
  428 			if err != nil {
  429 				return nil, err
  430 			}
  431 
  432 			switch rr := reflect.ValueOf(r); rr.Kind() {
  433 			case reflect.Slice:
  434 				if rr.Len() > 0 {
  435 					rv.SetMapIndex(k, elemv)
  436 				}
  437 			}
  438 		case reflect.Interface:
  439 			elemvv, isNil := indirect(elemv)
  440 			if isNil {
  441 				continue
  442 			}
  443 
  444 			switch elemvv.Kind() {
  445 			case reflect.Array, reflect.Slice:
  446 				r, err := ns.checkWhereArray(elemvv, kv, mv, path, op)
  447 				if err != nil {
  448 					return nil, err
  449 				}
  450 
  451 				switch rr := reflect.ValueOf(r); rr.Kind() {
  452 				case reflect.Slice:
  453 					if rr.Len() > 0 {
  454 						rv.SetMapIndex(k, elemv)
  455 					}
  456 				}
  457 			}
  458 		}
  459 	}
  460 	return rv.Interface(), nil
  461 }
  462 
  463 // toFloat returns the float value if possible.
  464 func toFloat(v reflect.Value) (float64, error) {
  465 	switch v.Kind() {
  466 	case reflect.Float32, reflect.Float64:
  467 		return v.Float(), nil
  468 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  469 		return v.Convert(reflect.TypeOf(float64(0))).Float(), nil
  470 	case reflect.Interface:
  471 		return toFloat(v.Elem())
  472 	}
  473 	return -1, errors.New("unable to convert value to float")
  474 }
  475 
  476 // toInt returns the int value if possible, -1 if not.
  477 // TODO(bep) consolidate all these reflect funcs.
  478 func toInt(v reflect.Value) (int64, error) {
  479 	switch v.Kind() {
  480 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  481 		return v.Int(), nil
  482 	case reflect.Interface:
  483 		return toInt(v.Elem())
  484 	}
  485 	return -1, errors.New("unable to convert value to int")
  486 }
  487 
  488 func toUint(v reflect.Value) (uint64, error) {
  489 	switch v.Kind() {
  490 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  491 		return v.Uint(), nil
  492 	case reflect.Interface:
  493 		return toUint(v.Elem())
  494 	}
  495 	return 0, errors.New("unable to convert value to uint")
  496 }
  497 
  498 // toString returns the string value if possible, "" if not.
  499 func toString(v reflect.Value) (string, error) {
  500 	switch v.Kind() {
  501 	case reflect.String:
  502 		return v.String(), nil
  503 	case reflect.Interface:
  504 		return toString(v.Elem())
  505 	}
  506 	return "", errors.New("unable to convert value to string")
  507 }
  508 
  509 func (ns *Namespace) toTimeUnix(v reflect.Value) int64 {
  510 	t, ok := hreflect.AsTime(v, ns.loc)
  511 	if !ok {
  512 		panic("coding error: argument must be time.Time type reflect Value")
  513 	}
  514 	return t.Unix()
  515 }