github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/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 "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 items in list l. 67 func (ns *Namespace) After(n any, l any) (any, error) { 68 if n == nil || l == nil { 69 return nil, errors.New("both limit and seq must be provided") 70 } 71 72 nv, err := cast.ToIntE(n) 73 if err != nil { 74 return nil, err 75 } 76 77 if nv < 0 { 78 return nil, errors.New("sequence bounds out of range [" + cast.ToString(nv) + ":]") 79 } 80 81 lv := reflect.ValueOf(l) 82 lv, isNil := indirect(lv) 83 if isNil { 84 return nil, errors.New("can't iterate over a nil value") 85 } 86 87 switch lv.Kind() { 88 case reflect.Array, reflect.Slice, reflect.String: 89 // okay 90 default: 91 return nil, errors.New("can't iterate over " + reflect.ValueOf(l).Type().String()) 92 } 93 94 if nv >= lv.Len() { 95 return lv.Slice(0, 0).Interface(), nil 96 } 97 98 return lv.Slice(nv, lv.Len()).Interface(), nil 99 } 100 101 // Delimit takes a given list l and returns a string delimited by sep. 102 // If last is passed to the function, it will be used as the final delimiter. 103 func (ns *Namespace) Delimit(l, sep any, last ...any) (template.HTML, error) { 104 d, err := cast.ToStringE(sep) 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 lv := reflect.ValueOf(l) 121 lv, isNil := indirect(lv) 122 if isNil { 123 return "", errors.New("can't iterate over a nil value") 124 } 125 126 var str string 127 switch lv.Kind() { 128 case reflect.Map: 129 sortSeq, err := ns.Sort(l) 130 if err != nil { 131 return "", err 132 } 133 lv = reflect.ValueOf(sortSeq) 134 fallthrough 135 case reflect.Array, reflect.Slice, reflect.String: 136 for i := 0; i < lv.Len(); i++ { 137 val := lv.Index(i).Interface() 138 valStr, err := cast.ToStringE(val) 139 if err != nil { 140 continue 141 } 142 switch { 143 case i == lv.Len()-2 && dLast != nil: 144 str += valStr + *dLast 145 case i == lv.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", l) 154 } 155 156 return template.HTML(str), nil 157 } 158 159 // Dictionary creates a new map from the given parameters by 160 // treating values as key-value pairs. The number of values must be even. 161 // The keys can be string slices, which will create the needed nested structure. 162 func (ns *Namespace) Dictionary(values ...any) (map[string]any, error) { 163 if len(values)%2 != 0 { 164 return nil, errors.New("invalid dictionary call") 165 } 166 167 root := make(map[string]any) 168 169 for i := 0; i < len(values); i += 2 { 170 dict := root 171 var key string 172 switch v := values[i].(type) { 173 case string: 174 key = v 175 case []string: 176 for i := 0; i < len(v)-1; i++ { 177 key = v[i] 178 var m map[string]any 179 v, found := dict[key] 180 if found { 181 m = v.(map[string]any) 182 } else { 183 m = make(map[string]any) 184 dict[key] = m 185 } 186 dict = m 187 } 188 key = v[len(v)-1] 189 default: 190 return nil, errors.New("invalid dictionary key") 191 } 192 dict[key] = values[i+1] 193 } 194 195 return root, nil 196 } 197 198 // EchoParam returns a the value in the collection c with key k if is set; otherwise, it returns an 199 // empty string. 200 func (ns *Namespace) EchoParam(c, k any) any { 201 av, isNil := indirect(reflect.ValueOf(c)) 202 if isNil { 203 return "" 204 } 205 206 var avv reflect.Value 207 switch av.Kind() { 208 case reflect.Array, reflect.Slice: 209 index, ok := k.(int) 210 if ok && av.Len() > index { 211 avv = av.Index(index) 212 } 213 case reflect.Map: 214 kv := reflect.ValueOf(k) 215 if kv.Type().AssignableTo(av.Type().Key()) { 216 avv = av.MapIndex(kv) 217 } 218 } 219 220 avv, isNil = indirect(avv) 221 222 if isNil { 223 return "" 224 } 225 226 if avv.IsValid() { 227 switch avv.Kind() { 228 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 229 return avv.Int() 230 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 231 return avv.Uint() 232 case reflect.Float32, reflect.Float64: 233 return avv.Float() 234 case reflect.String: 235 return avv.String() 236 } 237 } 238 239 return "" 240 } 241 242 // First returns the first limit items in list l. 243 func (ns *Namespace) First(limit any, l any) (any, error) { 244 if limit == nil || l == nil { 245 return nil, errors.New("both limit and seq must be provided") 246 } 247 248 limitv, err := cast.ToIntE(limit) 249 if err != nil { 250 return nil, err 251 } 252 253 if limitv < 0 { 254 return nil, errors.New("sequence length must be non-negative") 255 } 256 257 lv := reflect.ValueOf(l) 258 lv, isNil := indirect(lv) 259 if isNil { 260 return nil, errors.New("can't iterate over a nil value") 261 } 262 263 switch lv.Kind() { 264 case reflect.Array, reflect.Slice, reflect.String: 265 // okay 266 default: 267 return nil, errors.New("can't iterate over " + reflect.ValueOf(l).Type().String()) 268 } 269 270 if limitv > lv.Len() { 271 limitv = lv.Len() 272 } 273 274 return lv.Slice(0, limitv).Interface(), nil 275 } 276 277 // In returns whether v is in the list l. l may be an array or slice. 278 func (ns *Namespace) In(l any, v any) (bool, error) { 279 if l == nil || v == nil { 280 return false, nil 281 } 282 283 lv := reflect.ValueOf(l) 284 vv := reflect.ValueOf(v) 285 286 vvk := normalize(vv) 287 288 switch lv.Kind() { 289 case reflect.Array, reflect.Slice: 290 for i := 0; i < lv.Len(); i++ { 291 lvv, isNil := indirectInterface(lv.Index(i)) 292 if isNil { 293 continue 294 } 295 296 lvvk := normalize(lvv) 297 298 if lvvk == vvk { 299 return true, nil 300 } 301 } 302 } 303 ss, err := cast.ToStringE(l) 304 if err != nil { 305 return false, nil 306 } 307 308 su, err := cast.ToStringE(v) 309 if err != nil { 310 return false, nil 311 } 312 return strings.Contains(ss, su), nil 313 } 314 315 // Intersect returns the common elements in the given sets, l1 and l2. l1 and 316 // l2 must be of the same type and may be either arrays or slices. 317 func (ns *Namespace) Intersect(l1, l2 any) (any, error) { 318 if l1 == nil || l2 == nil { 319 return make([]any, 0), nil 320 } 321 322 var ins *intersector 323 324 l1v := reflect.ValueOf(l1) 325 l2v := reflect.ValueOf(l2) 326 327 switch l1v.Kind() { 328 case reflect.Array, reflect.Slice: 329 ins = &intersector{r: reflect.MakeSlice(l1v.Type(), 0, 0), seen: make(map[any]bool)} 330 switch l2v.Kind() { 331 case reflect.Array, reflect.Slice: 332 for i := 0; i < l1v.Len(); i++ { 333 l1vv := l1v.Index(i) 334 if !l1vv.Type().Comparable() { 335 return make([]any, 0), errors.New("intersect does not support slices or arrays of uncomparable types") 336 } 337 338 for j := 0; j < l2v.Len(); j++ { 339 l2vv := l2v.Index(j) 340 if !l2vv.Type().Comparable() { 341 return make([]any, 0), errors.New("intersect does not support slices or arrays of uncomparable types") 342 } 343 344 ins.handleValuePair(l1vv, l2vv) 345 } 346 } 347 return ins.r.Interface(), nil 348 default: 349 return nil, errors.New("can't iterate over " + reflect.ValueOf(l2).Type().String()) 350 } 351 default: 352 return nil, errors.New("can't iterate over " + reflect.ValueOf(l1).Type().String()) 353 } 354 } 355 356 // Group groups a set of items by the given key. 357 // This is currently only supported for Pages. 358 func (ns *Namespace) Group(key any, items any) (any, error) { 359 if key == nil { 360 return nil, errors.New("nil is not a valid key to group by") 361 } 362 363 if g, ok := items.(collections.Grouper); ok { 364 return g.Group(key, items) 365 } 366 367 in := newSliceElement(items) 368 369 if g, ok := in.(collections.Grouper); ok { 370 return g.Group(key, items) 371 } 372 373 return nil, fmt.Errorf("grouping not supported for type %T %T", items, in) 374 } 375 376 // IsSet returns whether a given array, channel, slice, or map in c has the given key 377 // defined. 378 func (ns *Namespace) IsSet(c any, key any) (bool, error) { 379 av := reflect.ValueOf(c) 380 kv := reflect.ValueOf(key) 381 382 switch av.Kind() { 383 case reflect.Array, reflect.Chan, reflect.Slice: 384 k, err := cast.ToIntE(key) 385 if err != nil { 386 return false, fmt.Errorf("isset unable to use key of type %T as index", key) 387 } 388 if av.Len() > k { 389 return true, nil 390 } 391 case reflect.Map: 392 if kv.Type() == av.Type().Key() { 393 return av.MapIndex(kv).IsValid(), nil 394 } 395 default: 396 helpers.DistinctErrorLog.Printf("WARNING: calling IsSet with unsupported type %q (%T) will always return false.\n", av.Kind(), c) 397 } 398 399 return false, nil 400 } 401 402 // Last returns the last limit items in the list l. 403 func (ns *Namespace) Last(limit any, l any) (any, error) { 404 if limit == nil || l == nil { 405 return nil, errors.New("both limit and seq must be provided") 406 } 407 408 limitv, err := cast.ToIntE(limit) 409 if err != nil { 410 return nil, err 411 } 412 413 if limitv < 0 { 414 return nil, errors.New("sequence length must be non-negative") 415 } 416 417 seqv := reflect.ValueOf(l) 418 seqv, isNil := indirect(seqv) 419 if isNil { 420 return nil, errors.New("can't iterate over a nil value") 421 } 422 423 switch seqv.Kind() { 424 case reflect.Array, reflect.Slice, reflect.String: 425 // okay 426 default: 427 return nil, errors.New("can't iterate over " + reflect.ValueOf(l).Type().String()) 428 } 429 430 if limitv > seqv.Len() { 431 limitv = seqv.Len() 432 } 433 434 return seqv.Slice(seqv.Len()-limitv, seqv.Len()).Interface(), nil 435 } 436 437 // Querify encodes the given params in URL-encoded form ("bar=baz&foo=quux") sorted by key. 438 func (ns *Namespace) Querify(params ...any) (string, error) { 439 qs := url.Values{} 440 441 if len(params) == 1 { 442 switch v := params[0].(type) { 443 case []string: 444 if len(v)%2 != 0 { 445 return "", errors.New("invalid query") 446 } 447 448 for i := 0; i < len(v); i += 2 { 449 qs.Add(v[i], v[i+1]) 450 } 451 452 return qs.Encode(), nil 453 454 case []any: 455 params = v 456 457 default: 458 return "", errors.New("query keys must be strings") 459 } 460 } 461 462 if len(params)%2 != 0 { 463 return "", errors.New("invalid query") 464 } 465 466 for i := 0; i < len(params); i += 2 { 467 switch v := params[i].(type) { 468 case string: 469 qs.Add(v, fmt.Sprintf("%v", params[i+1])) 470 default: 471 return "", errors.New("query keys must be strings") 472 } 473 } 474 475 return qs.Encode(), nil 476 } 477 478 // Reverse creates a copy of the list l and reverses it. 479 func (ns *Namespace) Reverse(l any) (any, error) { 480 if l == nil { 481 return nil, nil 482 } 483 v := reflect.ValueOf(l) 484 485 switch v.Kind() { 486 case reflect.Slice: 487 default: 488 return nil, errors.New("argument must be a slice") 489 } 490 491 sliceCopy := reflect.MakeSlice(v.Type(), v.Len(), v.Len()) 492 493 for i := v.Len() - 1; i >= 0; i-- { 494 element := sliceCopy.Index(i) 495 element.Set(v.Index(v.Len() - 1 - i)) 496 } 497 498 return sliceCopy.Interface(), nil 499 } 500 501 // Seq creates a sequence of integers from args. It's named and used as GNU's seq. 502 // 503 // Examples: 504 // 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 list l in a randomised order. 578 func (ns *Namespace) Shuffle(l any) (any, error) { 579 if l == nil { 580 return nil, errors.New("both count and seq must be provided") 581 } 582 583 lv := reflect.ValueOf(l) 584 lv, isNil := indirect(lv) 585 if isNil { 586 return nil, errors.New("can't iterate over a nil value") 587 } 588 589 switch lv.Kind() { 590 case reflect.Array, reflect.Slice, reflect.String: 591 // okay 592 default: 593 return nil, errors.New("can't iterate over " + reflect.ValueOf(l).Type().String()) 594 } 595 596 shuffled := reflect.MakeSlice(reflect.TypeOf(l), lv.Len(), lv.Len()) 597 598 randomIndices := rand.Perm(lv.Len()) 599 600 for index, value := range randomIndices { 601 shuffled.Index(value).Set(lv.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 returns a new list with duplicate elements in the list l removed. 737 func (ns *Namespace) Uniq(l any) (any, error) { 738 if l == nil { 739 return make([]any, 0), nil 740 } 741 742 v := reflect.ValueOf(l) 743 var slice reflect.Value 744 745 switch v.Kind() { 746 case reflect.Slice: 747 slice = reflect.MakeSlice(v.Type(), 0, 0) 748 749 case reflect.Array: 750 slice = reflect.MakeSlice(reflect.SliceOf(v.Type().Elem()), 0, 0) 751 default: 752 return nil, fmt.Errorf("type %T not supported", l) 753 } 754 755 seen := make(map[any]bool) 756 757 for i := 0; i < v.Len(); i++ { 758 ev, _ := indirectInterface(v.Index(i)) 759 760 key := normalize(ev) 761 762 if _, found := seen[key]; !found { 763 slice = reflect.Append(slice, ev) 764 seen[key] = true 765 } 766 } 767 768 return slice.Interface(), nil 769 } 770 771 // KeyVals creates a key and values wrapper. 772 func (ns *Namespace) KeyVals(key any, values ...any) (types.KeyValues, error) { 773 return types.KeyValues{Key: key, Values: values}, nil 774 } 775 776 // NewScratch creates a new Scratch which can be used to store values in a 777 // thread safe way. 778 func (ns *Namespace) NewScratch() *maps.Scratch { 779 return maps.NewScratch() 780 }