github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/lang/funcs/collection.go (about) 1 package funcs 2 3 import ( 4 "errors" 5 "fmt" 6 "sort" 7 8 "github.com/zclconf/go-cty/cty" 9 "github.com/zclconf/go-cty/cty/convert" 10 "github.com/zclconf/go-cty/cty/function" 11 "github.com/zclconf/go-cty/cty/function/stdlib" 12 "github.com/zclconf/go-cty/cty/gocty" 13 ) 14 15 var ElementFunc = function.New(&function.Spec{ 16 Params: []function.Parameter{ 17 { 18 Name: "list", 19 Type: cty.DynamicPseudoType, 20 }, 21 { 22 Name: "index", 23 Type: cty.Number, 24 }, 25 }, 26 Type: func(args []cty.Value) (cty.Type, error) { 27 list := args[0] 28 listTy := list.Type() 29 switch { 30 case listTy.IsListType(): 31 return listTy.ElementType(), nil 32 case listTy.IsTupleType(): 33 if !args[1].IsKnown() { 34 // If the index isn't known yet then we can't predict the 35 // result type since each tuple element can have its own type. 36 return cty.DynamicPseudoType, nil 37 } 38 39 etys := listTy.TupleElementTypes() 40 var index int 41 err := gocty.FromCtyValue(args[1], &index) 42 if err != nil { 43 // e.g. fractional number where whole number is required 44 return cty.DynamicPseudoType, fmt.Errorf("invalid index: %s", err) 45 } 46 if len(etys) == 0 { 47 return cty.DynamicPseudoType, errors.New("cannot use element function with an empty list") 48 } 49 index = index % len(etys) 50 return etys[index], nil 51 default: 52 return cty.DynamicPseudoType, fmt.Errorf("cannot read elements from %s", listTy.FriendlyName()) 53 } 54 }, 55 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 56 var index int 57 err := gocty.FromCtyValue(args[1], &index) 58 if err != nil { 59 // can't happen because we checked this in the Type function above 60 return cty.DynamicVal, fmt.Errorf("invalid index: %s", err) 61 } 62 63 if !args[0].IsKnown() { 64 return cty.UnknownVal(retType), nil 65 } 66 67 l := args[0].LengthInt() 68 if l == 0 { 69 return cty.DynamicVal, errors.New("cannot use element function with an empty list") 70 } 71 index = index % l 72 73 // We did all the necessary type checks in the type function above, 74 // so this is guaranteed not to fail. 75 return args[0].Index(cty.NumberIntVal(int64(index))), nil 76 }, 77 }) 78 79 var LengthFunc = function.New(&function.Spec{ 80 Params: []function.Parameter{ 81 { 82 Name: "value", 83 Type: cty.DynamicPseudoType, 84 AllowDynamicType: true, 85 AllowUnknown: true, 86 }, 87 }, 88 Type: func(args []cty.Value) (cty.Type, error) { 89 collTy := args[0].Type() 90 switch { 91 case collTy == cty.String || collTy.IsTupleType() || collTy.IsObjectType() || collTy.IsListType() || collTy.IsMapType() || collTy.IsSetType() || collTy == cty.DynamicPseudoType: 92 return cty.Number, nil 93 default: 94 return cty.Number, errors.New("argument must be a string, a collection type, or a structural type") 95 } 96 }, 97 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 98 coll := args[0] 99 collTy := args[0].Type() 100 switch { 101 case collTy == cty.DynamicPseudoType: 102 return cty.UnknownVal(cty.Number), nil 103 case collTy.IsTupleType(): 104 l := len(collTy.TupleElementTypes()) 105 return cty.NumberIntVal(int64(l)), nil 106 case collTy.IsObjectType(): 107 l := len(collTy.AttributeTypes()) 108 return cty.NumberIntVal(int64(l)), nil 109 case collTy == cty.String: 110 // We'll delegate to the cty stdlib strlen function here, because 111 // it deals with all of the complexities of tokenizing unicode 112 // grapheme clusters. 113 return stdlib.Strlen(coll) 114 case collTy.IsListType() || collTy.IsSetType() || collTy.IsMapType(): 115 return coll.Length(), nil 116 default: 117 // Should never happen, because of the checks in our Type func above 118 return cty.UnknownVal(cty.Number), errors.New("impossible value type for length(...)") 119 } 120 }, 121 }) 122 123 // CoalesceFunc constructs a function that takes any number of arguments and 124 // returns the first one that isn't empty. This function was copied from go-cty 125 // stdlib and modified so that it returns the first *non-empty* non-null element 126 // from a sequence, instead of merely the first non-null. 127 var CoalesceFunc = function.New(&function.Spec{ 128 Params: []function.Parameter{}, 129 VarParam: &function.Parameter{ 130 Name: "vals", 131 Type: cty.DynamicPseudoType, 132 AllowUnknown: true, 133 AllowDynamicType: true, 134 AllowNull: true, 135 }, 136 Type: func(args []cty.Value) (ret cty.Type, err error) { 137 argTypes := make([]cty.Type, len(args)) 138 for i, val := range args { 139 argTypes[i] = val.Type() 140 } 141 retType, _ := convert.UnifyUnsafe(argTypes) 142 if retType == cty.NilType { 143 return cty.NilType, errors.New("all arguments must have the same type") 144 } 145 return retType, nil 146 }, 147 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 148 for _, argVal := range args { 149 // We already know this will succeed because of the checks in our Type func above 150 argVal, _ = convert.Convert(argVal, retType) 151 if !argVal.IsKnown() { 152 return cty.UnknownVal(retType), nil 153 } 154 if argVal.IsNull() { 155 continue 156 } 157 if retType == cty.String && argVal.RawEquals(cty.StringVal("")) { 158 continue 159 } 160 161 return argVal, nil 162 } 163 return cty.NilVal, errors.New("no non-null, non-empty-string arguments") 164 }, 165 }) 166 167 // CoalesceListFunc constructs a function that takes any number of list arguments 168 // and returns the first one that isn't empty. 169 var CoalesceListFunc = function.New(&function.Spec{ 170 Params: []function.Parameter{}, 171 VarParam: &function.Parameter{ 172 Name: "vals", 173 Type: cty.DynamicPseudoType, 174 AllowUnknown: true, 175 AllowDynamicType: true, 176 AllowNull: true, 177 }, 178 Type: func(args []cty.Value) (ret cty.Type, err error) { 179 if len(args) == 0 { 180 return cty.NilType, errors.New("at least one argument is required") 181 } 182 183 argTypes := make([]cty.Type, len(args)) 184 185 for i, arg := range args { 186 // if any argument is unknown, we can't be certain know which type we will return 187 if !arg.IsKnown() { 188 return cty.DynamicPseudoType, nil 189 } 190 ty := arg.Type() 191 192 if !ty.IsListType() && !ty.IsTupleType() { 193 return cty.NilType, errors.New("coalescelist arguments must be lists or tuples") 194 } 195 196 argTypes[i] = arg.Type() 197 } 198 199 last := argTypes[0] 200 // If there are mixed types, we have to return a dynamic type. 201 for _, next := range argTypes[1:] { 202 if !next.Equals(last) { 203 return cty.DynamicPseudoType, nil 204 } 205 } 206 207 return last, nil 208 }, 209 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 210 for _, arg := range args { 211 if !arg.IsKnown() { 212 // If we run into an unknown list at some point, we can't 213 // predict the final result yet. (If there's a known, non-empty 214 // arg before this then we won't get here.) 215 return cty.UnknownVal(retType), nil 216 } 217 218 if arg.LengthInt() > 0 { 219 return arg, nil 220 } 221 } 222 223 return cty.NilVal, errors.New("no non-null arguments") 224 }, 225 }) 226 227 // CompactFunc constructs a function that takes a list of strings and returns a new list 228 // with any empty string elements removed. 229 var CompactFunc = function.New(&function.Spec{ 230 Params: []function.Parameter{ 231 { 232 Name: "list", 233 Type: cty.List(cty.String), 234 }, 235 }, 236 Type: function.StaticReturnType(cty.List(cty.String)), 237 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 238 listVal := args[0] 239 if !listVal.IsWhollyKnown() { 240 // If some of the element values aren't known yet then we 241 // can't yet return a compacted list 242 return cty.UnknownVal(retType), nil 243 } 244 245 var outputList []cty.Value 246 247 for it := listVal.ElementIterator(); it.Next(); { 248 _, v := it.Element() 249 if v.IsNull() || v.AsString() == "" { 250 continue 251 } 252 outputList = append(outputList, v) 253 } 254 255 if len(outputList) == 0 { 256 return cty.ListValEmpty(cty.String), nil 257 } 258 259 return cty.ListVal(outputList), nil 260 }, 261 }) 262 263 // ContainsFunc constructs a function that determines whether a given list or 264 // set contains a given single value as one of its elements. 265 var ContainsFunc = function.New(&function.Spec{ 266 Params: []function.Parameter{ 267 { 268 Name: "list", 269 Type: cty.DynamicPseudoType, 270 }, 271 { 272 Name: "value", 273 Type: cty.DynamicPseudoType, 274 }, 275 }, 276 Type: function.StaticReturnType(cty.Bool), 277 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 278 arg := args[0] 279 ty := arg.Type() 280 281 if !ty.IsListType() && !ty.IsTupleType() && !ty.IsSetType() { 282 return cty.NilVal, errors.New("argument must be list, tuple, or set") 283 } 284 285 _, err = Index(cty.TupleVal(arg.AsValueSlice()), args[1]) 286 if err != nil { 287 return cty.False, nil 288 } 289 290 return cty.True, nil 291 }, 292 }) 293 294 // IndexFunc constructs a function that finds the element index for a given value in a list. 295 var IndexFunc = function.New(&function.Spec{ 296 Params: []function.Parameter{ 297 { 298 Name: "list", 299 Type: cty.DynamicPseudoType, 300 }, 301 { 302 Name: "value", 303 Type: cty.DynamicPseudoType, 304 }, 305 }, 306 Type: function.StaticReturnType(cty.Number), 307 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 308 if !(args[0].Type().IsListType() || args[0].Type().IsTupleType()) { 309 return cty.NilVal, errors.New("argument must be a list or tuple") 310 } 311 312 if !args[0].IsKnown() { 313 return cty.UnknownVal(cty.Number), nil 314 } 315 316 if args[0].LengthInt() == 0 { // Easy path 317 return cty.NilVal, errors.New("cannot search an empty list") 318 } 319 320 for it := args[0].ElementIterator(); it.Next(); { 321 i, v := it.Element() 322 eq, err := stdlib.Equal(v, args[1]) 323 if err != nil { 324 return cty.NilVal, err 325 } 326 if !eq.IsKnown() { 327 return cty.UnknownVal(cty.Number), nil 328 } 329 if eq.True() { 330 return i, nil 331 } 332 } 333 return cty.NilVal, errors.New("item not found") 334 335 }, 336 }) 337 338 // DistinctFunc constructs a function that takes a list and returns a new list 339 // with any duplicate elements removed. 340 var DistinctFunc = function.New(&function.Spec{ 341 Params: []function.Parameter{ 342 { 343 Name: "list", 344 Type: cty.List(cty.DynamicPseudoType), 345 }, 346 }, 347 Type: func(args []cty.Value) (cty.Type, error) { 348 return args[0].Type(), nil 349 }, 350 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 351 listVal := args[0] 352 353 if !listVal.IsWhollyKnown() { 354 return cty.UnknownVal(retType), nil 355 } 356 var list []cty.Value 357 358 for it := listVal.ElementIterator(); it.Next(); { 359 _, v := it.Element() 360 list, err = appendIfMissing(list, v) 361 if err != nil { 362 return cty.NilVal, err 363 } 364 } 365 366 if len(list) == 0 { 367 return cty.ListValEmpty(retType.ElementType()), nil 368 } 369 return cty.ListVal(list), nil 370 }, 371 }) 372 373 // ChunklistFunc constructs a function that splits a single list into fixed-size chunks, 374 // returning a list of lists. 375 var ChunklistFunc = function.New(&function.Spec{ 376 Params: []function.Parameter{ 377 { 378 Name: "list", 379 Type: cty.List(cty.DynamicPseudoType), 380 }, 381 { 382 Name: "size", 383 Type: cty.Number, 384 }, 385 }, 386 Type: func(args []cty.Value) (cty.Type, error) { 387 return cty.List(args[0].Type()), nil 388 }, 389 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 390 listVal := args[0] 391 if !listVal.IsKnown() { 392 return cty.UnknownVal(retType), nil 393 } 394 395 if listVal.LengthInt() == 0 { 396 return cty.ListValEmpty(listVal.Type()), nil 397 } 398 399 var size int 400 err = gocty.FromCtyValue(args[1], &size) 401 if err != nil { 402 return cty.NilVal, fmt.Errorf("invalid index: %s", err) 403 } 404 405 if size < 0 { 406 return cty.NilVal, errors.New("the size argument must be positive") 407 } 408 409 output := make([]cty.Value, 0) 410 411 // if size is 0, returns a list made of the initial list 412 if size == 0 { 413 output = append(output, listVal) 414 return cty.ListVal(output), nil 415 } 416 417 chunk := make([]cty.Value, 0) 418 419 l := args[0].LengthInt() 420 i := 0 421 422 for it := listVal.ElementIterator(); it.Next(); { 423 _, v := it.Element() 424 chunk = append(chunk, v) 425 426 // Chunk when index isn't 0, or when reaching the values's length 427 if (i+1)%size == 0 || (i+1) == l { 428 output = append(output, cty.ListVal(chunk)) 429 chunk = make([]cty.Value, 0) 430 } 431 i++ 432 } 433 434 return cty.ListVal(output), nil 435 }, 436 }) 437 438 // FlattenFunc constructs a function that takes a list and replaces any elements 439 // that are lists with a flattened sequence of the list contents. 440 var FlattenFunc = function.New(&function.Spec{ 441 Params: []function.Parameter{ 442 { 443 Name: "list", 444 Type: cty.DynamicPseudoType, 445 }, 446 }, 447 Type: func(args []cty.Value) (cty.Type, error) { 448 if !args[0].IsWhollyKnown() { 449 return cty.DynamicPseudoType, nil 450 } 451 452 argTy := args[0].Type() 453 if !argTy.IsListType() && !argTy.IsSetType() && !argTy.IsTupleType() { 454 return cty.NilType, errors.New("can only flatten lists, sets and tuples") 455 } 456 457 retVal, known := flattener(args[0]) 458 if !known { 459 return cty.DynamicPseudoType, nil 460 } 461 462 tys := make([]cty.Type, len(retVal)) 463 for i, ty := range retVal { 464 tys[i] = ty.Type() 465 } 466 return cty.Tuple(tys), nil 467 }, 468 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 469 inputList := args[0] 470 if inputList.LengthInt() == 0 { 471 return cty.EmptyTupleVal, nil 472 } 473 474 out, known := flattener(inputList) 475 if !known { 476 return cty.UnknownVal(retType), nil 477 } 478 479 return cty.TupleVal(out), nil 480 }, 481 }) 482 483 // Flatten until it's not a cty.List, and return whether the value is known. 484 // We can flatten lists with unknown values, as long as they are not 485 // lists themselves. 486 func flattener(flattenList cty.Value) ([]cty.Value, bool) { 487 out := make([]cty.Value, 0) 488 for it := flattenList.ElementIterator(); it.Next(); { 489 _, val := it.Element() 490 if val.Type().IsListType() || val.Type().IsSetType() || val.Type().IsTupleType() { 491 if !val.IsKnown() { 492 return out, false 493 } 494 495 res, known := flattener(val) 496 if !known { 497 return res, known 498 } 499 out = append(out, res...) 500 } else { 501 out = append(out, val) 502 } 503 } 504 return out, true 505 } 506 507 // KeysFunc constructs a function that takes a map and returns a sorted list of the map keys. 508 var KeysFunc = function.New(&function.Spec{ 509 Params: []function.Parameter{ 510 { 511 Name: "inputMap", 512 Type: cty.DynamicPseudoType, 513 AllowUnknown: true, 514 }, 515 }, 516 Type: func(args []cty.Value) (cty.Type, error) { 517 ty := args[0].Type() 518 switch { 519 case ty.IsMapType(): 520 return cty.List(cty.String), nil 521 case ty.IsObjectType(): 522 atys := ty.AttributeTypes() 523 if len(atys) == 0 { 524 return cty.EmptyTuple, nil 525 } 526 // All of our result elements will be strings, and atys just 527 // decides how many there are. 528 etys := make([]cty.Type, len(atys)) 529 for i := range etys { 530 etys[i] = cty.String 531 } 532 return cty.Tuple(etys), nil 533 default: 534 return cty.DynamicPseudoType, function.NewArgErrorf(0, "must have map or object type") 535 } 536 }, 537 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 538 m := args[0] 539 var keys []cty.Value 540 541 switch { 542 case m.Type().IsObjectType(): 543 // In this case we allow unknown values so we must work only with 544 // the attribute _types_, not with the value itself. 545 var names []string 546 for name := range m.Type().AttributeTypes() { 547 names = append(names, name) 548 } 549 sort.Strings(names) // same ordering guaranteed by cty's ElementIterator 550 if len(names) == 0 { 551 return cty.EmptyTupleVal, nil 552 } 553 keys = make([]cty.Value, len(names)) 554 for i, name := range names { 555 keys[i] = cty.StringVal(name) 556 } 557 return cty.TupleVal(keys), nil 558 default: 559 if !m.IsKnown() { 560 return cty.UnknownVal(retType), nil 561 } 562 563 // cty guarantees that ElementIterator will iterate in lexicographical 564 // order by key. 565 for it := args[0].ElementIterator(); it.Next(); { 566 k, _ := it.Element() 567 keys = append(keys, k) 568 } 569 if len(keys) == 0 { 570 return cty.ListValEmpty(cty.String), nil 571 } 572 return cty.ListVal(keys), nil 573 } 574 }, 575 }) 576 577 // ListFunc constructs a function that takes an arbitrary number of arguments 578 // and returns a list containing those values in the same order. 579 // 580 // This function is deprecated in Terraform v0.12 581 var ListFunc = function.New(&function.Spec{ 582 Params: []function.Parameter{}, 583 VarParam: &function.Parameter{ 584 Name: "vals", 585 Type: cty.DynamicPseudoType, 586 AllowUnknown: true, 587 AllowDynamicType: true, 588 AllowNull: true, 589 }, 590 Type: func(args []cty.Value) (ret cty.Type, err error) { 591 if len(args) == 0 { 592 return cty.NilType, errors.New("at least one argument is required") 593 } 594 595 argTypes := make([]cty.Type, len(args)) 596 597 for i, arg := range args { 598 argTypes[i] = arg.Type() 599 } 600 601 retType, _ := convert.UnifyUnsafe(argTypes) 602 if retType == cty.NilType { 603 return cty.NilType, errors.New("all arguments must have the same type") 604 } 605 606 return cty.List(retType), nil 607 }, 608 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 609 newList := make([]cty.Value, 0, len(args)) 610 611 for _, arg := range args { 612 // We already know this will succeed because of the checks in our Type func above 613 arg, _ = convert.Convert(arg, retType.ElementType()) 614 newList = append(newList, arg) 615 } 616 617 return cty.ListVal(newList), nil 618 }, 619 }) 620 621 // LookupFunc constructs a function that performs dynamic lookups of map types. 622 var LookupFunc = function.New(&function.Spec{ 623 Params: []function.Parameter{ 624 { 625 Name: "inputMap", 626 Type: cty.DynamicPseudoType, 627 }, 628 { 629 Name: "key", 630 Type: cty.String, 631 }, 632 }, 633 VarParam: &function.Parameter{ 634 Name: "default", 635 Type: cty.DynamicPseudoType, 636 AllowUnknown: true, 637 AllowDynamicType: true, 638 AllowNull: true, 639 }, 640 Type: func(args []cty.Value) (ret cty.Type, err error) { 641 if len(args) < 1 || len(args) > 3 { 642 return cty.NilType, fmt.Errorf("lookup() takes two or three arguments, got %d", len(args)) 643 } 644 645 ty := args[0].Type() 646 647 switch { 648 case ty.IsObjectType(): 649 if !args[1].IsKnown() { 650 return cty.DynamicPseudoType, nil 651 } 652 653 key := args[1].AsString() 654 if ty.HasAttribute(key) { 655 return args[0].GetAttr(key).Type(), nil 656 } else if len(args) == 3 { 657 // if the key isn't found but a default is provided, 658 // return the default type 659 return args[2].Type(), nil 660 } 661 return cty.DynamicPseudoType, function.NewArgErrorf(0, "the given object has no attribute %q", key) 662 case ty.IsMapType(): 663 if len(args) == 3 { 664 _, err = convert.Convert(args[2], ty.ElementType()) 665 if err != nil { 666 return cty.NilType, function.NewArgErrorf(2, "the default value must have the same type as the map elements") 667 } 668 } 669 return ty.ElementType(), nil 670 default: 671 return cty.NilType, function.NewArgErrorf(0, "lookup() requires a map as the first argument") 672 } 673 }, 674 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 675 var defaultVal cty.Value 676 defaultValueSet := false 677 678 if len(args) == 3 { 679 defaultVal = args[2] 680 defaultValueSet = true 681 } 682 683 mapVar := args[0] 684 lookupKey := args[1].AsString() 685 686 if !mapVar.IsWhollyKnown() { 687 return cty.UnknownVal(retType), nil 688 } 689 690 if mapVar.Type().IsObjectType() { 691 if mapVar.Type().HasAttribute(lookupKey) { 692 return mapVar.GetAttr(lookupKey), nil 693 } 694 } else if mapVar.HasIndex(cty.StringVal(lookupKey)) == cty.True { 695 return mapVar.Index(cty.StringVal(lookupKey)), nil 696 } 697 698 if defaultValueSet { 699 defaultVal, err = convert.Convert(defaultVal, retType) 700 if err != nil { 701 return cty.NilVal, err 702 } 703 return defaultVal, nil 704 } 705 706 return cty.UnknownVal(cty.DynamicPseudoType), fmt.Errorf( 707 "lookup failed to find '%s'", lookupKey) 708 }, 709 }) 710 711 // MapFunc constructs a function that takes an even number of arguments and 712 // returns a map whose elements are constructed from consecutive pairs of arguments. 713 // 714 // This function is deprecated in Terraform v0.12 715 var MapFunc = function.New(&function.Spec{ 716 Params: []function.Parameter{}, 717 VarParam: &function.Parameter{ 718 Name: "vals", 719 Type: cty.DynamicPseudoType, 720 AllowUnknown: true, 721 AllowDynamicType: true, 722 AllowNull: true, 723 }, 724 Type: func(args []cty.Value) (ret cty.Type, err error) { 725 if len(args) < 2 || len(args)%2 != 0 { 726 return cty.NilType, fmt.Errorf("map requires an even number of two or more arguments, got %d", len(args)) 727 } 728 729 argTypes := make([]cty.Type, len(args)/2) 730 index := 0 731 732 for i := 0; i < len(args); i += 2 { 733 argTypes[index] = args[i+1].Type() 734 index++ 735 } 736 737 valType, _ := convert.UnifyUnsafe(argTypes) 738 if valType == cty.NilType { 739 return cty.NilType, errors.New("all arguments must have the same type") 740 } 741 742 return cty.Map(valType), nil 743 }, 744 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 745 for _, arg := range args { 746 if !arg.IsWhollyKnown() { 747 return cty.UnknownVal(retType), nil 748 } 749 } 750 751 outputMap := make(map[string]cty.Value) 752 753 for i := 0; i < len(args); i += 2 { 754 755 key := args[i].AsString() 756 757 err := gocty.FromCtyValue(args[i], &key) 758 if err != nil { 759 return cty.NilVal, err 760 } 761 762 val := args[i+1] 763 764 var variable cty.Value 765 err = gocty.FromCtyValue(val, &variable) 766 if err != nil { 767 return cty.NilVal, err 768 } 769 770 // We already know this will succeed because of the checks in our Type func above 771 variable, _ = convert.Convert(variable, retType.ElementType()) 772 773 // Check for duplicate keys 774 if _, ok := outputMap[key]; ok { 775 return cty.NilVal, fmt.Errorf("argument %d is a duplicate key: %q", i+1, key) 776 } 777 outputMap[key] = variable 778 } 779 780 return cty.MapVal(outputMap), nil 781 }, 782 }) 783 784 // MatchkeysFunc constructs a function that constructs a new list by taking a 785 // subset of elements from one list whose indexes match the corresponding 786 // indexes of values in another list. 787 var MatchkeysFunc = function.New(&function.Spec{ 788 Params: []function.Parameter{ 789 { 790 Name: "values", 791 Type: cty.List(cty.DynamicPseudoType), 792 }, 793 { 794 Name: "keys", 795 Type: cty.List(cty.DynamicPseudoType), 796 }, 797 { 798 Name: "searchset", 799 Type: cty.List(cty.DynamicPseudoType), 800 }, 801 }, 802 Type: func(args []cty.Value) (cty.Type, error) { 803 ty, _ := convert.UnifyUnsafe([]cty.Type{args[1].Type(), args[2].Type()}) 804 if ty == cty.NilType { 805 return cty.NilType, errors.New("keys and searchset must be of the same type") 806 } 807 808 // the return type is based on args[0] (values) 809 return args[0].Type(), nil 810 }, 811 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 812 if !args[0].IsKnown() { 813 return cty.UnknownVal(cty.List(retType.ElementType())), nil 814 } 815 816 if args[0].LengthInt() != args[1].LengthInt() { 817 return cty.ListValEmpty(retType.ElementType()), errors.New("length of keys and values should be equal") 818 } 819 820 output := make([]cty.Value, 0) 821 values := args[0] 822 823 // Keys and searchset must be the same type. 824 // We can skip error checking here because we've already verified that 825 // they can be unified in the Type function 826 ty, _ := convert.UnifyUnsafe([]cty.Type{args[1].Type(), args[2].Type()}) 827 keys, _ := convert.Convert(args[1], ty) 828 searchset, _ := convert.Convert(args[2], ty) 829 830 // if searchset is empty, return an empty list. 831 if searchset.LengthInt() == 0 { 832 return cty.ListValEmpty(retType.ElementType()), nil 833 } 834 835 if !values.IsWhollyKnown() || !keys.IsWhollyKnown() { 836 return cty.UnknownVal(retType), nil 837 } 838 839 i := 0 840 for it := keys.ElementIterator(); it.Next(); { 841 _, key := it.Element() 842 for iter := searchset.ElementIterator(); iter.Next(); { 843 _, search := iter.Element() 844 eq, err := stdlib.Equal(key, search) 845 if err != nil { 846 return cty.NilVal, err 847 } 848 if !eq.IsKnown() { 849 return cty.ListValEmpty(retType.ElementType()), nil 850 } 851 if eq.True() { 852 v := values.Index(cty.NumberIntVal(int64(i))) 853 output = append(output, v) 854 break 855 } 856 } 857 i++ 858 } 859 860 // if we haven't matched any key, then output is an empty list. 861 if len(output) == 0 { 862 return cty.ListValEmpty(retType.ElementType()), nil 863 } 864 return cty.ListVal(output), nil 865 }, 866 }) 867 868 // MergeFunc constructs a function that takes an arbitrary number of maps and 869 // returns a single map that contains a merged set of elements from all of the maps. 870 // 871 // If more than one given map defines the same key then the one that is later in 872 // the argument sequence takes precedence. 873 var MergeFunc = function.New(&function.Spec{ 874 Params: []function.Parameter{}, 875 VarParam: &function.Parameter{ 876 Name: "maps", 877 Type: cty.DynamicPseudoType, 878 AllowDynamicType: true, 879 }, 880 Type: function.StaticReturnType(cty.DynamicPseudoType), 881 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 882 outputMap := make(map[string]cty.Value) 883 884 for _, arg := range args { 885 if !arg.IsWhollyKnown() { 886 return cty.UnknownVal(retType), nil 887 } 888 if !arg.Type().IsObjectType() && !arg.Type().IsMapType() { 889 return cty.NilVal, fmt.Errorf("arguments must be maps or objects, got %#v", arg.Type().FriendlyName()) 890 } 891 for it := arg.ElementIterator(); it.Next(); { 892 k, v := it.Element() 893 outputMap[k.AsString()] = v 894 } 895 } 896 return cty.ObjectVal(outputMap), nil 897 }, 898 }) 899 900 // ReverseFunc takes a sequence and produces a new sequence of the same length 901 // with all of the same elements as the given sequence but in reverse order. 902 var ReverseFunc = function.New(&function.Spec{ 903 Params: []function.Parameter{ 904 { 905 Name: "list", 906 Type: cty.DynamicPseudoType, 907 }, 908 }, 909 Type: func(args []cty.Value) (cty.Type, error) { 910 argTy := args[0].Type() 911 switch { 912 case argTy.IsTupleType(): 913 argTys := argTy.TupleElementTypes() 914 retTys := make([]cty.Type, len(argTys)) 915 for i, ty := range argTys { 916 retTys[len(retTys)-i-1] = ty 917 } 918 return cty.Tuple(retTys), nil 919 case argTy.IsListType(), argTy.IsSetType(): // We accept sets here to mimic the usual behavior of auto-converting to list 920 return cty.List(argTy.ElementType()), nil 921 default: 922 return cty.NilType, function.NewArgErrorf(0, "can only reverse list or tuple values, not %s", argTy.FriendlyName()) 923 } 924 }, 925 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 926 in := args[0].AsValueSlice() 927 outVals := make([]cty.Value, len(in)) 928 for i, v := range in { 929 outVals[len(outVals)-i-1] = v 930 } 931 switch { 932 case retType.IsTupleType(): 933 return cty.TupleVal(outVals), nil 934 default: 935 if len(outVals) == 0 { 936 return cty.ListValEmpty(retType.ElementType()), nil 937 } 938 return cty.ListVal(outVals), nil 939 } 940 }, 941 }) 942 943 // SetProductFunc calculates the cartesian product of two or more sets or 944 // sequences. If the arguments are all lists then the result is a list of tuples, 945 // preserving the ordering of all of the input lists. Otherwise the result is a 946 // set of tuples. 947 var SetProductFunc = function.New(&function.Spec{ 948 Params: []function.Parameter{}, 949 VarParam: &function.Parameter{ 950 Name: "sets", 951 Type: cty.DynamicPseudoType, 952 }, 953 Type: func(args []cty.Value) (retType cty.Type, err error) { 954 if len(args) < 2 { 955 return cty.NilType, errors.New("at least two arguments are required") 956 } 957 958 listCount := 0 959 elemTys := make([]cty.Type, len(args)) 960 for i, arg := range args { 961 aty := arg.Type() 962 switch { 963 case aty.IsSetType(): 964 elemTys[i] = aty.ElementType() 965 case aty.IsListType(): 966 elemTys[i] = aty.ElementType() 967 listCount++ 968 case aty.IsTupleType(): 969 // We can accept a tuple type only if there's some common type 970 // that all of its elements can be converted to. 971 allEtys := aty.TupleElementTypes() 972 if len(allEtys) == 0 { 973 elemTys[i] = cty.DynamicPseudoType 974 listCount++ 975 break 976 } 977 ety, _ := convert.UnifyUnsafe(allEtys) 978 if ety == cty.NilType { 979 return cty.NilType, function.NewArgErrorf(i, "all elements must be of the same type") 980 } 981 elemTys[i] = ety 982 listCount++ 983 default: 984 return cty.NilType, function.NewArgErrorf(i, "a set or a list is required") 985 } 986 } 987 988 if listCount == len(args) { 989 return cty.List(cty.Tuple(elemTys)), nil 990 } 991 return cty.Set(cty.Tuple(elemTys)), nil 992 }, 993 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 994 ety := retType.ElementType() 995 996 total := 1 997 for _, arg := range args { 998 // Because of our type checking function, we are guaranteed that 999 // all of the arguments are known, non-null values of types that 1000 // support LengthInt. 1001 total *= arg.LengthInt() 1002 } 1003 1004 if total == 0 { 1005 // If any of the arguments was an empty collection then our result 1006 // is also an empty collection, which we'll short-circuit here. 1007 if retType.IsListType() { 1008 return cty.ListValEmpty(ety), nil 1009 } 1010 return cty.SetValEmpty(ety), nil 1011 } 1012 1013 subEtys := ety.TupleElementTypes() 1014 product := make([][]cty.Value, total) 1015 1016 b := make([]cty.Value, total*len(args)) 1017 n := make([]int, len(args)) 1018 s := 0 1019 argVals := make([][]cty.Value, len(args)) 1020 for i, arg := range args { 1021 argVals[i] = arg.AsValueSlice() 1022 } 1023 1024 for i := range product { 1025 e := s + len(args) 1026 pi := b[s:e] 1027 product[i] = pi 1028 s = e 1029 1030 for j, n := range n { 1031 val := argVals[j][n] 1032 ty := subEtys[j] 1033 if !val.Type().Equals(ty) { 1034 var err error 1035 val, err = convert.Convert(val, ty) 1036 if err != nil { 1037 // Should never happen since we checked this in our 1038 // type-checking function. 1039 return cty.NilVal, fmt.Errorf("failed to convert argVals[%d][%d] to %s; this is a bug in Terraform", j, n, ty.FriendlyName()) 1040 } 1041 } 1042 pi[j] = val 1043 } 1044 1045 for j := len(n) - 1; j >= 0; j-- { 1046 n[j]++ 1047 if n[j] < len(argVals[j]) { 1048 break 1049 } 1050 n[j] = 0 1051 } 1052 } 1053 1054 productVals := make([]cty.Value, total) 1055 for i, vals := range product { 1056 productVals[i] = cty.TupleVal(vals) 1057 } 1058 1059 if retType.IsListType() { 1060 return cty.ListVal(productVals), nil 1061 } 1062 return cty.SetVal(productVals), nil 1063 }, 1064 }) 1065 1066 // SliceFunc constructs a function that extracts some consecutive elements 1067 // from within a list. 1068 var SliceFunc = function.New(&function.Spec{ 1069 Params: []function.Parameter{ 1070 { 1071 Name: "list", 1072 Type: cty.DynamicPseudoType, 1073 }, 1074 { 1075 Name: "start_index", 1076 Type: cty.Number, 1077 }, 1078 { 1079 Name: "end_index", 1080 Type: cty.Number, 1081 }, 1082 }, 1083 Type: func(args []cty.Value) (cty.Type, error) { 1084 arg := args[0] 1085 argTy := arg.Type() 1086 1087 if argTy.IsSetType() { 1088 return cty.NilType, function.NewArgErrorf(0, "cannot slice a set, because its elements do not have indices; use the tolist function to force conversion to list if the ordering of the result is not important") 1089 } 1090 if !argTy.IsListType() && !argTy.IsTupleType() { 1091 return cty.NilType, function.NewArgErrorf(0, "must be a list or tuple value") 1092 } 1093 1094 startIndex, endIndex, idxsKnown, err := sliceIndexes(args) 1095 if err != nil { 1096 return cty.NilType, err 1097 } 1098 1099 if argTy.IsListType() { 1100 return argTy, nil 1101 } 1102 1103 if !idxsKnown { 1104 // If we don't know our start/end indices then we can't predict 1105 // the result type if we're planning to return a tuple. 1106 return cty.DynamicPseudoType, nil 1107 } 1108 return cty.Tuple(argTy.TupleElementTypes()[startIndex:endIndex]), nil 1109 }, 1110 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 1111 inputList := args[0] 1112 1113 if retType == cty.DynamicPseudoType { 1114 return cty.DynamicVal, nil 1115 } 1116 1117 // we ignore idxsKnown return value here because the indices are always 1118 // known here, or else the call would've short-circuited. 1119 startIndex, endIndex, _, err := sliceIndexes(args) 1120 if err != nil { 1121 return cty.NilVal, err 1122 } 1123 1124 if endIndex-startIndex == 0 { 1125 if retType.IsTupleType() { 1126 return cty.EmptyTupleVal, nil 1127 } 1128 return cty.ListValEmpty(retType.ElementType()), nil 1129 } 1130 1131 outputList := inputList.AsValueSlice()[startIndex:endIndex] 1132 1133 if retType.IsTupleType() { 1134 return cty.TupleVal(outputList), nil 1135 } 1136 1137 return cty.ListVal(outputList), nil 1138 }, 1139 }) 1140 1141 func sliceIndexes(args []cty.Value) (int, int, bool, error) { 1142 var startIndex, endIndex, length int 1143 var startKnown, endKnown, lengthKnown bool 1144 1145 if args[0].Type().IsTupleType() || args[0].IsKnown() { // if it's a tuple then we always know the length by the type, but lists must be known 1146 length = args[0].LengthInt() 1147 lengthKnown = true 1148 } 1149 1150 if args[1].IsKnown() { 1151 if err := gocty.FromCtyValue(args[1], &startIndex); err != nil { 1152 return 0, 0, false, function.NewArgErrorf(1, "invalid start index: %s", err) 1153 } 1154 if startIndex < 0 { 1155 return 0, 0, false, function.NewArgErrorf(1, "start index must not be less than zero") 1156 } 1157 if lengthKnown && startIndex > length { 1158 return 0, 0, false, function.NewArgErrorf(1, "start index must not be greater than the length of the list") 1159 } 1160 startKnown = true 1161 } 1162 if args[2].IsKnown() { 1163 if err := gocty.FromCtyValue(args[2], &endIndex); err != nil { 1164 return 0, 0, false, function.NewArgErrorf(2, "invalid end index: %s", err) 1165 } 1166 if endIndex < 0 { 1167 return 0, 0, false, function.NewArgErrorf(2, "end index must not be less than zero") 1168 } 1169 if lengthKnown && endIndex > length { 1170 return 0, 0, false, function.NewArgErrorf(2, "end index must not be greater than the length of the list") 1171 } 1172 endKnown = true 1173 } 1174 if startKnown && endKnown { 1175 if startIndex > endIndex { 1176 return 0, 0, false, function.NewArgErrorf(1, "start index must not be greater than end index") 1177 } 1178 } 1179 return startIndex, endIndex, startKnown && endKnown, nil 1180 } 1181 1182 // TransposeFunc contructs a function that takes a map of lists of strings and 1183 // TransposeFunc constructs a function that takes a map of lists of strings and 1184 // swaps the keys and values to produce a new map of lists of strings. 1185 var TransposeFunc = function.New(&function.Spec{ 1186 Params: []function.Parameter{ 1187 { 1188 Name: "values", 1189 Type: cty.Map(cty.List(cty.String)), 1190 }, 1191 }, 1192 Type: function.StaticReturnType(cty.Map(cty.List(cty.String))), 1193 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 1194 inputMap := args[0] 1195 if !inputMap.IsWhollyKnown() { 1196 return cty.UnknownVal(retType), nil 1197 } 1198 1199 outputMap := make(map[string]cty.Value) 1200 tmpMap := make(map[string][]string) 1201 1202 for it := inputMap.ElementIterator(); it.Next(); { 1203 inKey, inVal := it.Element() 1204 for iter := inVal.ElementIterator(); iter.Next(); { 1205 _, val := iter.Element() 1206 if !val.Type().Equals(cty.String) { 1207 return cty.MapValEmpty(cty.List(cty.String)), errors.New("input must be a map of lists of strings") 1208 } 1209 1210 outKey := val.AsString() 1211 if _, ok := tmpMap[outKey]; !ok { 1212 tmpMap[outKey] = make([]string, 0) 1213 } 1214 outVal := tmpMap[outKey] 1215 outVal = append(outVal, inKey.AsString()) 1216 sort.Strings(outVal) 1217 tmpMap[outKey] = outVal 1218 } 1219 } 1220 1221 for outKey, outVal := range tmpMap { 1222 values := make([]cty.Value, 0) 1223 for _, v := range outVal { 1224 values = append(values, cty.StringVal(v)) 1225 } 1226 outputMap[outKey] = cty.ListVal(values) 1227 } 1228 1229 return cty.MapVal(outputMap), nil 1230 }, 1231 }) 1232 1233 // ValuesFunc constructs a function that returns a list of the map values, 1234 // in the order of the sorted keys. 1235 var ValuesFunc = function.New(&function.Spec{ 1236 Params: []function.Parameter{ 1237 { 1238 Name: "values", 1239 Type: cty.DynamicPseudoType, 1240 }, 1241 }, 1242 Type: func(args []cty.Value) (ret cty.Type, err error) { 1243 ty := args[0].Type() 1244 if ty.IsMapType() { 1245 return cty.List(ty.ElementType()), nil 1246 } else if ty.IsObjectType() { 1247 // The result is a tuple type with all of the same types as our 1248 // object type's attributes, sorted in lexicographical order by the 1249 // keys. (This matches the sort order guaranteed by ElementIterator 1250 // on a cty object value.) 1251 atys := ty.AttributeTypes() 1252 if len(atys) == 0 { 1253 return cty.EmptyTuple, nil 1254 } 1255 attrNames := make([]string, 0, len(atys)) 1256 for name := range atys { 1257 attrNames = append(attrNames, name) 1258 } 1259 sort.Strings(attrNames) 1260 1261 tys := make([]cty.Type, len(attrNames)) 1262 for i, name := range attrNames { 1263 tys[i] = atys[name] 1264 } 1265 return cty.Tuple(tys), nil 1266 } 1267 return cty.NilType, errors.New("values() requires a map as the first argument") 1268 }, 1269 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 1270 mapVar := args[0] 1271 1272 // We can just iterate the map/object value here because cty guarantees 1273 // that these types always iterate in key lexicographical order. 1274 var values []cty.Value 1275 for it := mapVar.ElementIterator(); it.Next(); { 1276 _, val := it.Element() 1277 values = append(values, val) 1278 } 1279 1280 if retType.IsTupleType() { 1281 return cty.TupleVal(values), nil 1282 } 1283 if len(values) == 0 { 1284 return cty.ListValEmpty(retType.ElementType()), nil 1285 } 1286 return cty.ListVal(values), nil 1287 }, 1288 }) 1289 1290 // ZipmapFunc constructs a function that constructs a map from a list of keys 1291 // and a corresponding list of values. 1292 var ZipmapFunc = function.New(&function.Spec{ 1293 Params: []function.Parameter{ 1294 { 1295 Name: "keys", 1296 Type: cty.List(cty.String), 1297 }, 1298 { 1299 Name: "values", 1300 Type: cty.DynamicPseudoType, 1301 }, 1302 }, 1303 Type: func(args []cty.Value) (ret cty.Type, err error) { 1304 keys := args[0] 1305 values := args[1] 1306 valuesTy := values.Type() 1307 1308 switch { 1309 case valuesTy.IsListType(): 1310 return cty.Map(values.Type().ElementType()), nil 1311 case valuesTy.IsTupleType(): 1312 if !keys.IsWhollyKnown() { 1313 // Since zipmap with a tuple produces an object, we need to know 1314 // all of the key names before we can predict our result type. 1315 return cty.DynamicPseudoType, nil 1316 } 1317 1318 keysRaw := keys.AsValueSlice() 1319 valueTypesRaw := valuesTy.TupleElementTypes() 1320 if len(keysRaw) != len(valueTypesRaw) { 1321 return cty.NilType, fmt.Errorf("number of keys (%d) does not match number of values (%d)", len(keysRaw), len(valueTypesRaw)) 1322 } 1323 atys := make(map[string]cty.Type, len(valueTypesRaw)) 1324 for i, keyVal := range keysRaw { 1325 if keyVal.IsNull() { 1326 return cty.NilType, fmt.Errorf("keys list has null value at index %d", i) 1327 } 1328 key := keyVal.AsString() 1329 atys[key] = valueTypesRaw[i] 1330 } 1331 return cty.Object(atys), nil 1332 1333 default: 1334 return cty.NilType, errors.New("values argument must be a list or tuple value") 1335 } 1336 }, 1337 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 1338 keys := args[0] 1339 values := args[1] 1340 1341 if !keys.IsWhollyKnown() { 1342 // Unknown map keys and object attributes are not supported, so 1343 // our entire result must be unknown in this case. 1344 return cty.UnknownVal(retType), nil 1345 } 1346 1347 // both keys and values are guaranteed to be shallowly-known here, 1348 // because our declared params above don't allow unknown or null values. 1349 if keys.LengthInt() != values.LengthInt() { 1350 return cty.NilVal, fmt.Errorf("number of keys (%d) does not match number of values (%d)", keys.LengthInt(), values.LengthInt()) 1351 } 1352 1353 output := make(map[string]cty.Value) 1354 1355 i := 0 1356 for it := keys.ElementIterator(); it.Next(); { 1357 _, v := it.Element() 1358 val := values.Index(cty.NumberIntVal(int64(i))) 1359 output[v.AsString()] = val 1360 i++ 1361 } 1362 1363 switch { 1364 case retType.IsMapType(): 1365 if len(output) == 0 { 1366 return cty.MapValEmpty(retType.ElementType()), nil 1367 } 1368 return cty.MapVal(output), nil 1369 case retType.IsObjectType(): 1370 return cty.ObjectVal(output), nil 1371 default: 1372 // Should never happen because the type-check function should've 1373 // caught any other case. 1374 return cty.NilVal, fmt.Errorf("internally selected incorrect result type %s (this is a bug)", retType.FriendlyName()) 1375 } 1376 }, 1377 }) 1378 1379 // helper function to add an element to a list, if it does not already exist 1380 func appendIfMissing(slice []cty.Value, element cty.Value) ([]cty.Value, error) { 1381 for _, ele := range slice { 1382 eq, err := stdlib.Equal(ele, element) 1383 if err != nil { 1384 return slice, err 1385 } 1386 if eq.True() { 1387 return slice, nil 1388 } 1389 } 1390 return append(slice, element), nil 1391 } 1392 1393 // Element returns a single element from a given list at the given index. If 1394 // index is greater than the length of the list then it is wrapped modulo 1395 // the list length. 1396 func Element(list, index cty.Value) (cty.Value, error) { 1397 return ElementFunc.Call([]cty.Value{list, index}) 1398 } 1399 1400 // Length returns the number of elements in the given collection or number of 1401 // Unicode characters in the given string. 1402 func Length(collection cty.Value) (cty.Value, error) { 1403 return LengthFunc.Call([]cty.Value{collection}) 1404 } 1405 1406 // Coalesce takes any number of arguments and returns the first one that isn't empty. 1407 func Coalesce(args ...cty.Value) (cty.Value, error) { 1408 return CoalesceFunc.Call(args) 1409 } 1410 1411 // CoalesceList takes any number of list arguments and returns the first one that isn't empty. 1412 func CoalesceList(args ...cty.Value) (cty.Value, error) { 1413 return CoalesceListFunc.Call(args) 1414 } 1415 1416 // Compact takes a list of strings and returns a new list 1417 // with any empty string elements removed. 1418 func Compact(list cty.Value) (cty.Value, error) { 1419 return CompactFunc.Call([]cty.Value{list}) 1420 } 1421 1422 // Contains determines whether a given list contains a given single value 1423 // as one of its elements. 1424 func Contains(list, value cty.Value) (cty.Value, error) { 1425 return ContainsFunc.Call([]cty.Value{list, value}) 1426 } 1427 1428 // Index finds the element index for a given value in a list. 1429 func Index(list, value cty.Value) (cty.Value, error) { 1430 return IndexFunc.Call([]cty.Value{list, value}) 1431 } 1432 1433 // Distinct takes a list and returns a new list with any duplicate elements removed. 1434 func Distinct(list cty.Value) (cty.Value, error) { 1435 return DistinctFunc.Call([]cty.Value{list}) 1436 } 1437 1438 // Chunklist splits a single list into fixed-size chunks, returning a list of lists. 1439 func Chunklist(list, size cty.Value) (cty.Value, error) { 1440 return ChunklistFunc.Call([]cty.Value{list, size}) 1441 } 1442 1443 // Flatten takes a list and replaces any elements that are lists with a flattened 1444 // sequence of the list contents. 1445 func Flatten(list cty.Value) (cty.Value, error) { 1446 return FlattenFunc.Call([]cty.Value{list}) 1447 } 1448 1449 // Keys takes a map and returns a sorted list of the map keys. 1450 func Keys(inputMap cty.Value) (cty.Value, error) { 1451 return KeysFunc.Call([]cty.Value{inputMap}) 1452 } 1453 1454 // List takes any number of list arguments and returns a list containing those 1455 // values in the same order. 1456 func List(args ...cty.Value) (cty.Value, error) { 1457 return ListFunc.Call(args) 1458 } 1459 1460 // Lookup performs a dynamic lookup into a map. 1461 // There are two required arguments, map and key, plus an optional default, 1462 // which is a value to return if no key is found in map. 1463 func Lookup(args ...cty.Value) (cty.Value, error) { 1464 return LookupFunc.Call(args) 1465 } 1466 1467 // Map takes an even number of arguments and returns a map whose elements are constructed 1468 // from consecutive pairs of arguments. 1469 func Map(args ...cty.Value) (cty.Value, error) { 1470 return MapFunc.Call(args) 1471 } 1472 1473 // Matchkeys constructs a new list by taking a subset of elements from one list 1474 // whose indexes match the corresponding indexes of values in another list. 1475 func Matchkeys(values, keys, searchset cty.Value) (cty.Value, error) { 1476 return MatchkeysFunc.Call([]cty.Value{values, keys, searchset}) 1477 } 1478 1479 // Merge takes an arbitrary number of maps and returns a single map that contains 1480 // a merged set of elements from all of the maps. 1481 // 1482 // If more than one given map defines the same key then the one that is later in 1483 // the argument sequence takes precedence. 1484 func Merge(maps ...cty.Value) (cty.Value, error) { 1485 return MergeFunc.Call(maps) 1486 } 1487 1488 // Reverse takes a sequence and produces a new sequence of the same length 1489 // with all of the same elements as the given sequence but in reverse order. 1490 func Reverse(list cty.Value) (cty.Value, error) { 1491 return ReverseFunc.Call([]cty.Value{list}) 1492 } 1493 1494 // SetProduct computes the cartesian product of sets or sequences. 1495 func SetProduct(sets ...cty.Value) (cty.Value, error) { 1496 return SetProductFunc.Call(sets) 1497 } 1498 1499 // Slice extracts some consecutive elements from within a list. 1500 func Slice(list, start, end cty.Value) (cty.Value, error) { 1501 return SliceFunc.Call([]cty.Value{list, start, end}) 1502 } 1503 1504 // Transpose takes a map of lists of strings and swaps the keys and values to 1505 // produce a new map of lists of strings. 1506 func Transpose(values cty.Value) (cty.Value, error) { 1507 return TransposeFunc.Call([]cty.Value{values}) 1508 } 1509 1510 // Values returns a list of the map values, in the order of the sorted keys. 1511 // This function only works on flat maps. 1512 func Values(values cty.Value) (cty.Value, error) { 1513 return ValuesFunc.Call([]cty.Value{values}) 1514 } 1515 1516 // Zipmap constructs a map from a list of keys and a corresponding list of values. 1517 func Zipmap(keys, values cty.Value) (cty.Value, error) { 1518 return ZipmapFunc.Call([]cty.Value{keys, values}) 1519 }