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