aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/zclconf/go-cty/cty/gocty/type_implied.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/zclconf/go-cty/cty/gocty/type_implied.go')
-rw-r--r--vendor/github.com/zclconf/go-cty/cty/gocty/type_implied.go108
1 files changed, 108 insertions, 0 deletions
diff --git a/vendor/github.com/zclconf/go-cty/cty/gocty/type_implied.go b/vendor/github.com/zclconf/go-cty/cty/gocty/type_implied.go
new file mode 100644
index 0000000..ce4c8f1
--- /dev/null
+++ b/vendor/github.com/zclconf/go-cty/cty/gocty/type_implied.go
@@ -0,0 +1,108 @@
1package gocty
2
3import (
4 "reflect"
5
6 "github.com/zclconf/go-cty/cty"
7)
8
9// ImpliedType takes an arbitrary Go value (as an interface{}) and attempts
10// to find a suitable cty.Type instance that could be used for a conversion
11// with ToCtyValue.
12//
13// This allows -- for simple situations at least -- types to be defined just
14// once in Go and the cty types derived from the Go types, but in the process
15// it makes some assumptions that may be undesirable so applications are
16// encouraged to build their cty types directly if exacting control is
17// required.
18//
19// Not all Go types can be represented as cty types, so an error may be
20// returned which is usually considered to be a bug in the calling program.
21// In particular, ImpliedType will never use capsule types in its returned
22// type, because it cannot know the capsule types supported by the calling
23// program.
24func ImpliedType(gv interface{}) (cty.Type, error) {
25 rt := reflect.TypeOf(gv)
26 var path cty.Path
27 return impliedType(rt, path)
28}
29
30func impliedType(rt reflect.Type, path cty.Path) (cty.Type, error) {
31 switch rt.Kind() {
32
33 case reflect.Ptr:
34 return impliedType(rt.Elem(), path)
35
36 // Primitive types
37 case reflect.Bool:
38 return cty.Bool, nil
39 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
40 return cty.Number, nil
41 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
42 return cty.Number, nil
43 case reflect.Float32, reflect.Float64:
44 return cty.Number, nil
45 case reflect.String:
46 return cty.String, nil
47
48 // Collection types
49 case reflect.Slice:
50 path := append(path, cty.IndexStep{Key: cty.UnknownVal(cty.Number)})
51 ety, err := impliedType(rt.Elem(), path)
52 if err != nil {
53 return cty.NilType, err
54 }
55 return cty.List(ety), nil
56 case reflect.Map:
57 if !stringType.AssignableTo(rt.Key()) {
58 return cty.NilType, path.NewErrorf("no cty.Type for %s (must have string keys)", rt)
59 }
60 path := append(path, cty.IndexStep{Key: cty.UnknownVal(cty.String)})
61 ety, err := impliedType(rt.Elem(), path)
62 if err != nil {
63 return cty.NilType, err
64 }
65 return cty.Map(ety), nil
66
67 // Structural types
68 case reflect.Struct:
69 return impliedStructType(rt, path)
70
71 default:
72 return cty.NilType, path.NewErrorf("no cty.Type for %s", rt)
73 }
74}
75
76func impliedStructType(rt reflect.Type, path cty.Path) (cty.Type, error) {
77 if valueType.AssignableTo(rt) {
78 // Special case: cty.Value represents cty.DynamicPseudoType, for
79 // type conformance checking.
80 return cty.DynamicPseudoType, nil
81 }
82
83 fieldIdxs := structTagIndices(rt)
84 if len(fieldIdxs) == 0 {
85 return cty.NilType, path.NewErrorf("no cty.Type for %s (no cty field tags)", rt)
86 }
87
88 atys := make(map[string]cty.Type, len(fieldIdxs))
89
90 {
91 // Temporary extension of path for attributes
92 path := append(path, nil)
93
94 for k, fi := range fieldIdxs {
95 path[len(path)-1] = cty.GetAttrStep{Name: k}
96
97 ft := rt.Field(fi).Type
98 aty, err := impliedType(ft, path)
99 if err != nil {
100 return cty.NilType, err
101 }
102
103 atys[k] = aty
104 }
105 }
106
107 return cty.Object(atys), nil
108}