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 }