mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-09-07 01:26:43 +00:00
109 lines
3.0 KiB
Go
109 lines
3.0 KiB
Go
![]() |
package gocty
|
||
|
|
||
|
import (
|
||
|
"reflect"
|
||
|
|
||
|
"github.com/zclconf/go-cty/cty"
|
||
|
)
|
||
|
|
||
|
// ImpliedType takes an arbitrary Go value (as an interface{}) and attempts
|
||
|
// to find a suitable cty.Type instance that could be used for a conversion
|
||
|
// with ToCtyValue.
|
||
|
//
|
||
|
// This allows -- for simple situations at least -- types to be defined just
|
||
|
// once in Go and the cty types derived from the Go types, but in the process
|
||
|
// it makes some assumptions that may be undesirable so applications are
|
||
|
// encouraged to build their cty types directly if exacting control is
|
||
|
// required.
|
||
|
//
|
||
|
// Not all Go types can be represented as cty types, so an error may be
|
||
|
// returned which is usually considered to be a bug in the calling program.
|
||
|
// In particular, ImpliedType will never use capsule types in its returned
|
||
|
// type, because it cannot know the capsule types supported by the calling
|
||
|
// program.
|
||
|
func ImpliedType(gv interface{}) (cty.Type, error) {
|
||
|
rt := reflect.TypeOf(gv)
|
||
|
var path cty.Path
|
||
|
return impliedType(rt, path)
|
||
|
}
|
||
|
|
||
|
func impliedType(rt reflect.Type, path cty.Path) (cty.Type, error) {
|
||
|
switch rt.Kind() {
|
||
|
|
||
|
case reflect.Ptr:
|
||
|
return impliedType(rt.Elem(), path)
|
||
|
|
||
|
// Primitive types
|
||
|
case reflect.Bool:
|
||
|
return cty.Bool, nil
|
||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
|
return cty.Number, nil
|
||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
|
return cty.Number, nil
|
||
|
case reflect.Float32, reflect.Float64:
|
||
|
return cty.Number, nil
|
||
|
case reflect.String:
|
||
|
return cty.String, nil
|
||
|
|
||
|
// Collection types
|
||
|
case reflect.Slice:
|
||
|
path := append(path, cty.IndexStep{Key: cty.UnknownVal(cty.Number)})
|
||
|
ety, err := impliedType(rt.Elem(), path)
|
||
|
if err != nil {
|
||
|
return cty.NilType, err
|
||
|
}
|
||
|
return cty.List(ety), nil
|
||
|
case reflect.Map:
|
||
|
if !stringType.AssignableTo(rt.Key()) {
|
||
|
return cty.NilType, path.NewErrorf("no cty.Type for %s (must have string keys)", rt)
|
||
|
}
|
||
|
path := append(path, cty.IndexStep{Key: cty.UnknownVal(cty.String)})
|
||
|
ety, err := impliedType(rt.Elem(), path)
|
||
|
if err != nil {
|
||
|
return cty.NilType, err
|
||
|
}
|
||
|
return cty.Map(ety), nil
|
||
|
|
||
|
// Structural types
|
||
|
case reflect.Struct:
|
||
|
return impliedStructType(rt, path)
|
||
|
|
||
|
default:
|
||
|
return cty.NilType, path.NewErrorf("no cty.Type for %s", rt)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func impliedStructType(rt reflect.Type, path cty.Path) (cty.Type, error) {
|
||
|
if valueType.AssignableTo(rt) {
|
||
|
// Special case: cty.Value represents cty.DynamicPseudoType, for
|
||
|
// type conformance checking.
|
||
|
return cty.DynamicPseudoType, nil
|
||
|
}
|
||
|
|
||
|
fieldIdxs := structTagIndices(rt)
|
||
|
if len(fieldIdxs) == 0 {
|
||
|
return cty.NilType, path.NewErrorf("no cty.Type for %s (no cty field tags)", rt)
|
||
|
}
|
||
|
|
||
|
atys := make(map[string]cty.Type, len(fieldIdxs))
|
||
|
|
||
|
{
|
||
|
// Temporary extension of path for attributes
|
||
|
path := append(path, nil)
|
||
|
|
||
|
for k, fi := range fieldIdxs {
|
||
|
path[len(path)-1] = cty.GetAttrStep{Name: k}
|
||
|
|
||
|
ft := rt.Field(fi).Type
|
||
|
aty, err := impliedType(ft, path)
|
||
|
if err != nil {
|
||
|
return cty.NilType, err
|
||
|
}
|
||
|
|
||
|
atys[k] = aty
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return cty.Object(atys), nil
|
||
|
}
|