github.com/coveo/gotemplate@v2.7.7+incompatible/template/extra_data.go (about)

     1  package template
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strconv"
     7  	"strings"
     8  	"unicode/utf8"
     9  
    10  	"github.com/coveo/gotemplate/collections"
    11  	"github.com/coveo/gotemplate/hcl"
    12  	"github.com/coveo/gotemplate/json"
    13  	"github.com/coveo/gotemplate/utils"
    14  	"github.com/coveo/gotemplate/xml"
    15  	"github.com/coveo/gotemplate/yaml"
    16  )
    17  
    18  const (
    19  	dataBase       = "Data Manipulation"
    20  	dataConversion = "Data Conversion"
    21  )
    22  
    23  var dataFuncsBase = dictionary{
    24  	"String":    toStringClass,
    25  	"append":    addElements,
    26  	"array":     array,
    27  	"bool":      strconv.ParseBool,
    28  	"char":      toChar,
    29  	"contains":  contains,
    30  	"content":   content,
    31  	"dict":      createDict,
    32  	"extract":   extract,
    33  	"get":       get,
    34  	"hasKey":    hasKey,
    35  	"initial":   initial,
    36  	"intersect": intersect,
    37  	"isNil":     func(value interface{}) bool { return value == nil },
    38  	"isSet":     func(value interface{}) bool { return value != nil },
    39  	"isZero":    isZero,
    40  	"key":       key,
    41  	"keys":      keys,
    42  	"lenc":      utf8.RuneCountInString,
    43  	"list":      collections.NewList,
    44  	"merge":     merge,
    45  	"omit":      omit,
    46  	"pick":      pick,
    47  	"pickv":     pickv,
    48  	"pluck":     pluck,
    49  	"prepend":   prepend,
    50  	"rest":      rest,
    51  	"reverse":   reverse,
    52  	"safeIndex": safeIndex,
    53  	"set":       set,
    54  	"slice":     slice,
    55  	"string":    toString,
    56  	"undef":     collections.IfUndef,
    57  	"unique":    unique,
    58  	"union":     union,
    59  	"unset":     unset,
    60  	"values":    values,
    61  	"without":   without,
    62  }
    63  
    64  var dataFuncsConversion = dictionary{
    65  	"toBash":         collections.ToBash,
    66  	"toHcl":          toHCL,
    67  	"toInternalHcl":  toInternalHCL,
    68  	"toJson":         toJSON,
    69  	"toPrettyHcl":    toPrettyHCL,
    70  	"toPrettyJson":   toPrettyJSON,
    71  	"toPrettyTFVars": toPrettyTFVars,
    72  	"toQuotedHcl":    toQuotedHCL,
    73  	"toQuotedJson":   toQuotedJSON,
    74  	"toQuotedTFVars": toQuotedTFVars,
    75  	"toTFVars":       toTFVars,
    76  	"toYaml":         toYAML,
    77  	//"toXml":          toXML,
    78  }
    79  
    80  var dataFuncsArgs = arguments{
    81  	"append":         {"list", "elements"},
    82  	"array":          {"value"},
    83  	"bool":           {"str"},
    84  	"char":           {"value"},
    85  	"contains":       {"list", "elements"},
    86  	"content":        {"keymap"},
    87  	"data":           {"data", "context"},
    88  	"extract":        {"source", "indexes"},
    89  	"get":            {"map", "key", "default"},
    90  	"hasKey":         {"dictionary", "key"},
    91  	"hcl":            {"hcl", "context"},
    92  	"initial":        {"list"},
    93  	"intersect":      {"list", "elements"},
    94  	"json":           {"json", "context"},
    95  	"key":            {"value"},
    96  	"keys":           {"dictionary"},
    97  	"lenc":           {"str"},
    98  	"merge":          {"destination", "sources"},
    99  	"omit":           {"dict", "keys"},
   100  	"pick":           {"dict", "keys"},
   101  	"pickv":          {"dict", "message", "keys"},
   102  	"pluck":          {"key", "dictionaries"},
   103  	"prepend":        {"list", "elements"},
   104  	"rest":           {"list"},
   105  	"reverse":        {"list"},
   106  	"safeIndex":      {"value", "index", "default"},
   107  	"set":            {"dict", "key", "value"},
   108  	"slice":          {"value", "args"},
   109  	"string":         {"value"},
   110  	"String":         {"value"},
   111  	"toBash":         {"value"},
   112  	"toHcl":          {"value"},
   113  	"toInternalHcl":  {"value"},
   114  	"toJson":         {"value"},
   115  	"toPrettyHcl":    {"value"},
   116  	"toPrettyJson":   {"value"},
   117  	"toPrettyTFVars": {"value"},
   118  	"toQuotedHcl":    {"value"},
   119  	"toQuotedJson":   {"value"},
   120  	"toQuotedTFVars": {"value"},
   121  	"toTFVars":       {"value"},
   122  	"toYaml":         {"value"},
   123  	"undef":          {"default", "values"},
   124  	"unique":         {"list"},
   125  	"union":          {"list", "elements"},
   126  	"unset":          {"dictionary", "key"},
   127  	"without":        {"list", "elements"},
   128  	"xml":            {"yaml", "context"},
   129  	"yaml":           {"yaml", "context"},
   130  }
   131  
   132  var dataFuncsAliases = aliases{
   133  	"append":        {"push"},
   134  	"contains":      {"has"},
   135  	"data":          {"DATA", "fromData", "fromDATA"},
   136  	"dict":          {"dictionary"},
   137  	"hcl":           {"HCL", "fromHcl", "fromHCL", "tfvars", "fromTFVars", "TFVARS", "fromTFVARS"},
   138  	"isNil":         {"isNull"},
   139  	"isZero":        {"isEmpty"},
   140  	"json":          {"JSON", "fromJson", "fromJSON"},
   141  	"lenc":          {"nbChars"},
   142  	"list":          {"tuple"},
   143  	"toHcl":         {"toHCL"},
   144  	"toInternalHcl": {"toInternalHCL", "toIHCL", "toIHcl"},
   145  	"toJson":        {"toJSON"},
   146  	"toPrettyHcl":   {"toPrettyHCL"},
   147  	"toPrettyJson":  {"toPrettyJSON"},
   148  	"toPrettyXml":   {"toPrettyXML"},
   149  	"toQuotedHcl":   {"toQuotedHCL"},
   150  	"toQuotedJson":  {"toQuotedJSON"},
   151  	"toXml":         {"toXML"},
   152  	"toYaml":        {"toYAML"},
   153  	"undef":         {"ifUndef"},
   154  	"unique":        {"uniq"},
   155  	"unset":         {"delete", "remove"},
   156  	"xml":           {"XML", "fromXml", "fromXML"},
   157  	"yaml":          {"YAML", "fromYaml", "fromYAML"},
   158  }
   159  
   160  var dataFuncsHelp = descriptions{
   161  	"String":         "Returns a String class object that allows invoking standard string operations as method.",
   162  	"append":         "Append new items to an existing list, creating a new list.",
   163  	"array":          "Ensures that the supplied argument is an array (if it is already an array/slice, there is no change, if not, the argument is replaced by []interface{} with a single value).",
   164  	"bool":           "Converts the `string` into boolean value (`string` must be `True`, `true`, `TRUE`, `1` or `False`, `false`, `FALSE`, `0`)",
   165  	"char":           "Returns the character corresponging to the supplied integer value",
   166  	"contains":       "Test to see if a list has a particular elements.",
   167  	"content":        "Returns the content of a single element map (used to retrieve content in a declaration like `value \"name\" { a = 1 b = 3}`)",
   168  	"data":           "Tries to convert the supplied data string into data structure (Go spec). It will try to convert HCL, YAML and JSON format. If context is omitted, default context is used.",
   169  	"dict":           "Returns a new dictionary from a list of pairs (key, value).",
   170  	"extract":        "Extracts values from a slice or a map, indexes could be either integers for slice or strings for maps",
   171  	"get":            "Returns the value associated with the supplied map, key and map could be inverted for convenience (i.e. when using piping mode)",
   172  	"hasKey":         "Returns true if the dictionary contains the specified key.",
   173  	"hcl":            "Converts the supplied hcl string into data structure (Go spec). If context is omitted, default context is used.",
   174  	"initial":        "Returns but the last element. ",
   175  	"intersect":      "Returns a list that is the intersection of the list and all arguments (removing duplicates).",
   176  	"isNil":          "Returns true if the supplied value is nil.",
   177  	"isSet":          "Returns true if the supplied value is not nil.",
   178  	"isZero":         "Returns true if the supplied value is false, 0, nil or empty.",
   179  	"json":           "Converts the supplied json string into data structure (Go spec). If context is omitted, default context is used.",
   180  	"key":            "Returns the key name of a single element map (used to retrieve name in a declaration like `value \"name\" { a = 1 b = 3}`)",
   181  	"keys":           "Returns a list of all of the keys in a dict (in alphabetical order).",
   182  	"lenc":           "Returns the number of actual character in a string.",
   183  	"list":           "Returns a generic list from the supplied arguments.",
   184  	"merge":          "Merges two or more dictionaries into one, giving precedence to the dest dictionary.",
   185  	"omit":           "Returns a new dict with all the keys that do not match the given keys.",
   186  	"pick":           "Selects just the given keys out of a dictionary, creating a new dict.",
   187  	"pickv":          "Same as pick, but returns an error message if there are intruders in supplied dictionary.",
   188  	"pluck":          "Extracts a list of values matching the supplied key from a list of dictionary.",
   189  	"prepend":        "Push elements onto the front of a list, creating a new list.",
   190  	"rest":           "Gets the tail of the list (everything but the first item)",
   191  	"reverse":        "Produces a new list with the reversed elements of the given list.",
   192  	"safeIndex":      "Returns the element at index position or default if index is outside bounds.",
   193  	"set":            "Adds the value to the supplied map using key as identifier.",
   194  	"slice":          "Returns a slice of the supplied object (equivalent to object[from:to]).",
   195  	"string":         "Converts the supplied value into its string representation.",
   196  	"toBash":         "Converts the supplied value to bash compatible representation.",
   197  	"toHcl":          "Converts the supplied value to compact HCL representation.",
   198  	"toInternalHcl":  "Converts the supplied value to compact HCL representation used inside outer HCL definition.",
   199  	"toJson":         "Converts the supplied value to compact JSON representation.",
   200  	"toPrettyHcl":    "Converts the supplied value to pretty HCL representation.",
   201  	"toPrettyJson":   "Converts the supplied value to pretty JSON representation.",
   202  	"toPrettyTFVars": "Converts the supplied value to pretty HCL representation (without multiple map declarations).",
   203  	"toPrettyXml":    "Converts the supplied value to pretty XML representation.",
   204  	"toQuotedHcl":    "Converts the supplied value to compact quoted HCL representation.",
   205  	"toQuotedJson":   "Converts the supplied value to compact quoted JSON representation.",
   206  	"toQuotedTFVars": "Converts the supplied value to compact HCL representation (without multiple map declarations).",
   207  	"toTFVars":       "Converts the supplied value to compact HCL representation (without multiple map declarations).",
   208  	"toXml":          "Converts the supplied value to XML representation.",
   209  	"toYaml":         "Converts the supplied value to YAML representation.",
   210  	"undef":          "Returns the default value if value is not set, alias `undef` (differs from Sprig `default` function as empty value such as 0, false, \"\" are not considered as unset).",
   211  	"union":          "Returns a list that is the union of the list and all arguments (removing duplicates).",
   212  	"unique":         "Generates a list with all of the duplicates removed.",
   213  	"unset":          "Removes an element from a dictionary.",
   214  	"without":        "Filters items out of a list.",
   215  	"xml":            "Converts the supplied xml string into data structure (Go spec). If context is omitted, default context is used.",
   216  	"yaml":           "Converts the supplied yaml string into data structure (Go spec). If context is omitted, default context is used.",
   217  }
   218  
   219  func (t *Template) addDataFuncs() {
   220  	options := FuncOptions{
   221  		FuncHelp:    dataFuncsHelp,
   222  		FuncArgs:    dataFuncsArgs,
   223  		FuncAliases: dataFuncsAliases,
   224  	}
   225  	t.AddFunctions(dataFuncsBase, dataBase, options)
   226  	t.AddFunctions(dataFuncsConversion, dataConversion, options)
   227  	t.AddFunctions(dictionary{
   228  		"data": t.dataConverter,
   229  		"hcl":  t.hclConverter,
   230  		"json": t.jsonConverter,
   231  		//"xml":  t.xmlConverter,
   232  		"yaml": t.yamlConverter,
   233  	}, dataConversion, options)
   234  	t.optionsEnabled[Data] = true
   235  }
   236  
   237  func toChar(value interface{}) (r interface{}, err error) {
   238  	defer func() { err = trapError(err, recover()) }()
   239  	return process(value, func(a interface{}) interface{} {
   240  		return string(toInt(a))
   241  	})
   242  }
   243  
   244  func toString(s interface{}) string            { return fmt.Sprint(s) }
   245  func toStringClass(s interface{}) utils.String { return utils.String(toString(s)) }
   246  
   247  func toHCL(v interface{}) (string, error) {
   248  	output, err := hcl.Marshal(v)
   249  	return string(output), err
   250  }
   251  
   252  func toInternalHCL(v interface{}) (string, error) {
   253  	output, err := hcl.MarshalInternal(v)
   254  	return string(output), err
   255  }
   256  
   257  func toPrettyHCL(v interface{}) (string, error) {
   258  	output, err := hcl.MarshalIndent(v, "", "  ")
   259  	return string(output), err
   260  }
   261  
   262  func toQuotedHCL(v interface{}) (string, error) {
   263  	output, err := hcl.Marshal(v)
   264  	result := fmt.Sprintf("%q", output)
   265  	return result[1 : len(result)-1], err
   266  }
   267  
   268  func toTFVars(v interface{}) (string, error) {
   269  	output, err := hcl.MarshalTFVars(v)
   270  	return string(output), err
   271  }
   272  
   273  func toPrettyTFVars(v interface{}) (string, error) {
   274  	output, err := hcl.MarshalTFVarsIndent(v, "", "  ")
   275  	return string(output), err
   276  }
   277  
   278  func toQuotedTFVars(v interface{}) (string, error) {
   279  	output, err := hcl.MarshalTFVars(v)
   280  	result := fmt.Sprintf("%q", output)
   281  	return result[1 : len(result)-1], err
   282  }
   283  
   284  func toXML(v interface{}) (string, error) {
   285  	output, err := xml.Marshal(v)
   286  	return string(output), err
   287  }
   288  
   289  func toYAML(v interface{}) (string, error) {
   290  	output, err := yaml.Marshal(v)
   291  	return string(output), err
   292  }
   293  
   294  func toJSON(v interface{}) (string, error) {
   295  	output, err := json.Marshal(v)
   296  	return string(output), err
   297  }
   298  
   299  func toPrettyJSON(v interface{}) (string, error) {
   300  	output, err := json.MarshalIndent(v, "", "  ")
   301  	return string(output), err
   302  }
   303  
   304  func toQuotedJSON(v interface{}) (string, error) {
   305  	output, err := json.Marshal(v)
   306  	result := fmt.Sprintf("%q", output)
   307  	return result[1 : len(result)-1], err
   308  }
   309  
   310  func array(value interface{}) interface{} {
   311  	if value == nil {
   312  		return value
   313  	}
   314  	switch reflect.TypeOf(value).Kind() {
   315  	case reflect.Slice, reflect.Array:
   316  		return value
   317  	default:
   318  		return []interface{}{value}
   319  	}
   320  }
   321  
   322  func get(arg1, arg2 interface{}, defValue ...interface{}) (interface{}, error) {
   323  	// In pipe execution, the map is often the last parameter, but we also support to
   324  	// put the map as the first parameter.
   325  	var result interface{}
   326  	if dict, err := collections.TryAsDictionary(arg1); err == nil {
   327  		result = dict.Get(arg2)
   328  	} else if dict, err = collections.TryAsDictionary(arg2); err == nil {
   329  		result = dict.Get(arg1)
   330  	} else {
   331  		return nil, fmt.Errorf("Must supply dictionary object")
   332  	}
   333  	if result == nil {
   334  		switch len(defValue) {
   335  		case 0:
   336  			break
   337  		case 1:
   338  			result = defValue[0]
   339  		default:
   340  			result = defValue
   341  		}
   342  	}
   343  	return result, nil
   344  }
   345  
   346  func hasKey(arg1, arg2 interface{}) (interface{}, error) {
   347  	// In pipe execution, the map is often the last parameter, but we also support to
   348  	// put the map as the first parameter.
   349  	if dict, err := collections.TryAsDictionary(arg1); err == nil {
   350  		return dict.Has(arg2), nil
   351  	} else if dict, err = collections.TryAsDictionary(arg2); err == nil {
   352  		return dict.Has(arg1), nil
   353  	} else {
   354  		return nil, fmt.Errorf("Must supply dictionary object")
   355  	}
   356  }
   357  
   358  func set(arg1, arg2, arg3 interface{}) (string, error) {
   359  	// In pipe execution, the map is often the last parameter, but we also support to
   360  	// put the map as the first parameter.
   361  	if dict, err := collections.TryAsDictionary(arg1); err == nil {
   362  		dict.Set(arg2, arg3)
   363  	} else if dict, err = collections.TryAsDictionary(arg3); err == nil {
   364  		dict.Set(arg1, arg2)
   365  	} else {
   366  		return "", fmt.Errorf("Must supply dictionary object")
   367  	}
   368  	return "", nil
   369  }
   370  
   371  func unset(arg1, arg2 interface{}) (string, error) {
   372  	// In pipe execution, the map is often the last parameter, but we also support to
   373  	// put the map as the first parameter.
   374  	if dict, err := collections.TryAsDictionary(arg1); err == nil {
   375  		dict.Delete(arg2)
   376  	} else if dict, err = collections.TryAsDictionary(arg2); err == nil {
   377  		dict.Delete(arg1)
   378  	} else {
   379  		return "", fmt.Errorf("Must supply dictionary object")
   380  	}
   381  	return "", nil
   382  }
   383  
   384  func merge(target iDictionary, dict iDictionary, otherDicts ...iDictionary) iDictionary {
   385  	return target.Merge(dict, otherDicts...)
   386  }
   387  
   388  func key(v interface{}) (interface{}, error) {
   389  	key, _, err := getSingleMapElement(v)
   390  	return key, err
   391  }
   392  
   393  func content(v interface{}) (interface{}, error) {
   394  	_, value, err := getSingleMapElement(v)
   395  	return value, err
   396  }
   397  
   398  type marshaler func(interface{}) ([]byte, error)
   399  type unMarshaler func([]byte, interface{}) error
   400  
   401  // Internal function used to actually convert the supplied string and apply a conversion function over it to get a go map
   402  func (t Template) converter(from unMarshaler, content string, sourceWithError bool, context ...interface{}) (result interface{}, err error) {
   403  	if err = from([]byte(content), &result); err != nil && sourceWithError {
   404  		source := "\n"
   405  		for i, line := range collections.SplitLines(content) {
   406  			source += fmt.Sprintf("%4d %s\n", i+1, line)
   407  		}
   408  		err = fmt.Errorf("%s\n%v", source, err)
   409  	}
   410  	return
   411  }
   412  
   413  // Apply a converter to the result of the template execution of the supplied string
   414  func (t Template) templateConverter(to marshaler, from unMarshaler, source interface{}, context ...interface{}) (result interface{}, err error) {
   415  	if source == nil {
   416  		return nil, nil
   417  	}
   418  	if reflect.TypeOf(source).Kind() != reflect.String {
   419  		if source, err = to(source); err != nil {
   420  			return
   421  		}
   422  		source = string(source.([]byte))
   423  	}
   424  
   425  	var content string
   426  	if content, _, err = t.runTemplate(fmt.Sprint(source), context...); err == nil {
   427  		result, err = t.converter(from, content, true, context...)
   428  	}
   429  	return
   430  }
   431  
   432  func (t Template) xmlConverter(source interface{}, context ...interface{}) (interface{}, error) {
   433  	return t.templateConverter(xml.Marshal, xml.Unmarshal, source, context...)
   434  }
   435  
   436  func (t Template) yamlConverter(source interface{}, context ...interface{}) (interface{}, error) {
   437  	return t.templateConverter(yaml.Marshal, yaml.Unmarshal, source, context...)
   438  }
   439  
   440  func (t Template) jsonConverter(source interface{}, context ...interface{}) (interface{}, error) {
   441  	return t.templateConverter(json.Marshal, json.Unmarshal, source, context...)
   442  }
   443  
   444  func (t Template) hclConverter(source interface{}, context ...interface{}) (result interface{}, err error) {
   445  	return t.templateConverter(hcl.Marshal, hcl.Unmarshal, source, context...)
   446  }
   447  
   448  func (t Template) dataConverter(source interface{}, context ...interface{}) (result interface{}, err error) {
   449  	return t.templateConverter(
   450  		func(in interface{}) ([]byte, error) { return []byte(fmt.Sprint(in)), nil },
   451  		func(bs []byte, out interface{}) error { return collections.ConvertData(string(bs), out) },
   452  		source, context...)
   453  }
   454  
   455  func pick(dict iDictionary, keys ...interface{}) iDictionary {
   456  	return dict.Clone(keys...)
   457  }
   458  
   459  func omit(dict iDictionary, key interface{}, otherKeys ...interface{}) iDictionary {
   460  	return dict.Omit(key, otherKeys...)
   461  }
   462  
   463  func pickv(dict iDictionary, message string, key interface{}, otherKeys ...interface{}) (interface{}, error) {
   464  	o := dict.Omit(key, otherKeys...)
   465  
   466  	if o.Len() > 0 {
   467  		over := strings.Join(toStrings(o.GetKeys()), ", ")
   468  		if strings.Contains(message, "%v") {
   469  			message = fmt.Sprintf(message, over)
   470  		} else {
   471  			message = iif(message == "", "Unwanted values", message).(string)
   472  			message = fmt.Sprintf("%s %s", message, over)
   473  		}
   474  		return nil, fmt.Errorf(message)
   475  	}
   476  	return pick(dict, append(otherKeys, key)), nil
   477  }
   478  
   479  func keys(dict iDictionary) iList   { return dict.GetKeys() }
   480  func values(dict iDictionary) iList { return dict.GetValues() }
   481  
   482  func createDict(v ...interface{}) (iDictionary, error) {
   483  	if len(v)%2 != 0 {
   484  		return nil, fmt.Errorf("Must supply even number of arguments (keypair)")
   485  	}
   486  
   487  	result := collections.CreateDictionary(len(v) / 2)
   488  	for i := 0; i < len(v); i += 2 {
   489  		result.Set(v[i], v[i+1])
   490  	}
   491  	return result, nil
   492  }
   493  
   494  func pluck(key interface{}, dicts ...iDictionary) iList {
   495  	result := collections.CreateList(0, len(dicts))
   496  	for i := range dicts {
   497  		if dicts[i].Has(key) {
   498  			result.Append(dicts[i].Get(key))
   499  		}
   500  	}
   501  	return result
   502  }
   503  
   504  func rest(list interface{}) (interface{}, error)    { return slice(list, 1, -1) }
   505  func initial(list interface{}) (interface{}, error) { return slice(list, 0, -2) }
   506  
   507  func addElements(list interface{}, elements ...interface{}) (r iList, err error) {
   508  	defer func() { err = trapError(err, recover()) }()
   509  	return collections.AsList(list).Append(elements...), nil
   510  }
   511  
   512  func prepend(list interface{}, elements ...interface{}) (r iList, err error) {
   513  	defer func() { err = trapError(err, recover()) }()
   514  	return collections.AsList(list).Prepend(elements...), nil
   515  }
   516  
   517  func reverse(list interface{}) (r iList, err error) {
   518  	defer func() { err = trapError(err, recover()) }()
   519  	return collections.AsList(list).Reverse(), nil
   520  }
   521  
   522  func unique(list interface{}) (r iList, err error) {
   523  	defer func() { err = trapError(err, recover()) }()
   524  	return collections.AsList(list).Unique(), nil
   525  }
   526  
   527  func contains(list interface{}, elements ...interface{}) (r bool, err error) {
   528  	// Then, the list argument must be a real list of elements
   529  	defer func() { err = trapError(err, recover()) }()
   530  	if _, err := collections.TryAsList(list); err != nil && len(elements) == 1 {
   531  		if _, err2 := collections.TryAsList(elements[0]); err2 != nil {
   532  			str, subStr := elements[0], list
   533  			if s, isString := str.(collections.String); isString {
   534  				// Check if the str argument is of type String
   535  				str = string(s)
   536  			}
   537  
   538  			if s, isString := str.(string); isString {
   539  				// Check if the list argument is of type string
   540  				return strings.Contains(s, fmt.Sprint(subStr)), nil
   541  			}
   542  			return false, err
   543  		}
   544  		// Sprig has bad documentation and inverse the arguments, so we try to support both modes.
   545  		list, elements = elements[0], []interface{}{list}
   546  	}
   547  	return collections.AsList(list).Contains(elements...), nil
   548  }
   549  
   550  func intersect(list interface{}, elements ...interface{}) (r iList, err error) {
   551  	defer func() { err = trapError(err, recover()) }()
   552  	return collections.AsList(list).Intersect(elements...), nil
   553  }
   554  
   555  func union(list interface{}, elements ...interface{}) (r iList, err error) {
   556  	defer func() { err = trapError(err, recover()) }()
   557  	return collections.AsList(list).Union(elements...), nil
   558  }
   559  
   560  func without(list interface{}, elements ...interface{}) (r iList, err error) {
   561  	defer func() { err = trapError(err, recover()) }()
   562  	return collections.AsList(list).Without(elements...), nil
   563  }
   564  
   565  func isZero(value interface{}) bool {
   566  	return sprigDef(0, value) == 0
   567  }