aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/zclconf/go-cty/cty/gocty/in.go
diff options
context:
space:
mode:
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.go528
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 @@
1package gocty
2
3import (
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.
25func 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
34func 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
66func 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
83func 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
119func 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
136func 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
181func 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
231func 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
289func 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
383func 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
467func 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
487func 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.
518func 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}