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