diff options
Diffstat (limited to 'vendor/github.com/zclconf/go-cty/cty/gocty/in.go')
-rw-r--r-- | vendor/github.com/zclconf/go-cty/cty/gocty/in.go | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/vendor/github.com/zclconf/go-cty/cty/gocty/in.go b/vendor/github.com/zclconf/go-cty/cty/gocty/in.go new file mode 100644 index 0000000..642501b --- /dev/null +++ b/vendor/github.com/zclconf/go-cty/cty/gocty/in.go | |||
@@ -0,0 +1,528 @@ | |||
1 | package gocty | ||
2 | |||
3 | import ( | ||
4 | "math/big" | ||
5 | "reflect" | ||
6 | |||
7 | "github.com/zclconf/go-cty/cty" | ||
8 | "github.com/zclconf/go-cty/cty/set" | ||
9 | ) | ||
10 | |||
11 | // ToCtyValue produces a cty.Value from a Go value. The result will conform | ||
12 | // to the given type, or an error will be returned if this is not possible. | ||
13 | // | ||
14 | // The target type serves as a hint to resolve ambiguities in the mapping. | ||
15 | // For example, the Go type set.Set tells us that the value is a set but | ||
16 | // does not describe the set's element type. This also allows for convenient | ||
17 | // conversions, such as populating a set from a slice rather than having to | ||
18 | // first explicitly instantiate a set.Set. | ||
19 | // | ||
20 | // The audience of this function is assumed to be the developers of Go code | ||
21 | // that is integrating with cty, and thus the error messages it returns are | ||
22 | // presented from Go's perspective. These messages are thus not appropriate | ||
23 | // for display to end-users. An error returned from ToCtyValue represents a | ||
24 | // bug in the calling program, not user error. | ||
25 | func ToCtyValue(val interface{}, ty cty.Type) (cty.Value, error) { | ||
26 | // 'path' starts off as empty but will grow for each level of recursive | ||
27 | // call we make, so by the time toCtyValue returns it is likely to have | ||
28 | // unused capacity on the end of it, depending on how deeply-recursive | ||
29 | // the given Type is. | ||
30 | path := make(cty.Path, 0) | ||
31 | return toCtyValue(reflect.ValueOf(val), ty, path) | ||
32 | } | ||
33 | |||
34 | func toCtyValue(val reflect.Value, ty cty.Type, path cty.Path) (cty.Value, error) { | ||
35 | |||
36 | switch ty { | ||
37 | case cty.Bool: | ||
38 | return toCtyBool(val, path) | ||
39 | case cty.Number: | ||
40 | return toCtyNumber(val, path) | ||
41 | case cty.String: | ||
42 | return toCtyString(val, path) | ||
43 | case cty.DynamicPseudoType: | ||
44 | return toCtyDynamic(val, path) | ||
45 | } | ||
46 | |||
47 | switch { | ||
48 | case ty.IsListType(): | ||
49 | return toCtyList(val, ty.ElementType(), path) | ||
50 | case ty.IsMapType(): | ||
51 | return toCtyMap(val, ty.ElementType(), path) | ||
52 | case ty.IsSetType(): | ||
53 | return toCtySet(val, ty.ElementType(), path) | ||
54 | case ty.IsObjectType(): | ||
55 | return toCtyObject(val, ty.AttributeTypes(), path) | ||
56 | case ty.IsTupleType(): | ||
57 | return toCtyTuple(val, ty.TupleElementTypes(), path) | ||
58 | case ty.IsCapsuleType(): | ||
59 | return toCtyCapsule(val, ty, path) | ||
60 | } | ||
61 | |||
62 | // We should never fall out here | ||
63 | return cty.NilVal, path.NewErrorf("unsupported target type %#v", ty) | ||
64 | } | ||
65 | |||
66 | func toCtyBool(val reflect.Value, path cty.Path) (cty.Value, error) { | ||
67 | if val = toCtyUnwrapPointer(val); !val.IsValid() { | ||
68 | return cty.NullVal(cty.Bool), nil | ||
69 | } | ||
70 | |||
71 | switch val.Kind() { | ||
72 | |||
73 | case reflect.Bool: | ||
74 | return cty.BoolVal(val.Bool()), nil | ||
75 | |||
76 | default: | ||
77 | return cty.NilVal, path.NewErrorf("can't convert Go %s to bool", val.Kind()) | ||
78 | |||
79 | } | ||
80 | |||
81 | } | ||
82 | |||
83 | func toCtyNumber(val reflect.Value, path cty.Path) (cty.Value, error) { | ||
84 | if val = toCtyUnwrapPointer(val); !val.IsValid() { | ||
85 | return cty.NullVal(cty.Number), nil | ||
86 | } | ||
87 | |||
88 | switch val.Kind() { | ||
89 | |||
90 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
91 | return cty.NumberIntVal(val.Int()), nil | ||
92 | |||
93 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | ||
94 | return cty.NumberUIntVal(val.Uint()), nil | ||
95 | |||
96 | case reflect.Float32, reflect.Float64: | ||
97 | return cty.NumberFloatVal(val.Float()), nil | ||
98 | |||
99 | case reflect.Struct: | ||
100 | if val.Type().AssignableTo(bigIntType) { | ||
101 | bigInt := val.Interface().(big.Int) | ||
102 | bigFloat := (&big.Float{}).SetInt(&bigInt) | ||
103 | val = reflect.ValueOf(*bigFloat) | ||
104 | } | ||
105 | |||
106 | if val.Type().AssignableTo(bigFloatType) { | ||
107 | bigFloat := val.Interface().(big.Float) | ||
108 | return cty.NumberVal(&bigFloat), nil | ||
109 | } | ||
110 | |||
111 | fallthrough | ||
112 | default: | ||
113 | return cty.NilVal, path.NewErrorf("can't convert Go %s to number", val.Kind()) | ||
114 | |||
115 | } | ||
116 | |||
117 | } | ||
118 | |||
119 | func toCtyString(val reflect.Value, path cty.Path) (cty.Value, error) { | ||
120 | if val = toCtyUnwrapPointer(val); !val.IsValid() { | ||
121 | return cty.NullVal(cty.String), nil | ||
122 | } | ||
123 | |||
124 | switch val.Kind() { | ||
125 | |||
126 | case reflect.String: | ||
127 | return cty.StringVal(val.String()), nil | ||
128 | |||
129 | default: | ||
130 | return cty.NilVal, path.NewErrorf("can't convert Go %s to string", val.Kind()) | ||
131 | |||
132 | } | ||
133 | |||
134 | } | ||
135 | |||
136 | func toCtyList(val reflect.Value, ety cty.Type, path cty.Path) (cty.Value, error) { | ||
137 | if val = toCtyUnwrapPointer(val); !val.IsValid() { | ||
138 | return cty.NullVal(cty.List(ety)), nil | ||
139 | } | ||
140 | |||
141 | switch val.Kind() { | ||
142 | |||
143 | case reflect.Slice: | ||
144 | if val.IsNil() { | ||
145 | return cty.NullVal(cty.List(ety)), nil | ||
146 | } | ||
147 | fallthrough | ||
148 | case reflect.Array: | ||
149 | if val.Len() == 0 { | ||
150 | return cty.ListValEmpty(ety), nil | ||
151 | } | ||
152 | |||
153 | // While we work on our elements we'll temporarily grow | ||
154 | // path to give us a place to put our index step. | ||
155 | path = append(path, cty.PathStep(nil)) | ||
156 | |||
157 | vals := make([]cty.Value, val.Len()) | ||
158 | for i := range vals { | ||
159 | var err error | ||
160 | path[len(path)-1] = cty.IndexStep{ | ||
161 | Key: cty.NumberIntVal(int64(i)), | ||
162 | } | ||
163 | vals[i], err = toCtyValue(val.Index(i), ety, path) | ||
164 | if err != nil { | ||
165 | return cty.NilVal, err | ||
166 | } | ||
167 | } | ||
168 | |||
169 | // Discard our extra path segment, retaining it as extra capacity | ||
170 | // for future appending to the path. | ||
171 | path = path[:len(path)-1] | ||
172 | |||
173 | return cty.ListVal(vals), nil | ||
174 | |||
175 | default: | ||
176 | return cty.NilVal, path.NewErrorf("can't convert Go %s to %#v", val.Kind(), cty.List(ety)) | ||
177 | |||
178 | } | ||
179 | } | ||
180 | |||
181 | func toCtyMap(val reflect.Value, ety cty.Type, path cty.Path) (cty.Value, error) { | ||
182 | if val = toCtyUnwrapPointer(val); !val.IsValid() { | ||
183 | return cty.NullVal(cty.Map(ety)), nil | ||
184 | } | ||
185 | |||
186 | switch val.Kind() { | ||
187 | |||
188 | case reflect.Map: | ||
189 | if val.IsNil() { | ||
190 | return cty.NullVal(cty.Map(ety)), nil | ||
191 | } | ||
192 | |||
193 | if val.Len() == 0 { | ||
194 | return cty.MapValEmpty(ety), nil | ||
195 | } | ||
196 | |||
197 | keyType := val.Type().Key() | ||
198 | if keyType.Kind() != reflect.String { | ||
199 | return cty.NilVal, path.NewErrorf("can't convert Go map with key type %s; key type must be string", keyType) | ||
200 | } | ||
201 | |||
202 | // While we work on our elements we'll temporarily grow | ||
203 | // path to give us a place to put our index step. | ||
204 | path = append(path, cty.PathStep(nil)) | ||
205 | |||
206 | vals := make(map[string]cty.Value, val.Len()) | ||
207 | for _, kv := range val.MapKeys() { | ||
208 | k := kv.String() | ||
209 | var err error | ||
210 | path[len(path)-1] = cty.IndexStep{ | ||
211 | Key: cty.StringVal(k), | ||
212 | } | ||
213 | vals[k], err = toCtyValue(val.MapIndex(reflect.ValueOf(k)), ety, path) | ||
214 | if err != nil { | ||
215 | return cty.NilVal, err | ||
216 | } | ||
217 | } | ||
218 | |||
219 | // Discard our extra path segment, retaining it as extra capacity | ||
220 | // for future appending to the path. | ||
221 | path = path[:len(path)-1] | ||
222 | |||
223 | return cty.MapVal(vals), nil | ||
224 | |||
225 | default: | ||
226 | return cty.NilVal, path.NewErrorf("can't convert Go %s to %#v", val.Kind(), cty.Map(ety)) | ||
227 | |||
228 | } | ||
229 | } | ||
230 | |||
231 | func toCtySet(val reflect.Value, ety cty.Type, path cty.Path) (cty.Value, error) { | ||
232 | if val = toCtyUnwrapPointer(val); !val.IsValid() { | ||
233 | return cty.NullVal(cty.Set(ety)), nil | ||
234 | } | ||
235 | |||
236 | var vals []cty.Value | ||
237 | |||
238 | switch val.Kind() { | ||
239 | |||
240 | case reflect.Slice: | ||
241 | if val.IsNil() { | ||
242 | return cty.NullVal(cty.Set(ety)), nil | ||
243 | } | ||
244 | fallthrough | ||
245 | case reflect.Array: | ||
246 | if val.Len() == 0 { | ||
247 | return cty.SetValEmpty(ety), nil | ||
248 | } | ||
249 | |||
250 | vals = make([]cty.Value, val.Len()) | ||
251 | for i := range vals { | ||
252 | var err error | ||
253 | vals[i], err = toCtyValue(val.Index(i), ety, path) | ||
254 | if err != nil { | ||
255 | return cty.NilVal, err | ||
256 | } | ||
257 | } | ||
258 | |||
259 | case reflect.Struct: | ||
260 | |||
261 | if !val.Type().AssignableTo(setType) { | ||
262 | return cty.NilVal, path.NewErrorf("can't convert Go %s to %#v", val.Type(), cty.Set(ety)) | ||
263 | } | ||
264 | |||
265 | rawSet := val.Interface().(set.Set) | ||
266 | inVals := rawSet.Values() | ||
267 | |||
268 | if len(inVals) == 0 { | ||
269 | return cty.SetValEmpty(ety), nil | ||
270 | } | ||
271 | |||
272 | vals = make([]cty.Value, len(inVals)) | ||
273 | for i := range inVals { | ||
274 | var err error | ||
275 | vals[i], err = toCtyValue(reflect.ValueOf(inVals[i]), ety, path) | ||
276 | if err != nil { | ||
277 | return cty.NilVal, err | ||
278 | } | ||
279 | } | ||
280 | |||
281 | default: | ||
282 | return cty.NilVal, path.NewErrorf("can't convert Go %s to %#v", val.Kind(), cty.Set(ety)) | ||
283 | |||
284 | } | ||
285 | |||
286 | return cty.SetVal(vals), nil | ||
287 | } | ||
288 | |||
289 | func toCtyObject(val reflect.Value, attrTypes map[string]cty.Type, path cty.Path) (cty.Value, error) { | ||
290 | if val = toCtyUnwrapPointer(val); !val.IsValid() { | ||
291 | return cty.NullVal(cty.Object(attrTypes)), nil | ||
292 | } | ||
293 | |||
294 | switch val.Kind() { | ||
295 | |||
296 | case reflect.Map: | ||
297 | if val.IsNil() { | ||
298 | return cty.NullVal(cty.Object(attrTypes)), nil | ||
299 | } | ||
300 | |||
301 | keyType := val.Type().Key() | ||
302 | if keyType.Kind() != reflect.String { | ||
303 | return cty.NilVal, path.NewErrorf("can't convert Go map with key type %s; key type must be string", keyType) | ||
304 | } | ||
305 | |||
306 | if len(attrTypes) == 0 { | ||
307 | return cty.EmptyObjectVal, nil | ||
308 | } | ||
309 | |||
310 | // While we work on our elements we'll temporarily grow | ||
311 | // path to give us a place to put our GetAttr step. | ||
312 | path = append(path, cty.PathStep(nil)) | ||
313 | |||
314 | haveKeys := make(map[string]struct{}, val.Len()) | ||
315 | for _, kv := range val.MapKeys() { | ||
316 | haveKeys[kv.String()] = struct{}{} | ||
317 | } | ||
318 | |||
319 | vals := make(map[string]cty.Value, len(attrTypes)) | ||
320 | for k, at := range attrTypes { | ||
321 | var err error | ||
322 | path[len(path)-1] = cty.GetAttrStep{ | ||
323 | Name: k, | ||
324 | } | ||
325 | |||
326 | if _, have := haveKeys[k]; !have { | ||
327 | vals[k] = cty.NullVal(at) | ||
328 | continue | ||
329 | } | ||
330 | |||
331 | vals[k], err = toCtyValue(val.MapIndex(reflect.ValueOf(k)), at, path) | ||
332 | if err != nil { | ||
333 | return cty.NilVal, err | ||
334 | } | ||
335 | } | ||
336 | |||
337 | // Discard our extra path segment, retaining it as extra capacity | ||
338 | // for future appending to the path. | ||
339 | path = path[:len(path)-1] | ||
340 | |||
341 | return cty.ObjectVal(vals), nil | ||
342 | |||
343 | case reflect.Struct: | ||
344 | if len(attrTypes) == 0 { | ||
345 | return cty.EmptyObjectVal, nil | ||
346 | } | ||
347 | |||
348 | // While we work on our elements we'll temporarily grow | ||
349 | // path to give us a place to put our GetAttr step. | ||
350 | path = append(path, cty.PathStep(nil)) | ||
351 | |||
352 | attrFields := structTagIndices(val.Type()) | ||
353 | |||
354 | vals := make(map[string]cty.Value, len(attrTypes)) | ||
355 | for k, at := range attrTypes { | ||
356 | path[len(path)-1] = cty.GetAttrStep{ | ||
357 | Name: k, | ||
358 | } | ||
359 | |||
360 | if fieldIdx, have := attrFields[k]; have { | ||
361 | var err error | ||
362 | vals[k], err = toCtyValue(val.Field(fieldIdx), at, path) | ||
363 | if err != nil { | ||
364 | return cty.NilVal, err | ||
365 | } | ||
366 | } else { | ||
367 | vals[k] = cty.NullVal(at) | ||
368 | } | ||
369 | } | ||
370 | |||
371 | // Discard our extra path segment, retaining it as extra capacity | ||
372 | // for future appending to the path. | ||
373 | path = path[:len(path)-1] | ||
374 | |||
375 | return cty.ObjectVal(vals), nil | ||
376 | |||
377 | default: | ||
378 | return cty.NilVal, path.NewErrorf("can't convert Go %s to %#v", val.Kind(), cty.Object(attrTypes)) | ||
379 | |||
380 | } | ||
381 | } | ||
382 | |||
383 | func toCtyTuple(val reflect.Value, elemTypes []cty.Type, path cty.Path) (cty.Value, error) { | ||
384 | if val = toCtyUnwrapPointer(val); !val.IsValid() { | ||
385 | return cty.NullVal(cty.Tuple(elemTypes)), nil | ||
386 | } | ||
387 | |||
388 | switch val.Kind() { | ||
389 | |||
390 | case reflect.Slice: | ||
391 | if val.IsNil() { | ||
392 | return cty.NullVal(cty.Tuple(elemTypes)), nil | ||
393 | } | ||
394 | |||
395 | if val.Len() != len(elemTypes) { | ||
396 | return cty.NilVal, path.NewErrorf("wrong number of elements %d; need %d", val.Len(), len(elemTypes)) | ||
397 | } | ||
398 | |||
399 | if len(elemTypes) == 0 { | ||
400 | return cty.EmptyTupleVal, nil | ||
401 | } | ||
402 | |||
403 | // While we work on our elements we'll temporarily grow | ||
404 | // path to give us a place to put our Index step. | ||
405 | path = append(path, cty.PathStep(nil)) | ||
406 | |||
407 | vals := make([]cty.Value, len(elemTypes)) | ||
408 | for i, ety := range elemTypes { | ||
409 | var err error | ||
410 | |||
411 | path[len(path)-1] = cty.IndexStep{ | ||
412 | Key: cty.NumberIntVal(int64(i)), | ||
413 | } | ||
414 | |||
415 | vals[i], err = toCtyValue(val.Index(i), ety, path) | ||
416 | if err != nil { | ||
417 | return cty.NilVal, err | ||
418 | } | ||
419 | } | ||
420 | |||
421 | // Discard our extra path segment, retaining it as extra capacity | ||
422 | // for future appending to the path. | ||
423 | path = path[:len(path)-1] | ||
424 | |||
425 | return cty.TupleVal(vals), nil | ||
426 | |||
427 | case reflect.Struct: | ||
428 | fieldCount := val.Type().NumField() | ||
429 | if fieldCount != len(elemTypes) { | ||
430 | return cty.NilVal, path.NewErrorf("wrong number of struct fields %d; need %d", fieldCount, len(elemTypes)) | ||
431 | } | ||
432 | |||
433 | if len(elemTypes) == 0 { | ||
434 | return cty.EmptyTupleVal, nil | ||
435 | } | ||
436 | |||
437 | // While we work on our elements we'll temporarily grow | ||
438 | // path to give us a place to put our Index step. | ||
439 | path = append(path, cty.PathStep(nil)) | ||
440 | |||
441 | vals := make([]cty.Value, len(elemTypes)) | ||
442 | for i, ety := range elemTypes { | ||
443 | var err error | ||
444 | |||
445 | path[len(path)-1] = cty.IndexStep{ | ||
446 | Key: cty.NumberIntVal(int64(i)), | ||
447 | } | ||
448 | |||
449 | vals[i], err = toCtyValue(val.Field(i), ety, path) | ||
450 | if err != nil { | ||
451 | return cty.NilVal, err | ||
452 | } | ||
453 | } | ||
454 | |||
455 | // Discard our extra path segment, retaining it as extra capacity | ||
456 | // for future appending to the path. | ||
457 | path = path[:len(path)-1] | ||
458 | |||
459 | return cty.TupleVal(vals), nil | ||
460 | |||
461 | default: | ||
462 | return cty.NilVal, path.NewErrorf("can't convert Go %s to %#v", val.Kind(), cty.Tuple(elemTypes)) | ||
463 | |||
464 | } | ||
465 | } | ||
466 | |||
467 | func toCtyCapsule(val reflect.Value, capsuleType cty.Type, path cty.Path) (cty.Value, error) { | ||
468 | if val = toCtyUnwrapPointer(val); !val.IsValid() { | ||
469 | return cty.NullVal(capsuleType), nil | ||
470 | } | ||
471 | |||
472 | if val.Kind() != reflect.Ptr { | ||
473 | if !val.CanAddr() { | ||
474 | return cty.NilVal, path.NewErrorf("source value for capsule %#v must be addressable", capsuleType) | ||
475 | } | ||
476 | |||
477 | val = val.Addr() | ||
478 | } | ||
479 | |||
480 | if !val.Type().Elem().AssignableTo(capsuleType.EncapsulatedType()) { | ||
481 | return cty.NilVal, path.NewErrorf("value of type %T not compatible with capsule %#v", val.Interface(), capsuleType) | ||
482 | } | ||
483 | |||
484 | return cty.CapsuleVal(capsuleType, val.Interface()), nil | ||
485 | } | ||
486 | |||
487 | func toCtyDynamic(val reflect.Value, path cty.Path) (cty.Value, error) { | ||
488 | if val = toCtyUnwrapPointer(val); !val.IsValid() { | ||
489 | return cty.NullVal(cty.DynamicPseudoType), nil | ||
490 | } | ||
491 | |||
492 | switch val.Kind() { | ||
493 | |||
494 | case reflect.Struct: | ||
495 | if !val.Type().AssignableTo(valueType) { | ||
496 | return cty.NilVal, path.NewErrorf("can't convert Go %s dynamically; only cty.Value allowed", val.Type()) | ||
497 | } | ||
498 | |||
499 | return val.Interface().(cty.Value), nil | ||
500 | |||
501 | default: | ||
502 | return cty.NilVal, path.NewErrorf("can't convert Go %s dynamically; only cty.Value allowed", val.Kind()) | ||
503 | |||
504 | } | ||
505 | |||
506 | } | ||
507 | |||
508 | // toCtyUnwrapPointer is a helper for dealing with Go pointers. It has three | ||
509 | // possible outcomes: | ||
510 | // | ||
511 | // - Given value isn't a pointer, so it's just returned as-is. | ||
512 | // - Given value is a non-nil pointer, in which case it is dereferenced | ||
513 | // and the result returned. | ||
514 | // - Given value is a nil pointer, in which case an invalid value is returned. | ||
515 | // | ||
516 | // For nested pointer types, like **int, they are all dereferenced in turn | ||
517 | // until a non-pointer value is found, or until a nil pointer is encountered. | ||
518 | func toCtyUnwrapPointer(val reflect.Value) reflect.Value { | ||
519 | for val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface { | ||
520 | if val.IsNil() { | ||
521 | return reflect.Value{} | ||
522 | } | ||
523 | |||
524 | val = val.Elem() | ||
525 | } | ||
526 | |||
527 | return val | ||
528 | } | ||