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 }