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