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