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