github.com/terraform-linters/tflint@v0.51.2-0.20240520175844-3750771571b6/terraform/lang/funcs/collection.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package funcs
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"math/big"
    10  	"sort"
    11  
    12  	"github.com/zclconf/go-cty/cty"
    13  	"github.com/zclconf/go-cty/cty/convert"
    14  	"github.com/zclconf/go-cty/cty/function"
    15  	"github.com/zclconf/go-cty/cty/function/stdlib"
    16  	"github.com/zclconf/go-cty/cty/gocty"
    17  )
    18  
    19  var LengthFunc = function.New(&function.Spec{
    20  	Params: []function.Parameter{
    21  		{
    22  			Name:             "value",
    23  			Type:             cty.DynamicPseudoType,
    24  			AllowDynamicType: true,
    25  			AllowUnknown:     true,
    26  			AllowMarked:      true,
    27  		},
    28  	},
    29  	Type: func(args []cty.Value) (cty.Type, error) {
    30  		collTy := args[0].Type()
    31  		switch {
    32  		case collTy == cty.String || collTy.IsTupleType() || collTy.IsObjectType() || collTy.IsListType() || collTy.IsMapType() || collTy.IsSetType() || collTy == cty.DynamicPseudoType:
    33  			return cty.Number, nil
    34  		default:
    35  			return cty.Number, errors.New("argument must be a string, a collection type, or a structural type")
    36  		}
    37  	},
    38  	RefineResult: refineNotNull,
    39  	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
    40  		coll := args[0]
    41  		collTy := args[0].Type()
    42  		marks := coll.Marks()
    43  		switch {
    44  		case collTy == cty.DynamicPseudoType:
    45  			return cty.UnknownVal(cty.Number).WithMarks(marks), nil
    46  		case collTy.IsTupleType():
    47  			l := len(collTy.TupleElementTypes())
    48  			return cty.NumberIntVal(int64(l)).WithMarks(marks), nil
    49  		case collTy.IsObjectType():
    50  			l := len(collTy.AttributeTypes())
    51  			return cty.NumberIntVal(int64(l)).WithMarks(marks), nil
    52  		case collTy == cty.String:
    53  			// We'll delegate to the cty stdlib strlen function here, because
    54  			// it deals with all of the complexities of tokenizing unicode
    55  			// grapheme clusters.
    56  			return stdlib.Strlen(coll)
    57  		case collTy.IsListType() || collTy.IsSetType() || collTy.IsMapType():
    58  			return coll.Length(), nil
    59  		default:
    60  			// Should never happen, because of the checks in our Type func above
    61  			return cty.UnknownVal(cty.Number), errors.New("impossible value type for length(...)")
    62  		}
    63  	},
    64  })
    65  
    66  // AllTrueFunc constructs a function that returns true if all elements of the
    67  // list are true. If the list is empty, return true.
    68  var AllTrueFunc = function.New(&function.Spec{
    69  	Params: []function.Parameter{
    70  		{
    71  			Name: "list",
    72  			Type: cty.List(cty.Bool),
    73  		},
    74  	},
    75  	Type:         function.StaticReturnType(cty.Bool),
    76  	RefineResult: refineNotNull,
    77  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
    78  		result := cty.True
    79  		for it := args[0].ElementIterator(); it.Next(); {
    80  			_, v := it.Element()
    81  			if !v.IsKnown() {
    82  				return cty.UnknownVal(cty.Bool), nil
    83  			}
    84  			if v.IsNull() {
    85  				return cty.False, nil
    86  			}
    87  			result = result.And(v)
    88  			if result.False() {
    89  				return cty.False, nil
    90  			}
    91  		}
    92  		return result, nil
    93  	},
    94  })
    95  
    96  // AnyTrueFunc constructs a function that returns true if any element of the
    97  // list is true. If the list is empty, return false.
    98  var AnyTrueFunc = function.New(&function.Spec{
    99  	Params: []function.Parameter{
   100  		{
   101  			Name: "list",
   102  			Type: cty.List(cty.Bool),
   103  		},
   104  	},
   105  	Type:         function.StaticReturnType(cty.Bool),
   106  	RefineResult: refineNotNull,
   107  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   108  		result := cty.False
   109  		var hasUnknown bool
   110  		for it := args[0].ElementIterator(); it.Next(); {
   111  			_, v := it.Element()
   112  			if !v.IsKnown() {
   113  				hasUnknown = true
   114  				continue
   115  			}
   116  			if v.IsNull() {
   117  				continue
   118  			}
   119  			result = result.Or(v)
   120  			if result.True() {
   121  				return cty.True, nil
   122  			}
   123  		}
   124  		if hasUnknown {
   125  			return cty.UnknownVal(cty.Bool), nil
   126  		}
   127  		return result, nil
   128  	},
   129  })
   130  
   131  // CoalesceFunc constructs a function that takes any number of arguments and
   132  // returns the first one that isn't empty. This function was copied from go-cty
   133  // stdlib and modified so that it returns the first *non-empty* non-null element
   134  // from a sequence, instead of merely the first non-null.
   135  var CoalesceFunc = function.New(&function.Spec{
   136  	Params: []function.Parameter{},
   137  	VarParam: &function.Parameter{
   138  		Name:             "vals",
   139  		Type:             cty.DynamicPseudoType,
   140  		AllowUnknown:     true,
   141  		AllowDynamicType: true,
   142  		AllowNull:        true,
   143  	},
   144  	RefineResult: refineNotNull,
   145  	Type: func(args []cty.Value) (ret cty.Type, err error) {
   146  		argTypes := make([]cty.Type, len(args))
   147  		for i, val := range args {
   148  			argTypes[i] = val.Type()
   149  		}
   150  		retType, _ := convert.UnifyUnsafe(argTypes)
   151  		if retType == cty.NilType {
   152  			return cty.NilType, errors.New("all arguments must have the same type")
   153  		}
   154  		return retType, nil
   155  	},
   156  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   157  		for _, argVal := range args {
   158  			// We already know this will succeed because of the checks in our Type func above
   159  			argVal, _ = convert.Convert(argVal, retType)
   160  			if !argVal.IsKnown() {
   161  				return cty.UnknownVal(retType), nil
   162  			}
   163  			if argVal.IsNull() {
   164  				continue
   165  			}
   166  			if retType == cty.String && argVal.RawEquals(cty.StringVal("")) {
   167  				continue
   168  			}
   169  
   170  			return argVal, nil
   171  		}
   172  		return cty.NilVal, errors.New("no non-null, non-empty-string arguments")
   173  	},
   174  })
   175  
   176  // IndexFunc constructs a function that finds the element index for a given value in a list.
   177  var IndexFunc = function.New(&function.Spec{
   178  	Params: []function.Parameter{
   179  		{
   180  			Name: "list",
   181  			Type: cty.DynamicPseudoType,
   182  		},
   183  		{
   184  			Name: "value",
   185  			Type: cty.DynamicPseudoType,
   186  		},
   187  	},
   188  	Type:         function.StaticReturnType(cty.Number),
   189  	RefineResult: refineNotNull,
   190  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   191  		if !(args[0].Type().IsListType() || args[0].Type().IsTupleType()) {
   192  			return cty.NilVal, errors.New("argument must be a list or tuple")
   193  		}
   194  
   195  		if !args[0].IsKnown() {
   196  			return cty.UnknownVal(cty.Number), nil
   197  		}
   198  
   199  		if args[0].LengthInt() == 0 { // Easy path
   200  			return cty.NilVal, errors.New("cannot search an empty list")
   201  		}
   202  
   203  		for it := args[0].ElementIterator(); it.Next(); {
   204  			i, v := it.Element()
   205  			eq, err := stdlib.Equal(v, args[1])
   206  			if err != nil {
   207  				return cty.NilVal, err
   208  			}
   209  			if !eq.IsKnown() {
   210  				return cty.UnknownVal(cty.Number), nil
   211  			}
   212  			if eq.True() {
   213  				return i, nil
   214  			}
   215  		}
   216  		return cty.NilVal, errors.New("item not found")
   217  
   218  	},
   219  })
   220  
   221  // LookupFunc constructs a function that performs dynamic lookups of map types.
   222  var LookupFunc = function.New(&function.Spec{
   223  	Params: []function.Parameter{
   224  		{
   225  			Name:        "inputMap",
   226  			Type:        cty.DynamicPseudoType,
   227  			AllowMarked: true,
   228  		},
   229  		{
   230  			Name:        "key",
   231  			Type:        cty.String,
   232  			AllowMarked: true,
   233  		},
   234  	},
   235  	VarParam: &function.Parameter{
   236  		Name:             "default",
   237  		Type:             cty.DynamicPseudoType,
   238  		AllowUnknown:     true,
   239  		AllowDynamicType: true,
   240  		AllowNull:        true,
   241  		AllowMarked:      true,
   242  	},
   243  	Type: func(args []cty.Value) (ret cty.Type, err error) {
   244  		if len(args) < 1 || len(args) > 3 {
   245  			return cty.NilType, fmt.Errorf("lookup() takes two or three arguments, got %d", len(args))
   246  		}
   247  
   248  		ty := args[0].Type()
   249  
   250  		switch {
   251  		case ty.IsObjectType():
   252  			if !args[1].IsKnown() {
   253  				return cty.DynamicPseudoType, nil
   254  			}
   255  
   256  			keyVal, _ := args[1].Unmark()
   257  			key := keyVal.AsString()
   258  			if ty.HasAttribute(key) {
   259  				return args[0].GetAttr(key).Type(), nil
   260  			} else if len(args) == 3 {
   261  				// if the key isn't found but a default is provided,
   262  				// return the default type
   263  				return args[2].Type(), nil
   264  			}
   265  			return cty.DynamicPseudoType, function.NewArgErrorf(0, "the given object has no attribute %q", key)
   266  		case ty.IsMapType():
   267  			if len(args) == 3 {
   268  				_, err = convert.Convert(args[2], ty.ElementType())
   269  				if err != nil {
   270  					return cty.NilType, function.NewArgErrorf(2, "the default value must have the same type as the map elements")
   271  				}
   272  			}
   273  			return ty.ElementType(), nil
   274  		default:
   275  			return cty.NilType, function.NewArgErrorf(0, "lookup() requires a map as the first argument")
   276  		}
   277  	},
   278  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   279  		var defaultVal cty.Value
   280  		defaultValueSet := false
   281  
   282  		if len(args) == 3 {
   283  			// intentionally leave default value marked
   284  			defaultVal = args[2]
   285  			defaultValueSet = true
   286  		}
   287  
   288  		// keep track of marks from the collection and key
   289  		var markses []cty.ValueMarks
   290  
   291  		// unmark collection, retain marks to reapply later
   292  		mapVar, mapMarks := args[0].Unmark()
   293  		markses = append(markses, mapMarks)
   294  
   295  		// include marks on the key in the result
   296  		keyVal, keyMarks := args[1].Unmark()
   297  		if len(keyMarks) > 0 {
   298  			markses = append(markses, keyMarks)
   299  		}
   300  		lookupKey := keyVal.AsString()
   301  
   302  		if !mapVar.IsKnown() {
   303  			return cty.UnknownVal(retType).WithMarks(markses...), nil
   304  		}
   305  
   306  		if mapVar.Type().IsObjectType() {
   307  			if mapVar.Type().HasAttribute(lookupKey) {
   308  				return mapVar.GetAttr(lookupKey).WithMarks(markses...), nil
   309  			}
   310  		} else if mapVar.HasIndex(cty.StringVal(lookupKey)) == cty.True {
   311  			return mapVar.Index(cty.StringVal(lookupKey)).WithMarks(markses...), nil
   312  		}
   313  
   314  		if defaultValueSet {
   315  			defaultVal, err = convert.Convert(defaultVal, retType)
   316  			if err != nil {
   317  				return cty.NilVal, err
   318  			}
   319  			return defaultVal.WithMarks(markses...), nil
   320  		}
   321  
   322  		return cty.UnknownVal(cty.DynamicPseudoType), fmt.Errorf(
   323  			"lookup failed to find key %s", redactIfSensitive(lookupKey, keyMarks))
   324  	},
   325  })
   326  
   327  // MatchkeysFunc constructs a function that constructs a new list by taking a
   328  // subset of elements from one list whose indexes match the corresponding
   329  // indexes of values in another list.
   330  var MatchkeysFunc = function.New(&function.Spec{
   331  	Params: []function.Parameter{
   332  		{
   333  			Name: "values",
   334  			Type: cty.List(cty.DynamicPseudoType),
   335  		},
   336  		{
   337  			Name: "keys",
   338  			Type: cty.List(cty.DynamicPseudoType),
   339  		},
   340  		{
   341  			Name: "searchset",
   342  			Type: cty.List(cty.DynamicPseudoType),
   343  		},
   344  	},
   345  	Type: func(args []cty.Value) (cty.Type, error) {
   346  		ty, _ := convert.UnifyUnsafe([]cty.Type{args[1].Type(), args[2].Type()})
   347  		if ty == cty.NilType {
   348  			return cty.NilType, errors.New("keys and searchset must be of the same type")
   349  		}
   350  
   351  		// the return type is based on args[0] (values)
   352  		return args[0].Type(), nil
   353  	},
   354  	RefineResult: refineNotNull,
   355  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   356  		if !args[0].IsKnown() {
   357  			return cty.UnknownVal(cty.List(retType.ElementType())), nil
   358  		}
   359  
   360  		if args[0].LengthInt() != args[1].LengthInt() {
   361  			return cty.ListValEmpty(retType.ElementType()), errors.New("length of keys and values should be equal")
   362  		}
   363  
   364  		output := make([]cty.Value, 0)
   365  		values := args[0]
   366  
   367  		// Keys and searchset must be the same type.
   368  		// We can skip error checking here because we've already verified that
   369  		// they can be unified in the Type function
   370  		ty, _ := convert.UnifyUnsafe([]cty.Type{args[1].Type(), args[2].Type()})
   371  		keys, _ := convert.Convert(args[1], ty)
   372  		searchset, _ := convert.Convert(args[2], ty)
   373  
   374  		// if searchset is empty, return an empty list.
   375  		if searchset.LengthInt() == 0 {
   376  			return cty.ListValEmpty(retType.ElementType()), nil
   377  		}
   378  
   379  		if !values.IsWhollyKnown() || !keys.IsWhollyKnown() {
   380  			return cty.UnknownVal(retType), nil
   381  		}
   382  
   383  		i := 0
   384  		for it := keys.ElementIterator(); it.Next(); {
   385  			_, key := it.Element()
   386  			for iter := searchset.ElementIterator(); iter.Next(); {
   387  				_, search := iter.Element()
   388  				eq, err := stdlib.Equal(key, search)
   389  				if err != nil {
   390  					return cty.NilVal, err
   391  				}
   392  				if !eq.IsKnown() {
   393  					return cty.ListValEmpty(retType.ElementType()), nil
   394  				}
   395  				if eq.True() {
   396  					v := values.Index(cty.NumberIntVal(int64(i)))
   397  					output = append(output, v)
   398  					break
   399  				}
   400  			}
   401  			i++
   402  		}
   403  
   404  		// if we haven't matched any key, then output is an empty list.
   405  		if len(output) == 0 {
   406  			return cty.ListValEmpty(retType.ElementType()), nil
   407  		}
   408  		return cty.ListVal(output), nil
   409  	},
   410  })
   411  
   412  // OneFunc returns either the first element of a one-element list, or null
   413  // if given a zero-element list.
   414  var OneFunc = function.New(&function.Spec{
   415  	Params: []function.Parameter{
   416  		{
   417  			Name: "list",
   418  			Type: cty.DynamicPseudoType,
   419  		},
   420  	},
   421  	Type: func(args []cty.Value) (cty.Type, error) {
   422  		ty := args[0].Type()
   423  		switch {
   424  		case ty.IsListType() || ty.IsSetType():
   425  			return ty.ElementType(), nil
   426  		case ty.IsTupleType():
   427  			etys := ty.TupleElementTypes()
   428  			switch len(etys) {
   429  			case 0:
   430  				// No specific type information, so we'll ultimately return
   431  				// a null value of unknown type.
   432  				return cty.DynamicPseudoType, nil
   433  			case 1:
   434  				return etys[0], nil
   435  			}
   436  		}
   437  		return cty.NilType, function.NewArgErrorf(0, "must be a list, set, or tuple value with either zero or one elements")
   438  	},
   439  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   440  		val := args[0]
   441  		ty := val.Type()
   442  
   443  		// Our parameter spec above doesn't set AllowUnknown or AllowNull,
   444  		// so we can assume our top-level collection is both known and non-null
   445  		// in here.
   446  
   447  		switch {
   448  		case ty.IsListType() || ty.IsSetType():
   449  			lenVal := val.Length()
   450  			if !lenVal.IsKnown() {
   451  				return cty.UnknownVal(retType), nil
   452  			}
   453  			var l int
   454  			err := gocty.FromCtyValue(lenVal, &l)
   455  			if err != nil {
   456  				// It would be very strange to get here, because that would
   457  				// suggest that the length is either not a number or isn't
   458  				// an integer, which would suggest a bug in cty.
   459  				return cty.NilVal, fmt.Errorf("invalid collection length: %s", err)
   460  			}
   461  			switch l {
   462  			case 0:
   463  				return cty.NullVal(retType), nil
   464  			case 1:
   465  				var ret cty.Value
   466  				// We'll use an iterator here because that works for both lists
   467  				// and sets, whereas indexing directly would only work for lists.
   468  				// Since we've just checked the length, we should only actually
   469  				// run this loop body once.
   470  				for it := val.ElementIterator(); it.Next(); {
   471  					_, ret = it.Element()
   472  				}
   473  				return ret, nil
   474  			}
   475  		case ty.IsTupleType():
   476  			etys := ty.TupleElementTypes()
   477  			switch len(etys) {
   478  			case 0:
   479  				return cty.NullVal(retType), nil
   480  			case 1:
   481  				ret := val.Index(cty.NumberIntVal(0))
   482  				return ret, nil
   483  			}
   484  		}
   485  		return cty.NilVal, function.NewArgErrorf(0, "must be a list, set, or tuple value with either zero or one elements")
   486  	},
   487  })
   488  
   489  // SumFunc constructs a function that returns the sum of all
   490  // numbers provided in a list
   491  var SumFunc = function.New(&function.Spec{
   492  	Params: []function.Parameter{
   493  		{
   494  			Name: "list",
   495  			Type: cty.DynamicPseudoType,
   496  		},
   497  	},
   498  	Type:         function.StaticReturnType(cty.Number),
   499  	RefineResult: refineNotNull,
   500  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   501  
   502  		if !args[0].CanIterateElements() {
   503  			return cty.NilVal, function.NewArgErrorf(0, "cannot sum noniterable")
   504  		}
   505  
   506  		if args[0].LengthInt() == 0 { // Easy path
   507  			return cty.NilVal, function.NewArgErrorf(0, "cannot sum an empty list")
   508  		}
   509  
   510  		arg := args[0].AsValueSlice()
   511  		ty := args[0].Type()
   512  
   513  		if !ty.IsListType() && !ty.IsSetType() && !ty.IsTupleType() {
   514  			return cty.NilVal, function.NewArgErrorf(0, fmt.Sprintf("argument must be list, set, or tuple. Received %s", ty.FriendlyName()))
   515  		}
   516  
   517  		if !args[0].IsWhollyKnown() {
   518  			return cty.UnknownVal(cty.Number), nil
   519  		}
   520  
   521  		// big.Float.Add can panic if the input values are opposing infinities,
   522  		// so we must catch that here in order to remain within
   523  		// the cty Function abstraction.
   524  		defer func() {
   525  			if r := recover(); r != nil {
   526  				if _, ok := r.(big.ErrNaN); ok {
   527  					ret = cty.NilVal
   528  					err = fmt.Errorf("can't compute sum of opposing infinities")
   529  				} else {
   530  					// not a panic we recognize
   531  					panic(r)
   532  				}
   533  			}
   534  		}()
   535  
   536  		s := arg[0]
   537  		if s.IsNull() {
   538  			return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values")
   539  		}
   540  		s, err = convert.Convert(s, cty.Number)
   541  		if err != nil {
   542  			return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values")
   543  		}
   544  		for _, v := range arg[1:] {
   545  			if v.IsNull() {
   546  				return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values")
   547  			}
   548  			v, err = convert.Convert(v, cty.Number)
   549  			if err != nil {
   550  				return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values")
   551  			}
   552  			s = s.Add(v)
   553  		}
   554  
   555  		return s, nil
   556  	},
   557  })
   558  
   559  // TransposeFunc constructs a function that takes a map of lists of strings and
   560  // swaps the keys and values to produce a new map of lists of strings.
   561  var TransposeFunc = function.New(&function.Spec{
   562  	Params: []function.Parameter{
   563  		{
   564  			Name: "values",
   565  			Type: cty.Map(cty.List(cty.String)),
   566  		},
   567  	},
   568  	Type:         function.StaticReturnType(cty.Map(cty.List(cty.String))),
   569  	RefineResult: refineNotNull,
   570  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   571  		inputMap := args[0]
   572  		if !inputMap.IsWhollyKnown() {
   573  			return cty.UnknownVal(retType), nil
   574  		}
   575  
   576  		outputMap := make(map[string]cty.Value)
   577  		tmpMap := make(map[string][]string)
   578  
   579  		for it := inputMap.ElementIterator(); it.Next(); {
   580  			inKey, inVal := it.Element()
   581  			for iter := inVal.ElementIterator(); iter.Next(); {
   582  				_, val := iter.Element()
   583  				if !val.Type().Equals(cty.String) {
   584  					return cty.MapValEmpty(cty.List(cty.String)), errors.New("input must be a map of lists of strings")
   585  				}
   586  
   587  				outKey := val.AsString()
   588  				if _, ok := tmpMap[outKey]; !ok {
   589  					tmpMap[outKey] = make([]string, 0)
   590  				}
   591  				outVal := tmpMap[outKey]
   592  				outVal = append(outVal, inKey.AsString())
   593  				sort.Strings(outVal)
   594  				tmpMap[outKey] = outVal
   595  			}
   596  		}
   597  
   598  		for outKey, outVal := range tmpMap {
   599  			values := make([]cty.Value, 0)
   600  			for _, v := range outVal {
   601  				values = append(values, cty.StringVal(v))
   602  			}
   603  			outputMap[outKey] = cty.ListVal(values)
   604  		}
   605  
   606  		if len(outputMap) == 0 {
   607  			return cty.MapValEmpty(cty.List(cty.String)), nil
   608  		}
   609  
   610  		return cty.MapVal(outputMap), nil
   611  	},
   612  })
   613  
   614  // ListFunc constructs a function that takes an arbitrary number of arguments
   615  // and returns a list containing those values in the same order.
   616  //
   617  // This function is deprecated in Terraform v0.12
   618  var ListFunc = function.New(&function.Spec{
   619  	Params: []function.Parameter{},
   620  	VarParam: &function.Parameter{
   621  		Name:             "vals",
   622  		Type:             cty.DynamicPseudoType,
   623  		AllowUnknown:     true,
   624  		AllowDynamicType: true,
   625  		AllowNull:        true,
   626  	},
   627  	Type: func(args []cty.Value) (ret cty.Type, err error) {
   628  		return cty.DynamicPseudoType, fmt.Errorf("the \"list\" function was deprecated in Terraform v0.12 and is no longer available; use tolist([ ... ]) syntax to write a literal list")
   629  	},
   630  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   631  		return cty.DynamicVal, fmt.Errorf("the \"list\" function was deprecated in Terraform v0.12 and is no longer available; use tolist([ ... ]) syntax to write a literal list")
   632  	},
   633  })
   634  
   635  // MapFunc constructs a function that takes an even number of arguments and
   636  // returns a map whose elements are constructed from consecutive pairs of arguments.
   637  //
   638  // This function is deprecated in Terraform v0.12
   639  var MapFunc = function.New(&function.Spec{
   640  	Params: []function.Parameter{},
   641  	VarParam: &function.Parameter{
   642  		Name:             "vals",
   643  		Type:             cty.DynamicPseudoType,
   644  		AllowUnknown:     true,
   645  		AllowDynamicType: true,
   646  		AllowNull:        true,
   647  	},
   648  	Type: func(args []cty.Value) (ret cty.Type, err error) {
   649  		return cty.DynamicPseudoType, fmt.Errorf("the \"map\" function was deprecated in Terraform v0.12 and is no longer available; use tomap({ ... }) syntax to write a literal map")
   650  	},
   651  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
   652  		return cty.DynamicVal, fmt.Errorf("the \"map\" function was deprecated in Terraform v0.12 and is no longer available; use tomap({ ... }) syntax to write a literal map")
   653  	},
   654  })
   655  
   656  // Length returns the number of elements in the given collection or number of
   657  // Unicode characters in the given string.
   658  func Length(collection cty.Value) (cty.Value, error) {
   659  	return LengthFunc.Call([]cty.Value{collection})
   660  }
   661  
   662  // AllTrue returns true if all elements of the list are true. If the list is empty,
   663  // return true.
   664  func AllTrue(collection cty.Value) (cty.Value, error) {
   665  	return AllTrueFunc.Call([]cty.Value{collection})
   666  }
   667  
   668  // AnyTrue returns true if any element of the list is true. If the list is empty,
   669  // return false.
   670  func AnyTrue(collection cty.Value) (cty.Value, error) {
   671  	return AnyTrueFunc.Call([]cty.Value{collection})
   672  }
   673  
   674  // Coalesce takes any number of arguments and returns the first one that isn't empty.
   675  func Coalesce(args ...cty.Value) (cty.Value, error) {
   676  	return CoalesceFunc.Call(args)
   677  }
   678  
   679  // Index finds the element index for a given value in a list.
   680  func Index(list, value cty.Value) (cty.Value, error) {
   681  	return IndexFunc.Call([]cty.Value{list, value})
   682  }
   683  
   684  // List takes any number of list arguments and returns a list containing those
   685  //
   686  //	values in the same order.
   687  func List(args ...cty.Value) (cty.Value, error) {
   688  	return ListFunc.Call(args)
   689  }
   690  
   691  // Lookup performs a dynamic lookup into a map.
   692  // There are two required arguments, map and key, plus an optional default,
   693  // which is a value to return if no key is found in map.
   694  func Lookup(args ...cty.Value) (cty.Value, error) {
   695  	return LookupFunc.Call(args)
   696  }
   697  
   698  // Map takes an even number of arguments and returns a map whose elements are constructed
   699  // from consecutive pairs of arguments.
   700  func Map(args ...cty.Value) (cty.Value, error) {
   701  	return MapFunc.Call(args)
   702  }
   703  
   704  // Matchkeys constructs a new list by taking a subset of elements from one list
   705  // whose indexes match the corresponding indexes of values in another list.
   706  func Matchkeys(values, keys, searchset cty.Value) (cty.Value, error) {
   707  	return MatchkeysFunc.Call([]cty.Value{values, keys, searchset})
   708  }
   709  
   710  // One returns either the first element of a one-element list, or null
   711  // if given a zero-element list..
   712  func One(list cty.Value) (cty.Value, error) {
   713  	return OneFunc.Call([]cty.Value{list})
   714  }
   715  
   716  // Sum adds numbers in a list, set, or tuple
   717  func Sum(list cty.Value) (cty.Value, error) {
   718  	return SumFunc.Call([]cty.Value{list})
   719  }
   720  
   721  // Transpose takes a map of lists of strings and swaps the keys and values to
   722  // produce a new map of lists of strings.
   723  func Transpose(values cty.Value) (cty.Value, error) {
   724  	return TransposeFunc.Call([]cty.Value{values})
   725  }