aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/zclconf/go-cty/cty/gocty/out.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/zclconf/go-cty/cty/gocty/out.go')
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/gocty/out.go705
1 files changed, 705 insertions, 0 deletions
diff --git a/vendor/github.com/zclconf/go-cty/cty/gocty/out.go b/vendor/github.com/zclconf/go-cty/cty/gocty/out.go
new file mode 100644
index 0000000..99b65a7
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/gocty/out.go
@@ -0,0 +1,705 @@
1package gocty
2
3import (
4 "math/big"
5 "reflect"
6
7 "math"
8
9 "github.com/zclconf/go-cty/cty"
10)
11
12// FromCtyValue assigns a cty.Value to a reflect.Value, which must be a pointer,
13// using a fixed set of conversion rules.
14//
15// This function considers its audience to be the creator of the cty Value
16// given, and thus the error messages it generates are (unlike with ToCtyValue)
17// presented in cty terminology that is generally appropriate to return to
18// end-users in applications where cty data structures are built from
19// user-provided configuration. In particular this means that if incorrect
20// target types are provided by the calling application the resulting error
21// messages are likely to be confusing, since we assume that the given target
22// type is correct and the cty.Value is where the error lies.
23//
24// If an error is returned, the target data structure may have been partially
25// populated, but the degree to which this is true is an implementation
26// detail that the calling application should not rely on.
27//
28// The function will panic if given a non-pointer as the Go value target,
29// since that is considered to be a bug in the calling program.
30func FromCtyValue(val cty.Value, target interface{}) error {
31 tVal := reflect.ValueOf(target)
32 if tVal.Kind() != reflect.Ptr {
33 panic("target value is not a pointer")
34 }
35 if tVal.IsNil() {
36 panic("target value is nil pointer")
37 }
38
39 // 'path' starts off as empty but will grow for each level of recursive
40 // call we make, so by the time fromCtyValue returns it is likely to have
41 // unused capacity on the end of it, depending on how deeply-recursive
42 // the given cty.Value is.
43 path := make(cty.Path, 0)
44 return fromCtyValue(val, tVal, path)
45}
46
47func fromCtyValue(val cty.Value, target reflect.Value, path cty.Path) error {
48 ty := val.Type()
49
50 deepTarget := fromCtyPopulatePtr(target, false)
51
52 // If we're decoding into a cty.Value then we just pass through the
53 // value as-is, to enable partial decoding. This is the only situation
54 // where unknown values are permitted.
55 if deepTarget.Kind() == reflect.Struct && deepTarget.Type().AssignableTo(valueType) {
56 deepTarget.Set(reflect.ValueOf(val))
57 return nil
58 }
59
60 // Lists and maps can be nil without indirection, but everything else
61 // requires a pointer and we set it immediately to nil.
62 // We also make an exception for capsule types because we want to handle
63 // pointers specially for these.
64 // (fromCtyList and fromCtyMap must therefore deal with val.IsNull, while
65 // other types can assume no nulls after this point.)
66 if val.IsNull() && !val.Type().IsListType() && !val.Type().IsMapType() && !val.Type().IsCapsuleType() {
67 target = fromCtyPopulatePtr(target, true)
68 if target.Kind() != reflect.Ptr {
69 return path.NewErrorf("null value is not allowed")
70 }
71
72 target.Set(reflect.Zero(target.Type()))
73 return nil
74 }
75
76 target = deepTarget
77
78 if !val.IsKnown() {
79 return path.NewErrorf("value must be known")
80 }
81
82 switch ty {
83 case cty.Bool:
84 return fromCtyBool(val, target, path)
85 case cty.Number:
86 return fromCtyNumber(val, target, path)
87 case cty.String:
88 return fromCtyString(val, target, path)
89 }
90
91 switch {
92 case ty.IsListType():
93 return fromCtyList(val, target, path)
94 case ty.IsMapType():
95 return fromCtyMap(val, target, path)
96 case ty.IsSetType():
97 return fromCtySet(val, target, path)
98 case ty.IsObjectType():
99 return fromCtyObject(val, target, path)
100 case ty.IsTupleType():
101 return fromCtyTuple(val, target, path)
102 case ty.IsCapsuleType():
103 return fromCtyCapsule(val, target, path)
104 }
105
106 // We should never fall out here; reaching here indicates a bug in this
107 // function.
108 return path.NewErrorf("unsupported source type %#v", ty)
109}
110
111func fromCtyBool(val cty.Value, target reflect.Value, path cty.Path) error {
112 switch target.Kind() {
113
114 case reflect.Bool:
115 if val.True() {
116 target.Set(reflect.ValueOf(true))
117 } else {
118 target.Set(reflect.ValueOf(false))
119 }
120 return nil
121
122 default:
123 return likelyRequiredTypesError(path, target)
124
125 }
126}
127
128func fromCtyNumber(val cty.Value, target reflect.Value, path cty.Path) error {
129 bf := val.AsBigFloat()
130
131 switch target.Kind() {
132
133 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
134 return fromCtyNumberInt(bf, target, path)
135
136 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
137 return fromCtyNumberUInt(bf, target, path)
138
139 case reflect.Float32, reflect.Float64:
140 return fromCtyNumberFloat(bf, target, path)
141
142 case reflect.Struct:
143 return fromCtyNumberBig(bf, target, path)
144
145 default:
146 return likelyRequiredTypesError(path, target)
147
148 }
149}
150
151func fromCtyNumberInt(bf *big.Float, target reflect.Value, path cty.Path) error {
152 // Doing this with switch rather than << arithmetic because << with
153 // result >32-bits is not portable to 32-bit systems.
154 var min int64
155 var max int64
156 switch target.Type().Bits() {
157 case 8:
158 min = math.MinInt8
159 max = math.MaxInt8
160 case 16:
161 min = math.MinInt16
162 max = math.MaxInt16
163 case 32:
164 min = math.MinInt32
165 max = math.MaxInt32
166 case 64:
167 min = math.MinInt64
168 max = math.MaxInt64
169 default:
170 panic("weird number of bits in target int")
171 }
172
173 iv, accuracy := bf.Int64()
174 if accuracy != big.Exact || iv < min || iv > max {
175 return path.NewErrorf("value must be a whole number, between %d and %d", min, max)
176 }
177
178 target.Set(reflect.ValueOf(iv).Convert(target.Type()))
179
180 return nil
181}
182
183func fromCtyNumberUInt(bf *big.Float, target reflect.Value, path cty.Path) error {
184 // Doing this with switch rather than << arithmetic because << with
185 // result >32-bits is not portable to 32-bit systems.
186 var max uint64
187 switch target.Type().Bits() {
188 case 8:
189 max = math.MaxUint8
190 case 16:
191 max = math.MaxUint16
192 case 32:
193 max = math.MaxUint32
194 case 64:
195 max = math.MaxUint64
196 default:
197 panic("weird number of bits in target uint")
198 }
199
200 iv, accuracy := bf.Uint64()
201 if accuracy != big.Exact || iv > max {
202 return path.NewErrorf("value must be a whole number, between 0 and %d inclusive", max)
203 }
204
205 target.Set(reflect.ValueOf(iv).Convert(target.Type()))
206
207 return nil
208}
209
210func fromCtyNumberFloat(bf *big.Float, target reflect.Value, path cty.Path) error {
211 switch target.Kind() {
212 case reflect.Float32:
213 fv, accuracy := bf.Float32()
214 if accuracy != big.Exact {
215 // We allow the precision to be truncated as part of our conversion,
216 // but we don't want to silently introduce infinities.
217 if math.IsInf(float64(fv), 0) {
218 return path.NewErrorf("value must be between %f and %f inclusive", -math.MaxFloat32, math.MaxFloat32)
219 }
220 }
221 target.Set(reflect.ValueOf(fv))
222 return nil
223 case reflect.Float64:
224 fv, accuracy := bf.Float64()
225 if accuracy != big.Exact {
226 // We allow the precision to be truncated as part of our conversion,
227 // but we don't want to silently introduce infinities.
228 if math.IsInf(fv, 0) {
229 return path.NewErrorf("value must be between %f and %f inclusive", -math.MaxFloat64, math.MaxFloat64)
230 }
231 }
232 target.Set(reflect.ValueOf(fv))
233 return nil
234 default:
235 panic("unsupported kind of float")
236 }
237}
238
239func fromCtyNumberBig(bf *big.Float, target reflect.Value, path cty.Path) error {
240 switch {
241
242 case bigFloatType.AssignableTo(target.Type()):
243 // Easy!
244 target.Set(reflect.ValueOf(bf).Elem())
245 return nil
246
247 case bigIntType.AssignableTo(target.Type()):
248 bi, accuracy := bf.Int(nil)
249 if accuracy != big.Exact {
250 return path.NewErrorf("value must be a whole number")
251 }
252 target.Set(reflect.ValueOf(bi).Elem())
253 return nil
254
255 default:
256 return likelyRequiredTypesError(path, target)
257 }
258}
259
260func fromCtyString(val cty.Value, target reflect.Value, path cty.Path) error {
261 switch target.Kind() {
262
263 case reflect.String:
264 target.Set(reflect.ValueOf(val.AsString()))
265 return nil
266
267 default:
268 return likelyRequiredTypesError(path, target)
269
270 }
271}
272
273func fromCtyList(val cty.Value, target reflect.Value, path cty.Path) error {
274 switch target.Kind() {
275
276 case reflect.Slice:
277 if val.IsNull() {
278 target.Set(reflect.Zero(target.Type()))
279 return nil
280 }
281
282 length := val.LengthInt()
283 tv := reflect.MakeSlice(target.Type(), length, length)
284
285 path = append(path, nil)
286
287 i := 0
288 var err error
289 val.ForEachElement(func(key cty.Value, val cty.Value) bool {
290 path[len(path)-1] = cty.IndexStep{
291 Key: cty.NumberIntVal(int64(i)),
292 }
293
294 targetElem := tv.Index(i)
295 err = fromCtyValue(val, targetElem, path)
296 if err != nil {
297 return true
298 }
299
300 i++
301 return false
302 })
303 if err != nil {
304 return err
305 }
306
307 path = path[:len(path)-1]
308
309 target.Set(tv)
310 return nil
311
312 case reflect.Array:
313 if val.IsNull() {
314 return path.NewErrorf("null value is not allowed")
315 }
316
317 length := val.LengthInt()
318 if length != target.Len() {
319 return path.NewErrorf("must be a list of length %d", target.Len())
320 }
321
322 path = append(path, nil)
323
324 i := 0
325 var err error
326 val.ForEachElement(func(key cty.Value, val cty.Value) bool {
327 path[len(path)-1] = cty.IndexStep{
328 Key: cty.NumberIntVal(int64(i)),
329 }
330
331 targetElem := target.Index(i)
332 err = fromCtyValue(val, targetElem, path)
333 if err != nil {
334 return true
335 }
336
337 i++
338 return false
339 })
340 if err != nil {
341 return err
342 }
343
344 path = path[:len(path)-1]
345
346 return nil
347
348 default:
349 return likelyRequiredTypesError(path, target)
350
351 }
352}
353
354func fromCtyMap(val cty.Value, target reflect.Value, path cty.Path) error {
355
356 switch target.Kind() {
357
358 case reflect.Map:
359 if val.IsNull() {
360 target.Set(reflect.Zero(target.Type()))
361 return nil
362 }
363
364 tv := reflect.MakeMap(target.Type())
365 et := target.Type().Elem()
366
367 path = append(path, nil)
368
369 var err error
370 val.ForEachElement(func(key cty.Value, val cty.Value) bool {
371 path[len(path)-1] = cty.IndexStep{
372 Key: key,
373 }
374
375 ks := key.AsString()
376
377 targetElem := reflect.New(et)
378 err = fromCtyValue(val, targetElem, path)
379
380 tv.SetMapIndex(reflect.ValueOf(ks), targetElem.Elem())
381
382 return err != nil
383 })
384 if err != nil {
385 return err
386 }
387
388 path = path[:len(path)-1]
389
390 target.Set(tv)
391 return nil
392
393 default:
394 return likelyRequiredTypesError(path, target)
395
396 }
397}
398
399func fromCtySet(val cty.Value, target reflect.Value, path cty.Path) error {
400 switch target.Kind() {
401
402 case reflect.Slice:
403 if val.IsNull() {
404 target.Set(reflect.Zero(target.Type()))
405 return nil
406 }
407
408 length := val.LengthInt()
409 tv := reflect.MakeSlice(target.Type(), length, length)
410
411 i := 0
412 var err error
413 val.ForEachElement(func(key cty.Value, val cty.Value) bool {
414 targetElem := tv.Index(i)
415 err = fromCtyValue(val, targetElem, path)
416 if err != nil {
417 return true
418 }
419
420 i++
421 return false
422 })
423 if err != nil {
424 return err
425 }
426
427 target.Set(tv)
428 return nil
429
430 case reflect.Array:
431 if val.IsNull() {
432 return path.NewErrorf("null value is not allowed")
433 }
434
435 length := val.LengthInt()
436 if length != target.Len() {
437 return path.NewErrorf("must be a set of length %d", target.Len())
438 }
439
440 i := 0
441 var err error
442 val.ForEachElement(func(key cty.Value, val cty.Value) bool {
443 targetElem := target.Index(i)
444 err = fromCtyValue(val, targetElem, path)
445 if err != nil {
446 return true
447 }
448
449 i++
450 return false
451 })
452 if err != nil {
453 return err
454 }
455
456 return nil
457
458 // TODO: decode into set.Set instance
459
460 default:
461 return likelyRequiredTypesError(path, target)
462
463 }
464}
465
466func fromCtyObject(val cty.Value, target reflect.Value, path cty.Path) error {
467
468 switch target.Kind() {
469
470 case reflect.Struct:
471
472 attrTypes := val.Type().AttributeTypes()
473 targetFields := structTagIndices(target.Type())
474
475 path = append(path, nil)
476
477 for k, i := range targetFields {
478 if _, exists := attrTypes[k]; !exists {
479 // If the field in question isn't able to represent nil,
480 // that's an error.
481 fk := target.Field(i).Kind()
482 switch fk {
483 case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Interface:
484 // okay
485 default:
486 return path.NewErrorf("missing required attribute %q", k)
487 }
488 }
489 }
490
491 for k := range attrTypes {
492 path[len(path)-1] = cty.GetAttrStep{
493 Name: k,
494 }
495
496 fieldIdx, exists := targetFields[k]
497 if !exists {
498 return path.NewErrorf("unsupported attribute %q", k)
499 }
500
501 ev := val.GetAttr(k)
502
503 targetField := target.Field(fieldIdx)
504 err := fromCtyValue(ev, targetField, path)
505 if err != nil {
506 return err
507 }
508 }
509
510 path = path[:len(path)-1]
511
512 return nil
513
514 default:
515 return likelyRequiredTypesError(path, target)
516
517 }
518}
519
520func fromCtyTuple(val cty.Value, target reflect.Value, path cty.Path) error {
521
522 switch target.Kind() {
523
524 case reflect.Struct:
525
526 elemTypes := val.Type().TupleElementTypes()
527 fieldCount := target.Type().NumField()
528
529 if fieldCount != len(elemTypes) {
530 return path.NewErrorf("a tuple of %d elements is required", fieldCount)
531 }
532
533 path = append(path, nil)
534
535 for i := range elemTypes {
536 path[len(path)-1] = cty.IndexStep{
537 Key: cty.NumberIntVal(int64(i)),
538 }
539
540 ev := val.Index(cty.NumberIntVal(int64(i)))
541
542 targetField := target.Field(i)
543 err := fromCtyValue(ev, targetField, path)
544 if err != nil {
545 return err
546 }
547 }
548
549 path = path[:len(path)-1]
550
551 return nil
552
553 default:
554 return likelyRequiredTypesError(path, target)
555
556 }
557}
558
559func fromCtyCapsule(val cty.Value, target reflect.Value, path cty.Path) error {
560
561 if target.Kind() == reflect.Ptr {
562 // Walk through indirection until we get to the last pointer,
563 // which we might set to null below.
564 target = fromCtyPopulatePtr(target, true)
565
566 if val.IsNull() {
567 target.Set(reflect.Zero(target.Type()))
568 return nil
569 }
570
571 // Since a capsule contains a pointer to an object, we'll preserve
572 // that pointer on the way out and thus allow the caller to recover
573 // the original object, rather than a copy of it.
574
575 eType := val.Type().EncapsulatedType()
576
577 if !eType.AssignableTo(target.Elem().Type()) {
578 // Our interface contract promises that we won't expose Go
579 // implementation details in error messages, so we need to keep
580 // this vague. This can only arise if a calling application has
581 // more than one capsule type in play and a user mixes them up.
582 return path.NewErrorf("incorrect type %s", val.Type().FriendlyName())
583 }
584
585 target.Set(reflect.ValueOf(val.EncapsulatedValue()))
586
587 return nil
588 } else {
589 if val.IsNull() {
590 return path.NewErrorf("null value is not allowed")
591 }
592
593 // If our target isn't a pointer then we will attempt to copy
594 // the encapsulated value into it.
595
596 eType := val.Type().EncapsulatedType()
597
598 if !eType.AssignableTo(target.Type()) {
599 // Our interface contract promises that we won't expose Go
600 // implementation details in error messages, so we need to keep
601 // this vague. This can only arise if a calling application has
602 // more than one capsule type in play and a user mixes them up.
603 return path.NewErrorf("incorrect type %s", val.Type().FriendlyName())
604 }
605
606 // We know that EncapsulatedValue is always a pointer, so we
607 // can safely call .Elem on its reflect.Value.
608 target.Set(reflect.ValueOf(val.EncapsulatedValue()).Elem())
609
610 return nil
611 }
612
613}
614
615// fromCtyPopulatePtr recognizes when target is a pointer type and allocates
616// a value to assign to that pointer, which it returns.
617//
618// If the given value has multiple levels of indirection, like **int, these
619// will be processed in turn so that the return value is guaranteed to be
620// a non-pointer.
621//
622// As an exception, if decodingNull is true then the returned value will be
623// the final level of pointer, if any, so that the caller can assign it
624// as nil to represent a null value. If the given target value is not a pointer
625// at all then the returned value will be just the given target, so the caller
626// must test if the returned value is a pointer before trying to assign nil
627// to it.
628func fromCtyPopulatePtr(target reflect.Value, decodingNull bool) reflect.Value {
629 for {
630 if target.Kind() == reflect.Interface && !target.IsNil() {
631 e := target.Elem()
632 if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
633 target = e
634 }
635 }
636
637 if target.Kind() != reflect.Ptr {
638 break
639 }
640
641 // Stop early if we're decodingNull and we've found our last indirection
642 if target.Elem().Kind() != reflect.Ptr && decodingNull && target.CanSet() {
643 break
644 }
645
646 if target.IsNil() {
647 target.Set(reflect.New(target.Type().Elem()))
648 }
649
650 target = target.Elem()
651 }
652 return target
653}
654
655// likelyRequiredTypesError returns an error that states which types are
656// acceptable by making some assumptions about what types we support for
657// each target Go kind. It's not a precise science but it allows us to return
658// an error message that is cty-user-oriented rather than Go-oriented.
659//
660// Generally these error messages should be a matter of last resort, since
661// the calling application should be validating user-provided value types
662// before decoding anyway.
663func likelyRequiredTypesError(path cty.Path, target reflect.Value) error {
664 switch target.Kind() {
665
666 case reflect.Bool:
667 return path.NewErrorf("bool value is required")
668
669 case reflect.String:
670 return path.NewErrorf("string value is required")
671
672 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
673 fallthrough
674 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
675 fallthrough
676 case reflect.Float32, reflect.Float64:
677 return path.NewErrorf("number value is required")
678
679 case reflect.Slice, reflect.Array:
680 return path.NewErrorf("list or set value is required")
681
682 case reflect.Map:
683 return path.NewErrorf("map or object value is required")
684
685 case reflect.Struct:
686 switch {
687
688 case target.Type().AssignableTo(bigFloatType) || target.Type().AssignableTo(bigIntType):
689 return path.NewErrorf("number value is required")
690
691 case target.Type().AssignableTo(setType):
692 return path.NewErrorf("set or list value is required")
693
694 default:
695 return path.NewErrorf("object or tuple value is required")
696
697 }
698
699 default:
700 // We should avoid getting into this path, since this error
701 // message is rather useless.
702 return path.NewErrorf("incorrect type")
703
704 }
705}