1package stdlib 2 3import ( 4 "fmt" 5 6 "github.com/zclconf/go-cty/cty" 7 "github.com/zclconf/go-cty/cty/convert" 8 "github.com/zclconf/go-cty/cty/function" 9) 10 11var ConcatFunc = function.New(&function.Spec{ 12 Params: []function.Parameter{}, 13 VarParam: &function.Parameter{ 14 Name: "seqs", 15 Type: cty.DynamicPseudoType, 16 }, 17 Type: func(args []cty.Value) (ret cty.Type, err error) { 18 if len(args) == 0 { 19 return cty.NilType, fmt.Errorf("at least one argument is required") 20 } 21 22 if args[0].Type().IsListType() { 23 // Possibly we're going to return a list, if all of our other 24 // args are also lists and we can find a common element type. 25 tys := make([]cty.Type, len(args)) 26 for i, val := range args { 27 ty := val.Type() 28 if !ty.IsListType() { 29 tys = nil 30 break 31 } 32 tys[i] = ty 33 } 34 35 if tys != nil { 36 commonType, _ := convert.UnifyUnsafe(tys) 37 if commonType != cty.NilType { 38 return commonType, nil 39 } 40 } 41 } 42 43 etys := make([]cty.Type, 0, len(args)) 44 for i, val := range args { 45 ety := val.Type() 46 switch { 47 case ety.IsTupleType(): 48 etys = append(etys, ety.TupleElementTypes()...) 49 case ety.IsListType(): 50 if !val.IsKnown() { 51 // We need to know the list to count its elements to 52 // build our tuple type, so any concat of an unknown 53 // list can't be typed yet. 54 return cty.DynamicPseudoType, nil 55 } 56 57 l := val.LengthInt() 58 subEty := ety.ElementType() 59 for j := 0; j < l; j++ { 60 etys = append(etys, subEty) 61 } 62 default: 63 return cty.NilType, function.NewArgErrorf( 64 i, "all arguments must be lists or tuples; got %s", 65 ety.FriendlyName(), 66 ) 67 } 68 } 69 return cty.Tuple(etys), nil 70 }, 71 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 72 switch { 73 case retType.IsListType(): 74 // If retType is a list type then we know that all of the 75 // given values will be lists and that they will either be of 76 // retType or of something we can convert to retType. 77 vals := make([]cty.Value, 0, len(args)) 78 for i, list := range args { 79 list, err = convert.Convert(list, retType) 80 if err != nil { 81 // Conversion might fail because we used UnifyUnsafe 82 // to choose our return type. 83 return cty.NilVal, function.NewArgError(i, err) 84 } 85 86 it := list.ElementIterator() 87 for it.Next() { 88 _, v := it.Element() 89 vals = append(vals, v) 90 } 91 } 92 if len(vals) == 0 { 93 return cty.ListValEmpty(retType.ElementType()), nil 94 } 95 96 return cty.ListVal(vals), nil 97 case retType.IsTupleType(): 98 // If retType is a tuple type then we could have a mixture of 99 // lists and tuples but we know they all have known values 100 // (because our params don't AllowUnknown) and we know that 101 // concatenating them all together will produce a tuple of 102 // retType because of the work we did in the Type function above. 103 vals := make([]cty.Value, 0, len(args)) 104 105 for _, seq := range args { 106 // Both lists and tuples support ElementIterator, so this is easy. 107 it := seq.ElementIterator() 108 for it.Next() { 109 _, v := it.Element() 110 vals = append(vals, v) 111 } 112 } 113 114 return cty.TupleVal(vals), nil 115 default: 116 // should never happen if Type is working correctly above 117 panic("unsupported return type") 118 } 119 }, 120}) 121 122var RangeFunc = function.New(&function.Spec{ 123 VarParam: &function.Parameter{ 124 Name: "params", 125 Type: cty.Number, 126 }, 127 Type: function.StaticReturnType(cty.List(cty.Number)), 128 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 129 var start, end, step cty.Value 130 switch len(args) { 131 case 1: 132 if args[0].LessThan(cty.Zero).True() { 133 start, end, step = cty.Zero, args[0], cty.NumberIntVal(-1) 134 } else { 135 start, end, step = cty.Zero, args[0], cty.NumberIntVal(1) 136 } 137 case 2: 138 if args[1].LessThan(args[0]).True() { 139 start, end, step = args[0], args[1], cty.NumberIntVal(-1) 140 } else { 141 start, end, step = args[0], args[1], cty.NumberIntVal(1) 142 } 143 case 3: 144 start, end, step = args[0], args[1], args[2] 145 default: 146 return cty.NilVal, fmt.Errorf("must have one, two, or three arguments") 147 } 148 149 var vals []cty.Value 150 151 if step == cty.Zero { 152 return cty.NilVal, function.NewArgErrorf(2, "step must not be zero") 153 } 154 down := step.LessThan(cty.Zero).True() 155 156 if down { 157 if end.GreaterThan(start).True() { 158 return cty.NilVal, function.NewArgErrorf(1, "end must be less than start when step is negative") 159 } 160 } else { 161 if end.LessThan(start).True() { 162 return cty.NilVal, function.NewArgErrorf(1, "end must be greater than start when step is positive") 163 } 164 } 165 166 num := start 167 for { 168 if down { 169 if num.LessThanOrEqualTo(end).True() { 170 break 171 } 172 } else { 173 if num.GreaterThanOrEqualTo(end).True() { 174 break 175 } 176 } 177 if len(vals) >= 1024 { 178 // Artificial limit to prevent bad arguments from consuming huge amounts of memory 179 return cty.NilVal, fmt.Errorf("more than 1024 values were generated; either decrease the difference between start and end or use a smaller step") 180 } 181 vals = append(vals, num) 182 num = num.Add(step) 183 } 184 if len(vals) == 0 { 185 return cty.ListValEmpty(cty.Number), nil 186 } 187 return cty.ListVal(vals), nil 188 }, 189}) 190 191// Concat takes one or more sequences (lists or tuples) and returns the single 192// sequence that results from concatenating them together in order. 193// 194// If all of the given sequences are lists of the same element type then the 195// result is a list of that type. Otherwise, the result is a of a tuple type 196// constructed from the given sequence types. 197func Concat(seqs ...cty.Value) (cty.Value, error) { 198 return ConcatFunc.Call(seqs) 199} 200 201// Range creates a list of numbers by starting from the given starting value, 202// then adding the given step value until the result is greater than or 203// equal to the given stopping value. Each intermediate result becomes an 204// element in the resulting list. 205// 206// When all three parameters are set, the order is (start, end, step). If 207// only two parameters are set, they are the start and end respectively and 208// step defaults to 1. If only one argument is set, it gives the end value 209// with start defaulting to 0 and step defaulting to 1. 210// 211// Because the resulting list must be fully buffered in memory, there is an 212// artificial cap of 1024 elements, after which this function will return 213// an error to avoid consuming unbounded amounts of memory. The Range function 214// is primarily intended for creating small lists of indices to iterate over, 215// so there should be no reason to generate huge lists with it. 216func Range(params ...cty.Value) (cty.Value, error) { 217 return RangeFunc.Call(params) 218} 219