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