// Copyright 2019 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package collections provides template functions for manipulating collections
// such as arrays, maps, and slices.
package collections
import (
"fmt"
"html/template"
"math/rand"
"net/url"
"reflect"
"strings"
"time"
"errors"
"github.com/gohugoio/hugo/common/collections"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/tpl/compare"
"github.com/spf13/cast"
)
func init() {
// htime.Now cannot be used here
rand.Seed(time.Now().UTC().UnixNano())
}
// New returns a new instance of the collections-namespaced template functions.
func New(deps *deps.Deps) *Namespace {
language := deps.Conf.Language()
if language == nil {
panic("language must be set")
}
loc := langs.GetLocation(language)
return &Namespace{
loc: loc,
sortComp: compare.New(loc, true),
deps: deps,
}
}
// Namespace provides template functions for the "collections" namespace.
type Namespace struct {
loc *time.Location
sortComp *compare.Namespace
deps *deps.Deps
}
// After returns all the items after the first n items in list l.
func (ns *Namespace) After(n any, l any) (any, error) {
if n == nil || l == nil {
return nil, errors.New("both limit and seq must be provided")
}
nv, err := cast.ToIntE(n)
if err != nil {
return nil, err
}
if nv < 0 {
return nil, errors.New("sequence bounds out of range [" + cast.ToString(nv) + ":]")
}
lv := reflect.ValueOf(l)
lv, isNil := indirect(lv)
if isNil {
return nil, errors.New("can't iterate over a nil value")
}
switch lv.Kind() {
case reflect.Array, reflect.Slice, reflect.String:
// okay
default:
return nil, errors.New("can't iterate over " + reflect.ValueOf(l).Type().String())
}
if nv >= lv.Len() {
return lv.Slice(0, 0).Interface(), nil
}
return lv.Slice(nv, lv.Len()).Interface(), nil
}
// Delimit takes a given list l and returns a string delimited by sep.
// If last is passed to the function, it will be used as the final delimiter.
func (ns *Namespace) Delimit(l, sep any, last ...any) (template.HTML, error) {
d, err := cast.ToStringE(sep)
if err != nil {
return "", err
}
var dLast *string
if len(last) > 0 {
l := last[0]
dStr, err := cast.ToStringE(l)
if err != nil {
dLast = nil
} else {
dLast = &dStr
}
}
lv := reflect.ValueOf(l)
lv, isNil := indirect(lv)
if isNil {
return "", errors.New("can't iterate over a nil value")
}
var str string
switch lv.Kind() {
case reflect.Map:
sortSeq, err := ns.Sort(l)
if err != nil {