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