github.com/opentofu/opentofu@v1.7.1/internal/lang/funcs/collection.go (about)

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