collections.go (18509B)
1 // Copyright 2019 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 provides template functions for manipulating collections
15 // such as arrays, maps, and slices.
16 package collections
17
18 import (
19 "fmt"
20 "html/template"
21 "math/rand"
22 "net/url"
23 "reflect"
24 "strings"
25 "time"
26
27 "errors"
28
29 "github.com/gohugoio/hugo/common/collections"
30 "github.com/gohugoio/hugo/common/maps"
31 "github.com/gohugoio/hugo/common/types"
32 "github.com/gohugoio/hugo/deps"
33 "github.com/gohugoio/hugo/helpers"
34 "github.com/gohugoio/hugo/langs"
35 "github.com/gohugoio/hugo/tpl/compare"
36 "github.com/spf13/cast"
37 )
38
39 func init() {
40 // htime.Now cannot be used here
41 rand.Seed(time.Now().UTC().UnixNano())
42 }
43
44 // New returns a new instance of the collections-namespaced template functions.
45 func New(deps *deps.Deps) *Namespace {
46 if deps.Language == nil {
47 panic("language must be set")
48 }
49
50 loc := langs.GetLocation(deps.Language)
51
52 return &Namespace{
53 loc: loc,
54 sortComp: compare.New(loc, true),
55 deps: deps,
56 }
57 }
58
59 // Namespace provides template functions for the "collections" namespace.
60 type Namespace struct {
61 loc *time.Location
62 sortComp *compare.Namespace
63 deps *deps.Deps
64 }
65
66 // After returns all the items after the first N in a rangeable list.
67 func (ns *Namespace) After(index any, seq any) (any, error) {
68 if index == nil || seq == nil {
69 return nil, errors.New("both limit and seq must be provided")
70 }
71
72 indexv, err := cast.ToIntE(index)
73 if err != nil {
74 return nil, err
75 }
76
77 if indexv < 0 {
78 return nil, errors.New("sequence bounds out of range [" + cast.ToString(indexv) + ":]")
79 }
80
81 seqv := reflect.ValueOf(seq)
82 seqv, isNil := indirect(seqv)
83 if isNil {
84 return nil, errors.New("can't iterate over a nil value")
85 }
86
87 switch seqv.Kind() {
88 case reflect.Array, reflect.Slice, reflect.String:
89 // okay
90 default:
91 return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
92 }
93
94 if indexv >= seqv.Len() {
95 return seqv.Slice(0, 0).Interface(), nil
96 }
97
98 return seqv.Slice(indexv, seqv.Len()).Interface(), nil
99 }
100
101 // Delimit takes a given sequence and returns a delimited HTML string.
102 // If last is passed to the function, it will be used as the final delimiter.
103 func (ns *Namespace) Delimit(seq, delimiter any, last ...any) (template.HTML, error) {
104 d, err := cast.ToStringE(delimiter)
105 if err != nil {
106 return "", err
107 }
108
109 var dLast *string
110 if len(last) > 0 {
111 l := last[0]
112 dStr, err := cast.ToStringE(l)
113 if err != nil {
114 dLast = nil
115 } else {
116 dLast = &dStr
117 }
118 }
119
120 seqv := reflect.ValueOf(seq)
121 seqv, isNil := indirect(seqv)
122 if isNil {
123 return "", errors.New("can't iterate over a nil value")
124 }
125
126 var str string
127 switch seqv.Kind() {
128 case reflect.Map:
129 sortSeq, err := ns.Sort(seq)
130 if err != nil {
131 return "", err
132 }
133 seqv = reflect.ValueOf(sortSeq)
134 fallthrough
135 case reflect.Array, reflect.Slice, reflect.String:
136 for i := 0; i < seqv.Len(); i++ {
137 val := seqv.Index(i).Interface()
138 valStr, err := cast.ToStringE(val)
139 if err != nil {
140 continue
141 }
142 switch {
143 case i == seqv.Len()-2 && dLast != nil:
144 str += valStr + *dLast
145 case i == seqv.Len()-1:
146 str += valStr
147 default:
148 str += valStr + d
149 }
150 }
151
152 default:
153 return "", fmt.Errorf("can't iterate over %v", seq)
154 }
155
156 return template.HTML(str), nil
157 }
158
159 // Dictionary creates a map[string]interface{} from the given parameters by
160 // walking the parameters and treating them as key-value pairs. The number
161 // of parameters must be even.
162 // The keys can be string slices, which will create the needed nested structure.
163 func (ns *Namespace) Dictionary(values ...any) (map[string]any, error) {
164 if len(values)%2 != 0 {
165 return nil, errors.New("invalid dictionary call")
166 }
167
168 root := make(map[string]any)
169
170 for i := 0; i < len(values); i += 2 {
171 dict := root
172 var key string
173 switch v := values[i].(type) {
174 case string:
175 key = v
176 case []string:
177 for i := 0; i < len(v)-1; i++ {
178 key = v[i]
179 var m map[string]any
180 v, found := dict[key]
181 if found {
182 m = v.(map[string]any)
183 } else {
184 m = make(map[string]any)
185 dict[key] = m
186 }
187 dict = m
188 }
189 key = v[len(v)-1]
190 default:
191 return nil, errors.New("invalid dictionary key")
192 }
193 dict[key] = values[i+1]
194 }
195
196 return root, nil
197 }
198
199 // EchoParam returns a given value if it is set; otherwise, it returns an
200 // empty string.
201 func (ns *Namespace) EchoParam(a, key any) any {
202 av, isNil := indirect(reflect.ValueOf(a))
203 if isNil {
204 return ""
205 }
206
207 var avv reflect.Value
208 switch av.Kind() {
209 case reflect.Array, reflect.Slice:
210 index, ok := key.(int)
211 if ok && av.Len() > index {
212 avv = av.Index(index)
213 }
214 case reflect.Map:
215 kv := reflect.ValueOf(key)
216 if kv.Type().AssignableTo(av.Type().Key()) {
217 avv = av.MapIndex(kv)
218 }
219 }
220
221 avv, isNil = indirect(avv)
222
223 if isNil {
224 return ""
225 }
226
227 if avv.IsValid() {
228 switch avv.Kind() {
229 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
230 return avv.Int()
231 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
232 return avv.Uint()
233 case reflect.Float32, reflect.Float64:
234 return avv.Float()
235 case reflect.String:
236 return avv.String()
237 }
238 }
239
240 return ""
241 }
242
243 // First returns the first N items in a rangeable list.
244 func (ns *Namespace) First(limit any, seq any) (any, error) {
245 if limit == nil || seq == nil {
246 return nil, errors.New("both limit and seq must be provided")
247 }
248
249 limitv, err := cast.ToIntE(limit)
250 if err != nil {
251 return nil, err
252 }
253
254 if limitv < 0 {
255 return nil, errors.New("sequence length must be non-negative")
256 }
257
258 seqv := reflect.ValueOf(seq)
259 seqv, isNil := indirect(seqv)
260 if isNil {
261 return nil, errors.New("can't iterate over a nil value")
262 }
263
264 switch seqv.Kind() {
265 case reflect.Array, reflect.Slice, reflect.String:
266 // okay
267 default:
268 return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
269 }
270
271 if limitv > seqv.Len() {
272 limitv = seqv.Len()
273 }
274
275 return seqv.Slice(0, limitv).Interface(), nil
276 }
277
278 // In returns whether v is in the set l. l may be an array or slice.
279 func (ns *Namespace) In(l any, v any) (bool, error) {
280 if l == nil || v == nil {
281 return false, nil
282 }
283
284 lv := reflect.ValueOf(l)
285 vv := reflect.ValueOf(v)
286
287 vvk := normalize(vv)
288
289 switch lv.Kind() {
290 case reflect.Array, reflect.Slice:
291 for i := 0; i < lv.Len(); i++ {
292 lvv, isNil := indirectInterface(lv.Index(i))
293 if isNil {
294 continue
295 }
296
297 lvvk := normalize(lvv)
298
299 if lvvk == vvk {
300 return true, nil
301 }
302 }
303 }
304 ss, err := cast.ToStringE(l)
305 if err != nil {
306 return false, nil
307 }
308
309 su, err := cast.ToStringE(v)
310 if err != nil {
311 return false, nil
312 }
313 return strings.Contains(ss, su), nil
314 }
315
316 // Intersect returns the common elements in the given sets, l1 and l2. l1 and
317 // l2 must be of the same type and may be either arrays or slices.
318 func (ns *Namespace) Intersect(l1, l2 any) (any, error) {
319 if l1 == nil || l2 == nil {
320 return make([]any, 0), nil
321 }
322
323 var ins *intersector
324
325 l1v := reflect.ValueOf(l1)
326 l2v := reflect.ValueOf(l2)
327
328 switch l1v.Kind() {
329 case reflect.Array, reflect.Slice:
330 ins = &intersector{r: reflect.MakeSlice(l1v.Type(), 0, 0), seen: make(map[any]bool)}
331 switch l2v.Kind() {
332 case reflect.Array, reflect.Slice:
333 for i := 0; i < l1v.Len(); i++ {
334 l1vv := l1v.Index(i)
335 if !l1vv.Type().Comparable() {
336 return make([]any, 0), errors.New("intersect does not support slices or arrays of uncomparable types")
337 }
338
339 for j := 0; j < l2v.Len(); j++ {
340 l2vv := l2v.Index(j)
341 if !l2vv.Type().Comparable() {
342 return make([]any, 0), errors.New("intersect does not support slices or arrays of uncomparable types")
343 }
344
345 ins.handleValuePair(l1vv, l2vv)
346 }
347 }
348 return ins.r.Interface(), nil
349 default:
350 return nil, errors.New("can't iterate over " + reflect.ValueOf(l2).Type().String())
351 }
352 default:
353 return nil, errors.New("can't iterate over " + reflect.ValueOf(l1).Type().String())
354 }
355 }
356
357 // Group groups a set of elements by the given key.
358 // This is currently only supported for Pages.
359 func (ns *Namespace) Group(key any, items any) (any, error) {
360 if key == nil {
361 return nil, errors.New("nil is not a valid key to group by")
362 }
363
364 if g, ok := items.(collections.Grouper); ok {
365 return g.Group(key, items)
366 }
367
368 in := newSliceElement(items)
369
370 if g, ok := in.(collections.Grouper); ok {
371 return g.Group(key, items)
372 }
373
374 return nil, fmt.Errorf("grouping not supported for type %T %T", items, in)
375 }
376
377 // IsSet returns whether a given array, channel, slice, or map has a key
378 // defined.
379 func (ns *Namespace) IsSet(a any, key any) (bool, error) {
380 av := reflect.ValueOf(a)
381 kv := reflect.ValueOf(key)
382
383 switch av.Kind() {
384 case reflect.Array, reflect.Chan, reflect.Slice:
385 k, err := cast.ToIntE(key)
386 if err != nil {
387 return false, fmt.Errorf("isset unable to use key of type %T as index", key)
388 }
389 if av.Len() > k {
390 return true, nil
391 }
392 case reflect.Map:
393 if kv.Type() == av.Type().Key() {
394 return av.MapIndex(kv).IsValid(), nil
395 }
396 default:
397 helpers.DistinctErrorLog.Printf("WARNING: calling IsSet with unsupported type %q (%T) will always return false.\n", av.Kind(), a)
398 }
399
400 return false, nil
401 }
402
403 // Last returns the last N items in a rangeable list.
404 func (ns *Namespace) Last(limit any, seq any) (any, error) {
405 if limit == nil || seq == nil {
406 return nil, errors.New("both limit and seq must be provided")
407 }
408
409 limitv, err := cast.ToIntE(limit)
410 if err != nil {
411 return nil, err
412 }
413
414 if limitv < 0 {
415 return nil, errors.New("sequence length must be non-negative")
416 }
417
418 seqv := reflect.ValueOf(seq)
419 seqv, isNil := indirect(seqv)
420 if isNil {
421 return nil, errors.New("can't iterate over a nil value")
422 }
423
424 switch seqv.Kind() {
425 case reflect.Array, reflect.Slice, reflect.String:
426 // okay
427 default:
428 return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
429 }
430
431 if limitv > seqv.Len() {
432 limitv = seqv.Len()
433 }
434
435 return seqv.Slice(seqv.Len()-limitv, seqv.Len()).Interface(), nil
436 }
437
438 // Querify encodes the given parameters in URL-encoded form ("bar=baz&foo=quux") sorted by key.
439 func (ns *Namespace) Querify(params ...any) (string, error) {
440 qs := url.Values{}
441
442 if len(params) == 1 {
443 switch v := params[0].(type) {
444 case []string:
445 if len(v)%2 != 0 {
446 return "", errors.New("invalid query")
447 }
448
449 for i := 0; i < len(v); i += 2 {
450 qs.Add(v[i], v[i+1])
451 }
452
453 return qs.Encode(), nil
454
455 case []any:
456 params = v
457
458 default:
459 return "", errors.New("query keys must be strings")
460 }
461 }
462
463 if len(params)%2 != 0 {
464 return "", errors.New("invalid query")
465 }
466
467 for i := 0; i < len(params); i += 2 {
468 switch v := params[i].(type) {
469 case string:
470 qs.Add(v, fmt.Sprintf("%v", params[i+1]))
471 default:
472 return "", errors.New("query keys must be strings")
473 }
474 }
475
476 return qs.Encode(), nil
477 }
478
479 // Reverse creates a copy of slice and reverses it.
480 func (ns *Namespace) Reverse(slice any) (any, error) {
481 if slice == nil {
482 return nil, nil
483 }
484 v := reflect.ValueOf(slice)
485
486 switch v.Kind() {
487 case reflect.Slice:
488 default:
489 return nil, errors.New("argument must be a slice")
490 }
491
492 sliceCopy := reflect.MakeSlice(v.Type(), v.Len(), v.Len())
493
494 for i := v.Len() - 1; i >= 0; i-- {
495 element := sliceCopy.Index(i)
496 element.Set(v.Index(v.Len() - 1 - i))
497 }
498
499 return sliceCopy.Interface(), nil
500 }
501
502 // Seq creates a sequence of integers. It's named and used as GNU's seq.
503 //
504 // Examples:
505 // 3 => 1, 2, 3
506 // 1 2 4 => 1, 3
507 // -3 => -1, -2, -3
508 // 1 4 => 1, 2, 3, 4
509 // 1 -2 => 1, 0, -1, -2
510 func (ns *Namespace) Seq(args ...any) ([]int, error) {
511 if len(args) < 1 || len(args) > 3 {
512 return nil, errors.New("invalid number of arguments to Seq")
513 }
514
515 intArgs := cast.ToIntSlice(args)
516 if len(intArgs) < 1 || len(intArgs) > 3 {
517 return nil, errors.New("invalid arguments to Seq")
518 }
519
520 inc := 1
521 var last int
522 first := intArgs[0]
523
524 if len(intArgs) == 1 {
525 last = first
526 if last == 0 {
527 return []int{}, nil
528 } else if last > 0 {
529 first = 1
530 } else {
531 first = -1
532 inc = -1
533 }
534 } else if len(intArgs) == 2 {
535 last = intArgs[1]
536 if last < first {
537 inc = -1
538 }
539 } else {
540 inc = intArgs[1]
541 last = intArgs[2]
542 if inc == 0 {
543 return nil, errors.New("'increment' must not be 0")
544 }
545 if first < last && inc < 0 {
546 return nil, errors.New("'increment' must be > 0")
547 }
548 if first > last && inc > 0 {
549 return nil, errors.New("'increment' must be < 0")
550 }
551 }
552
553 // sanity check
554 if last < -100000 {
555 return nil, errors.New("size of result exceeds limit")
556 }
557 size := ((last - first) / inc) + 1
558
559 // sanity check
560 if size <= 0 || size > 2000 {
561 return nil, errors.New("size of result exceeds limit")
562 }
563
564 seq := make([]int, size)
565 val := first
566 for i := 0; ; i++ {
567 seq[i] = val
568 val += inc
569 if (inc < 0 && val < last) || (inc > 0 && val > last) {
570 break
571 }
572 }
573
574 return seq, nil
575 }
576
577 // Shuffle returns the given rangeable list in a randomised order.
578 func (ns *Namespace) Shuffle(seq any) (any, error) {
579 if seq == nil {
580 return nil, errors.New("both count and seq must be provided")
581 }
582
583 seqv := reflect.ValueOf(seq)
584 seqv, isNil := indirect(seqv)
585 if isNil {
586 return nil, errors.New("can't iterate over a nil value")
587 }
588
589 switch seqv.Kind() {
590 case reflect.Array, reflect.Slice, reflect.String:
591 // okay
592 default:
593 return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
594 }
595
596 shuffled := reflect.MakeSlice(reflect.TypeOf(seq), seqv.Len(), seqv.Len())
597
598 randomIndices := rand.Perm(seqv.Len())
599
600 for index, value := range randomIndices {
601 shuffled.Index(value).Set(seqv.Index(index))
602 }
603
604 return shuffled.Interface(), nil
605 }
606
607 // Slice returns a slice of all passed arguments.
608 func (ns *Namespace) Slice(args ...any) any {
609 if len(args) == 0 {
610 return args
611 }
612
613 return collections.Slice(args...)
614 }
615
616 type intersector struct {
617 r reflect.Value
618 seen map[any]bool
619 }
620
621 func (i *intersector) appendIfNotSeen(v reflect.Value) {
622 vi := v.Interface()
623 if !i.seen[vi] {
624 i.r = reflect.Append(i.r, v)
625 i.seen[vi] = true
626 }
627 }
628
629 func (i *intersector) handleValuePair(l1vv, l2vv reflect.Value) {
630 switch kind := l1vv.Kind(); {
631 case kind == reflect.String:
632 l2t, err := toString(l2vv)
633 if err == nil && l1vv.String() == l2t {
634 i.appendIfNotSeen(l1vv)
635 }
636 case isNumber(kind):
637 f1, err1 := numberToFloat(l1vv)
638 f2, err2 := numberToFloat(l2vv)
639 if err1 == nil && err2 == nil && f1 == f2 {
640 i.appendIfNotSeen(l1vv)
641 }
642 case kind == reflect.Ptr, kind == reflect.Struct:
643 if l1vv.Interface() == l2vv.Interface() {
644 i.appendIfNotSeen(l1vv)
645 }
646 case kind == reflect.Interface:
647 i.handleValuePair(reflect.ValueOf(l1vv.Interface()), l2vv)
648 }
649 }
650
651 // Union returns the union of the given sets, l1 and l2. l1 and
652 // l2 must be of the same type and may be either arrays or slices.
653 // If l1 and l2 aren't of the same type then l1 will be returned.
654 // If either l1 or l2 is nil then the non-nil list will be returned.
655 func (ns *Namespace) Union(l1, l2 any) (any, error) {
656 if l1 == nil && l2 == nil {
657 return []any{}, nil
658 } else if l1 == nil && l2 != nil {
659 return l2, nil
660 } else if l1 != nil && l2 == nil {
661 return l1, nil
662 }
663
664 l1v := reflect.ValueOf(l1)
665 l2v := reflect.ValueOf(l2)
666
667 var ins *intersector
668
669 switch l1v.Kind() {
670 case reflect.Array, reflect.Slice:
671 switch l2v.Kind() {
672 case reflect.Array, reflect.Slice:
673 ins = &intersector{r: reflect.MakeSlice(l1v.Type(), 0, 0), seen: make(map[any]bool)}
674
675 if l1v.Type() != l2v.Type() &&
676 l1v.Type().Elem().Kind() != reflect.Interface &&
677 l2v.Type().Elem().Kind() != reflect.Interface {
678 return ins.r.Interface(), nil
679 }
680
681 var (
682 l1vv reflect.Value
683 isNil bool
684 )
685
686 for i := 0; i < l1v.Len(); i++ {
687 l1vv, isNil = indirectInterface(l1v.Index(i))
688
689 if !l1vv.Type().Comparable() {
690 return []any{}, errors.New("union does not support slices or arrays of uncomparable types")
691 }
692
693 if !isNil {
694 ins.appendIfNotSeen(l1vv)
695 }
696 }
697
698 if !l1vv.IsValid() {
699 // The first slice may be empty. Pick the first value of the second
700 // to use as a prototype.
701 if l2v.Len() > 0 {
702 l1vv = l2v.Index(0)
703 }
704 }
705
706 for j := 0; j < l2v.Len(); j++ {
707 l2vv := l2v.Index(j)
708
709 switch kind := l1vv.Kind(); {
710 case kind == reflect.String:
711 l2t, err := toString(l2vv)
712 if err == nil {
713 ins.appendIfNotSeen(reflect.ValueOf(l2t))
714 }
715 case isNumber(kind):
716 var err error
717 l2vv, err = convertNumber(l2vv, kind)
718 if err == nil {
719 ins.appendIfNotSeen(l2vv)
720 }
721 case kind == reflect.Interface, kind == reflect.Struct, kind == reflect.Ptr:
722 ins.appendIfNotSeen(l2vv)
723
724 }
725 }
726
727 return ins.r.Interface(), nil
728 default:
729 return nil, errors.New("can't iterate over " + reflect.ValueOf(l2).Type().String())
730 }
731 default:
732 return nil, errors.New("can't iterate over " + reflect.ValueOf(l1).Type().String())
733 }
734 }
735
736 // Uniq takes in a slice or array and returns a slice with subsequent
737 // duplicate elements removed.
738 func (ns *Namespace) Uniq(seq any) (any, error) {
739 if seq == nil {
740 return make([]any, 0), nil
741 }
742
743 v := reflect.ValueOf(seq)
744 var slice reflect.Value
745
746 switch v.Kind() {
747 case reflect.Slice:
748 slice = reflect.MakeSlice(v.Type(), 0, 0)
749
750 case reflect.Array:
751 slice = reflect.MakeSlice(reflect.SliceOf(v.Type().Elem()), 0, 0)
752 default:
753 return nil, fmt.Errorf("type %T not supported", seq)
754 }
755
756 seen := make(map[any]bool)
757
758 for i := 0; i < v.Len(); i++ {
759 ev, _ := indirectInterface(v.Index(i))
760
761 key := normalize(ev)
762
763 if _, found := seen[key]; !found {
764 slice = reflect.Append(slice, ev)
765 seen[key] = true
766 }
767 }
768
769 return slice.Interface(), nil
770 }
771
772 // KeyVals creates a key and values wrapper.
773 func (ns *Namespace) KeyVals(key any, vals ...any) (types.KeyValues, error) {
774 return types.KeyValues{Key: key, Values: vals}, nil
775 }
776
777 // NewScratch creates a new Scratch which can be used to store values in a
778 // thread safe way.
779 func (ns *Namespace) NewScratch() *maps.Scratch {
780 return maps.NewScratch()
781 }