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  }