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