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  }