2018-05-22 02:39:26 +02:00
package funcs
import (
2019-04-30 19:12:00 +02:00
"errors"
2018-05-22 02:39:26 +02:00
"fmt"
2018-05-31 23:46:24 +02:00
"sort"
2018-05-22 02:39:26 +02:00
"github.com/zclconf/go-cty/cty"
2018-05-24 20:48:44 +02:00
"github.com/zclconf/go-cty/cty/convert"
2018-05-22 02:39:26 +02:00
"github.com/zclconf/go-cty/cty/function"
"github.com/zclconf/go-cty/cty/function/stdlib"
"github.com/zclconf/go-cty/cty/gocty"
)
var LengthFunc = function . New ( & function . Spec {
Params : [ ] function . Parameter {
{
Name : "value" ,
Type : cty . DynamicPseudoType ,
AllowDynamicType : true ,
AllowUnknown : true ,
} ,
} ,
Type : func ( args [ ] cty . Value ) ( cty . Type , error ) {
collTy := args [ 0 ] . Type ( )
switch {
2018-11-06 02:13:05 +01:00
case collTy == cty . String || collTy . IsTupleType ( ) || collTy . IsObjectType ( ) || collTy . IsListType ( ) || collTy . IsMapType ( ) || collTy . IsSetType ( ) || collTy == cty . DynamicPseudoType :
2018-05-22 02:39:26 +02:00
return cty . Number , nil
default :
2019-05-01 23:55:44 +02:00
return cty . Number , errors . New ( "argument must be a string, a collection type, or a structural type" )
2018-05-22 02:39:26 +02:00
}
} ,
Impl : func ( args [ ] cty . Value , retType cty . Type ) ( cty . Value , error ) {
coll := args [ 0 ]
collTy := args [ 0 ] . Type ( )
switch {
case collTy == cty . DynamicPseudoType :
return cty . UnknownVal ( cty . Number ) , nil
case collTy . IsTupleType ( ) :
l := len ( collTy . TupleElementTypes ( ) )
return cty . NumberIntVal ( int64 ( l ) ) , nil
case collTy . IsObjectType ( ) :
l := len ( collTy . AttributeTypes ( ) )
return cty . NumberIntVal ( int64 ( l ) ) , nil
case collTy == cty . String :
// We'll delegate to the cty stdlib strlen function here, because
// it deals with all of the complexities of tokenizing unicode
// grapheme clusters.
return stdlib . Strlen ( coll )
case collTy . IsListType ( ) || collTy . IsSetType ( ) || collTy . IsMapType ( ) :
return coll . Length ( ) , nil
default :
// Should never happen, because of the checks in our Type func above
2019-05-01 23:55:44 +02:00
return cty . UnknownVal ( cty . Number ) , errors . New ( "impossible value type for length(...)" )
2018-05-22 02:39:26 +02:00
}
} ,
} )
2020-09-22 15:06:42 +02:00
// AllTrueFunc constructs a function that returns true if all elements of the
2020-10-23 22:52:48 +02:00
// list are true. If the list is empty, return true.
2020-09-22 15:06:42 +02:00
var AllTrueFunc = function . New ( & function . Spec {
Params : [ ] function . Parameter {
{
2020-10-23 22:52:48 +02:00
Name : "list" ,
Type : cty . List ( cty . Bool ) ,
2020-09-22 15:06:42 +02:00
} ,
} ,
Type : function . StaticReturnType ( cty . Bool ) ,
Impl : func ( args [ ] cty . Value , retType cty . Type ) ( ret cty . Value , err error ) {
2020-10-23 22:52:48 +02:00
result := cty . True
2020-09-22 15:06:42 +02:00
for it := args [ 0 ] . ElementIterator ( ) ; it . Next ( ) ; {
_ , v := it . Element ( )
2020-10-23 22:52:48 +02:00
if v . IsNull ( ) {
return cty . False , nil
2020-09-22 15:06:42 +02:00
}
2020-10-23 22:52:48 +02:00
result = result . And ( v )
if result . False ( ) {
2020-09-22 15:06:42 +02:00
return cty . False , nil
}
2020-10-23 22:52:48 +02:00
}
return result , nil
} ,
} )
// AnyTrueFunc constructs a function that returns true if any element of the
// list is true. If the list is empty, return false.
var AnyTrueFunc = function . New ( & function . Spec {
Params : [ ] function . Parameter {
{
Name : "list" ,
Type : cty . List ( cty . Bool ) ,
} ,
} ,
Type : function . StaticReturnType ( cty . Bool ) ,
Impl : func ( args [ ] cty . Value , retType cty . Type ) ( ret cty . Value , err error ) {
result := cty . False
for it := args [ 0 ] . ElementIterator ( ) ; it . Next ( ) ; {
_ , v := it . Element ( )
if v . IsNull ( ) {
continue
2020-09-22 15:06:42 +02:00
}
2020-10-23 22:52:48 +02:00
result = result . Or ( v )
if result . True ( ) {
return cty . True , nil
2020-09-22 15:06:42 +02:00
}
}
2020-10-23 22:52:48 +02:00
return result , nil
2020-09-22 15:06:42 +02:00
} ,
} )
2019-05-02 16:47:19 +02:00
// CoalesceFunc constructs a function that takes any number of arguments and
2019-04-12 19:57:52 +02:00
// returns the first one that isn't empty. This function was copied from go-cty
// stdlib and modified so that it returns the first *non-empty* non-null element
// from a sequence, instead of merely the first non-null.
var CoalesceFunc = function . New ( & function . Spec {
Params : [ ] function . Parameter { } ,
VarParam : & function . Parameter {
Name : "vals" ,
Type : cty . DynamicPseudoType ,
AllowUnknown : true ,
AllowDynamicType : true ,
AllowNull : true ,
} ,
Type : func ( args [ ] cty . Value ) ( ret cty . Type , err error ) {
argTypes := make ( [ ] cty . Type , len ( args ) )
for i , val := range args {
argTypes [ i ] = val . Type ( )
}
retType , _ := convert . UnifyUnsafe ( argTypes )
if retType == cty . NilType {
2019-05-01 23:55:44 +02:00
return cty . NilType , errors . New ( "all arguments must have the same type" )
2019-04-12 19:57:52 +02:00
}
return retType , nil
} ,
Impl : func ( args [ ] cty . Value , retType cty . Type ) ( ret cty . Value , err error ) {
for _ , argVal := range args {
// We already know this will succeed because of the checks in our Type func above
argVal , _ = convert . Convert ( argVal , retType )
if ! argVal . IsKnown ( ) {
return cty . UnknownVal ( retType ) , nil
}
if argVal . IsNull ( ) {
continue
}
if retType == cty . String && argVal . RawEquals ( cty . StringVal ( "" ) ) {
continue
}
return argVal , nil
}
2019-05-01 23:55:44 +02:00
return cty . NilVal , errors . New ( "no non-null, non-empty-string arguments" )
2019-04-12 19:57:52 +02:00
} ,
} )
2019-05-02 16:47:19 +02:00
// IndexFunc constructs a function that finds the element index for a given value in a list.
2018-05-25 21:57:26 +02:00
var IndexFunc = function . New ( & function . Spec {
Params : [ ] function . Parameter {
{
Name : "list" ,
Type : cty . DynamicPseudoType ,
} ,
{
Name : "value" ,
Type : cty . DynamicPseudoType ,
} ,
} ,
Type : function . StaticReturnType ( cty . Number ) ,
Impl : func ( args [ ] cty . Value , retType cty . Type ) ( ret cty . Value , err error ) {
2018-05-26 01:30:50 +02:00
if ! ( args [ 0 ] . Type ( ) . IsListType ( ) || args [ 0 ] . Type ( ) . IsTupleType ( ) ) {
2019-05-01 23:55:44 +02:00
return cty . NilVal , errors . New ( "argument must be a list or tuple" )
2018-05-26 01:30:50 +02:00
}
2018-10-19 01:19:59 +02:00
if ! args [ 0 ] . IsKnown ( ) {
return cty . UnknownVal ( cty . Number ) , nil
}
2018-05-25 21:57:26 +02:00
if args [ 0 ] . LengthInt ( ) == 0 { // Easy path
2019-05-01 23:55:44 +02:00
return cty . NilVal , errors . New ( "cannot search an empty list" )
2018-05-25 21:57:26 +02:00
}
for it := args [ 0 ] . ElementIterator ( ) ; it . Next ( ) ; {
i , v := it . Element ( )
eq , err := stdlib . Equal ( v , args [ 1 ] )
if err != nil {
return cty . NilVal , err
}
if ! eq . IsKnown ( ) {
return cty . UnknownVal ( cty . Number ) , nil
}
if eq . True ( ) {
return i , nil
}
}
2019-05-01 23:55:44 +02:00
return cty . NilVal , errors . New ( "item not found" )
2018-05-25 21:57:26 +02:00
} ,
} )
2019-05-01 20:22:50 +02:00
// Flatten until it's not a cty.List, and return whether the value is known.
// We can flatten lists with unknown values, as long as they are not
// lists themselves.
func flattener ( flattenList cty . Value ) ( [ ] cty . Value , bool ) {
out := make ( [ ] cty . Value , 0 )
2018-05-30 16:26:19 +02:00
for it := flattenList . ElementIterator ( ) ; it . Next ( ) ; {
_ , val := it . Element ( )
2019-05-01 16:19:40 +02:00
if val . Type ( ) . IsListType ( ) || val . Type ( ) . IsSetType ( ) || val . Type ( ) . IsTupleType ( ) {
2019-05-01 20:22:50 +02:00
if ! val . IsKnown ( ) {
return out , false
}
res , known := flattener ( val )
if ! known {
return res , known
}
out = append ( out , res ... )
2018-05-30 16:26:19 +02:00
} else {
2019-05-01 20:22:50 +02:00
out = append ( out , val )
2018-05-30 16:26:19 +02:00
}
}
2019-05-01 20:22:50 +02:00
return out , true
2018-05-30 16:26:19 +02:00
}
2019-05-02 16:47:19 +02:00
// LookupFunc constructs a function that performs dynamic lookups of map types.
2018-05-31 18:33:43 +02:00
var LookupFunc = function . New ( & function . Spec {
Params : [ ] function . Parameter {
{
Name : "inputMap" ,
2018-10-16 20:04:19 +02:00
Type : cty . DynamicPseudoType ,
2018-05-31 18:33:43 +02:00
} ,
{
Name : "key" ,
Type : cty . String ,
} ,
} ,
VarParam : & function . Parameter {
Name : "default" ,
Type : cty . DynamicPseudoType ,
AllowUnknown : true ,
AllowDynamicType : true ,
AllowNull : true ,
} ,
2018-06-06 19:43:58 +02:00
Type : func ( args [ ] cty . Value ) ( ret cty . Type , err error ) {
2018-05-31 18:33:43 +02:00
if len ( args ) < 1 || len ( args ) > 3 {
2018-06-06 19:43:58 +02:00
return cty . NilType , fmt . Errorf ( "lookup() takes two or three arguments, got %d" , len ( args ) )
2018-05-31 18:33:43 +02:00
}
2018-10-16 21:09:04 +02:00
2018-10-16 20:04:19 +02:00
ty := args [ 0 ] . Type ( )
2018-10-22 12:58:47 +02:00
2018-10-16 20:04:19 +02:00
switch {
case ty . IsObjectType ( ) :
2018-10-22 12:58:47 +02:00
if ! args [ 1 ] . IsKnown ( ) {
2018-10-23 12:42:46 +02:00
return cty . DynamicPseudoType , nil
2018-10-22 12:58:47 +02:00
}
key := args [ 1 ] . AsString ( )
2018-10-16 21:09:04 +02:00
if ty . HasAttribute ( key ) {
return args [ 0 ] . GetAttr ( key ) . Type ( ) , nil
} else if len ( args ) == 3 {
// if the key isn't found but a default is provided,
// return the default type
return args [ 2 ] . Type ( ) , nil
}
2018-10-16 23:55:57 +02:00
return cty . DynamicPseudoType , function . NewArgErrorf ( 0 , "the given object has no attribute %q" , key )
2018-10-16 21:57:05 +02:00
case ty . IsMapType ( ) :
2019-08-01 21:52:52 +02:00
if len ( args ) == 3 {
_ , err = convert . Convert ( args [ 2 ] , ty . ElementType ( ) )
if err != nil {
return cty . NilType , function . NewArgErrorf ( 2 , "the default value must have the same type as the map elements" )
}
}
2018-10-16 21:09:04 +02:00
return ty . ElementType ( ) , nil
2018-10-16 21:57:05 +02:00
default :
2018-10-16 23:55:57 +02:00
return cty . NilType , function . NewArgErrorf ( 0 , "lookup() requires a map as the first argument" )
2018-10-16 20:04:19 +02:00
}
2018-06-06 19:43:58 +02:00
} ,
Impl : func ( args [ ] cty . Value , retType cty . Type ) ( ret cty . Value , err error ) {
2018-06-06 17:09:33 +02:00
var defaultVal cty . Value
2018-05-31 18:33:43 +02:00
defaultValueSet := false
if len ( args ) == 3 {
2018-06-06 17:09:33 +02:00
defaultVal = args [ 2 ]
2018-05-31 18:33:43 +02:00
defaultValueSet = true
}
mapVar := args [ 0 ]
lookupKey := args [ 1 ] . AsString ( )
2020-10-05 14:48:49 +02:00
if ! mapVar . IsKnown ( ) {
2018-06-06 19:43:58 +02:00
return cty . UnknownVal ( retType ) , nil
}
2018-10-16 21:09:04 +02:00
if mapVar . Type ( ) . IsObjectType ( ) {
if mapVar . Type ( ) . HasAttribute ( lookupKey ) {
return mapVar . GetAttr ( lookupKey ) , nil
2018-10-16 20:04:19 +02:00
}
2018-10-16 21:09:04 +02:00
} else if mapVar . HasIndex ( cty . StringVal ( lookupKey ) ) == cty . True {
2019-08-01 21:52:52 +02:00
return mapVar . Index ( cty . StringVal ( lookupKey ) ) , nil
2018-05-31 18:33:43 +02:00
}
if defaultValueSet {
2018-06-06 19:43:58 +02:00
defaultVal , err = convert . Convert ( defaultVal , retType )
if err != nil {
return cty . NilVal , err
2018-06-06 17:09:33 +02:00
}
2018-06-06 19:43:58 +02:00
return defaultVal , nil
2018-05-31 18:33:43 +02:00
}
2018-06-06 17:09:33 +02:00
return cty . UnknownVal ( cty . DynamicPseudoType ) , fmt . Errorf (
2018-05-31 18:33:43 +02:00
"lookup failed to find '%s'" , lookupKey )
} ,
} )
2019-05-02 16:47:19 +02:00
// MatchkeysFunc constructs a function that constructs a new list by taking a
2018-05-29 22:58:32 +02:00
// subset of elements from one list whose indexes match the corresponding
// indexes of values in another list.
var MatchkeysFunc = function . New ( & function . Spec {
Params : [ ] function . Parameter {
{
Name : "values" ,
Type : cty . List ( cty . DynamicPseudoType ) ,
} ,
{
Name : "keys" ,
Type : cty . List ( cty . DynamicPseudoType ) ,
} ,
{
Name : "searchset" ,
Type : cty . List ( cty . DynamicPseudoType ) ,
} ,
} ,
Type : func ( args [ ] cty . Value ) ( cty . Type , error ) {
2019-06-04 17:54:26 +02:00
ty , _ := convert . UnifyUnsafe ( [ ] cty . Type { args [ 1 ] . Type ( ) , args [ 2 ] . Type ( ) } )
2019-06-04 00:00:53 +02:00
if ty == cty . NilType {
2019-06-04 14:36:30 +02:00
return cty . NilType , errors . New ( "keys and searchset must be of the same type" )
2018-05-29 22:58:32 +02:00
}
2019-06-04 14:36:30 +02:00
// the return type is based on args[0] (values)
2018-05-29 22:58:32 +02:00
return args [ 0 ] . Type ( ) , nil
} ,
Impl : func ( args [ ] cty . Value , retType cty . Type ) ( ret cty . Value , err error ) {
2018-10-19 01:19:59 +02:00
if ! args [ 0 ] . IsKnown ( ) {
return cty . UnknownVal ( cty . List ( retType . ElementType ( ) ) ) , nil
}
2018-05-30 16:26:19 +02:00
if args [ 0 ] . LengthInt ( ) != args [ 1 ] . LengthInt ( ) {
2019-05-01 23:55:44 +02:00
return cty . ListValEmpty ( retType . ElementType ( ) ) , errors . New ( "length of keys and values should be equal" )
2018-05-30 16:26:19 +02:00
}
2018-05-29 22:58:32 +02:00
output := make ( [ ] cty . Value , 0 )
values := args [ 0 ]
2019-06-04 14:36:30 +02:00
// Keys and searchset must be the same type.
// We can skip error checking here because we've already verified that
// they can be unified in the Type function
2019-06-04 17:54:26 +02:00
ty , _ := convert . UnifyUnsafe ( [ ] cty . Type { args [ 1 ] . Type ( ) , args [ 2 ] . Type ( ) } )
2019-06-04 14:36:30 +02:00
keys , _ := convert . Convert ( args [ 1 ] , ty )
searchset , _ := convert . Convert ( args [ 2 ] , ty )
2018-05-29 22:58:32 +02:00
// if searchset is empty, return an empty list.
if searchset . LengthInt ( ) == 0 {
2018-05-30 16:26:19 +02:00
return cty . ListValEmpty ( retType . ElementType ( ) ) , nil
2018-05-29 22:58:32 +02:00
}
2018-06-07 19:51:57 +02:00
if ! values . IsWhollyKnown ( ) || ! keys . IsWhollyKnown ( ) {
2018-06-06 19:43:58 +02:00
return cty . UnknownVal ( retType ) , nil
}
2018-05-29 22:58:32 +02:00
i := 0
for it := keys . ElementIterator ( ) ; it . Next ( ) ; {
_ , key := it . Element ( )
for iter := searchset . ElementIterator ( ) ; iter . Next ( ) ; {
_ , search := iter . Element ( )
eq , err := stdlib . Equal ( key , search )
if err != nil {
return cty . NilVal , err
}
2018-05-30 16:26:19 +02:00
if ! eq . IsKnown ( ) {
return cty . ListValEmpty ( retType . ElementType ( ) ) , nil
}
2018-05-29 22:58:32 +02:00
if eq . True ( ) {
v := values . Index ( cty . NumberIntVal ( int64 ( i ) ) )
output = append ( output , v )
break
}
}
i ++
}
// if we haven't matched any key, then output is an empty list.
if len ( output ) == 0 {
2018-05-30 16:26:19 +02:00
return cty . ListValEmpty ( retType . ElementType ( ) ) , nil
2018-05-29 22:58:32 +02:00
}
return cty . ListVal ( output ) , nil
} ,
} )
2020-04-15 20:27:06 +02:00
// SumFunc constructs a function that returns the sum of all
// numbers provided in a list
var SumFunc = function . New ( & function . Spec {
Params : [ ] function . Parameter {
{
Name : "list" ,
Type : cty . DynamicPseudoType ,
} ,
} ,
Type : function . StaticReturnType ( cty . Number ) ,
Impl : func ( args [ ] cty . Value , retType cty . Type ) ( ret cty . Value , err error ) {
if ! args [ 0 ] . CanIterateElements ( ) {
return cty . NilVal , function . NewArgErrorf ( 0 , "cannot sum noniterable" )
}
if args [ 0 ] . LengthInt ( ) == 0 { // Easy path
return cty . NilVal , function . NewArgErrorf ( 0 , "cannot sum an empty list" )
}
arg := args [ 0 ] . AsValueSlice ( )
ty := args [ 0 ] . Type ( )
var i float64
var s float64
if ! ty . IsListType ( ) && ! ty . IsSetType ( ) && ! ty . IsTupleType ( ) {
return cty . NilVal , function . NewArgErrorf ( 0 , fmt . Sprintf ( "argument must be list, set, or tuple. Received %s" , ty . FriendlyName ( ) ) )
}
if ! args [ 0 ] . IsKnown ( ) {
return cty . UnknownVal ( cty . Number ) , nil
}
for _ , v := range arg {
if err := gocty . FromCtyValue ( v , & i ) ; err != nil {
return cty . UnknownVal ( cty . Number ) , function . NewArgErrorf ( 0 , "argument must be list, set, or tuple of number values" )
} else {
s += i
}
}
return cty . NumberFloatVal ( s ) , nil
} ,
} )
2019-05-02 16:47:19 +02:00
// TransposeFunc constructs a function that takes a map of lists of strings and
2018-05-31 23:46:24 +02:00
// swaps the keys and values to produce a new map of lists of strings.
var TransposeFunc = function . New ( & function . Spec {
Params : [ ] function . Parameter {
{
Name : "values" ,
Type : cty . Map ( cty . List ( cty . String ) ) ,
} ,
} ,
Type : function . StaticReturnType ( cty . Map ( cty . List ( cty . String ) ) ) ,
Impl : func ( args [ ] cty . Value , retType cty . Type ) ( ret cty . Value , err error ) {
inputMap := args [ 0 ]
2018-06-01 00:49:15 +02:00
if ! inputMap . IsWhollyKnown ( ) {
return cty . UnknownVal ( retType ) , nil
}
2018-05-31 23:46:24 +02:00
outputMap := make ( map [ string ] cty . Value )
tmpMap := make ( map [ string ] [ ] string )
for it := inputMap . ElementIterator ( ) ; it . Next ( ) ; {
inKey , inVal := it . Element ( )
for iter := inVal . ElementIterator ( ) ; iter . Next ( ) ; {
_ , val := iter . Element ( )
if ! val . Type ( ) . Equals ( cty . String ) {
2019-05-01 23:55:44 +02:00
return cty . MapValEmpty ( cty . List ( cty . String ) ) , errors . New ( "input must be a map of lists of strings" )
2018-05-31 23:46:24 +02:00
}
outKey := val . AsString ( )
if _ , ok := tmpMap [ outKey ] ; ! ok {
tmpMap [ outKey ] = make ( [ ] string , 0 )
}
outVal := tmpMap [ outKey ]
outVal = append ( outVal , inKey . AsString ( ) )
sort . Strings ( outVal )
tmpMap [ outKey ] = outVal
}
}
for outKey , outVal := range tmpMap {
values := make ( [ ] cty . Value , 0 )
for _ , v := range outVal {
values = append ( values , cty . StringVal ( v ) )
}
outputMap [ outKey ] = cty . ListVal ( values )
}
2019-11-08 18:40:39 +01:00
if len ( outputMap ) == 0 {
return cty . MapValEmpty ( cty . List ( cty . String ) ) , nil
}
2018-05-31 23:46:24 +02:00
return cty . MapVal ( outputMap ) , nil
} ,
} )
2020-11-04 22:18:44 +01:00
// ListFunc constructs a function that takes an arbitrary number of arguments
// and returns a list containing those values in the same order.
//
// This function is deprecated in Terraform v0.12
var ListFunc = function . New ( & function . Spec {
Params : [ ] function . Parameter { } ,
VarParam : & function . Parameter {
Name : "vals" ,
Type : cty . DynamicPseudoType ,
AllowUnknown : true ,
AllowDynamicType : true ,
AllowNull : true ,
} ,
Type : func ( args [ ] cty . Value ) ( ret cty . Type , err error ) {
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" )
} ,
Impl : func ( args [ ] cty . Value , retType cty . Type ) ( ret cty . Value , err error ) {
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" )
} ,
} )
// MapFunc constructs a function that takes an even number of arguments and
// returns a map whose elements are constructed from consecutive pairs of arguments.
//
// This function is deprecated in Terraform v0.12
var MapFunc = function . New ( & function . Spec {
Params : [ ] function . Parameter { } ,
VarParam : & function . Parameter {
Name : "vals" ,
Type : cty . DynamicPseudoType ,
AllowUnknown : true ,
AllowDynamicType : true ,
AllowNull : true ,
} ,
Type : func ( args [ ] cty . Value ) ( ret cty . Type , err error ) {
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" )
} ,
Impl : func ( args [ ] cty . Value , retType cty . Type ) ( ret cty . Value , err error ) {
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" )
} ,
} )
2018-05-31 00:38:55 +02:00
// helper function to add an element to a list, if it does not already exist
2018-05-26 01:15:50 +02:00
func appendIfMissing ( slice [ ] cty . Value , element cty . Value ) ( [ ] cty . Value , error ) {
for _ , ele := range slice {
eq , err := stdlib . Equal ( ele , element )
if err != nil {
return slice , err
}
if eq . True ( ) {
return slice , nil
}
}
return append ( slice , element ) , nil
}
2018-05-22 02:39:26 +02:00
// Length returns the number of elements in the given collection or number of
// Unicode characters in the given string.
func Length ( collection cty . Value ) ( cty . Value , error ) {
return LengthFunc . Call ( [ ] cty . Value { collection } )
}
2018-05-24 20:48:44 +02:00
2020-10-23 22:52:48 +02:00
// AllTrue returns true if all elements of the list are true. If the list is empty,
// return true.
2020-09-22 15:06:42 +02:00
func AllTrue ( collection cty . Value ) ( cty . Value , error ) {
return AllTrueFunc . Call ( [ ] cty . Value { collection } )
}
2020-10-23 22:52:48 +02:00
// AnyTrue returns true if any element of the list is true. If the list is empty,
// return false.
func AnyTrue ( collection cty . Value ) ( cty . Value , error ) {
return AnyTrueFunc . Call ( [ ] cty . Value { collection } )
}
2019-04-12 19:57:52 +02:00
// Coalesce takes any number of arguments and returns the first one that isn't empty.
func Coalesce ( args ... cty . Value ) ( cty . Value , error ) {
return CoalesceFunc . Call ( args )
}
2018-05-25 21:57:26 +02:00
// Index finds the element index for a given value in a list.
func Index ( list , value cty . Value ) ( cty . Value , error ) {
return IndexFunc . Call ( [ ] cty . Value { list , value } )
}
2018-05-26 01:15:50 +02:00
2018-05-30 17:41:31 +02:00
// List takes any number of list arguments and returns a list containing those
// values in the same order.
func List ( args ... cty . Value ) ( cty . Value , error ) {
return ListFunc . Call ( args )
}
2018-05-31 18:33:43 +02:00
// Lookup performs a dynamic lookup into a map.
// There are two required arguments, map and key, plus an optional default,
// which is a value to return if no key is found in map.
func Lookup ( args ... cty . Value ) ( cty . Value , error ) {
return LookupFunc . Call ( args )
}
2018-05-30 22:37:48 +02:00
// Map takes an even number of arguments and returns a map whose elements are constructed
// from consecutive pairs of arguments.
func Map ( args ... cty . Value ) ( cty . Value , error ) {
return MapFunc . Call ( args )
}
2018-05-29 22:58:32 +02:00
// Matchkeys constructs a new list by taking a subset of elements from one list
// whose indexes match the corresponding indexes of values in another list.
func Matchkeys ( values , keys , searchset cty . Value ) ( cty . Value , error ) {
return MatchkeysFunc . Call ( [ ] cty . Value { values , keys , searchset } )
}
2018-05-31 20:47:50 +02:00
2020-04-15 20:27:06 +02:00
// Sum adds numbers in a list, set, or tuple
func Sum ( list cty . Value ) ( cty . Value , error ) {
return SumFunc . Call ( [ ] cty . Value { list } )
}
2018-05-31 23:46:24 +02:00
// Transpose takes a map of lists of strings and swaps the keys and values to
// produce a new map of lists of strings.
func Transpose ( values cty . Value ) ( cty . Value , error ) {
return TransposeFunc . Call ( [ ] cty . Value { values } )
}