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