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