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 }