cty.go (149 lines of code) (raw):
package golden
import (
"fmt"
"reflect"
"strings"
"github.com/zclconf/go-cty/cty"
)
// ToCtyValue is a function that converts a primary/collection type to cty.Value
func ToCtyValue(input any) cty.Value {
if v, isCtyValue := input.(cty.Value); isCtyValue {
return v
}
val := reflect.ValueOf(input)
switch val.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return cty.NumberIntVal(val.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return cty.NumberUIntVal(val.Uint())
case reflect.Float32, reflect.Float64:
return cty.NumberFloatVal(val.Float())
case reflect.String:
return cty.StringVal(val.String())
case reflect.Bool:
return cty.BoolVal(val.Bool())
case reflect.Slice:
if val.Len() == 0 {
sliceType := reflect.TypeOf(input)
return cty.ListValEmpty(GoTypeToCtyType(sliceType.Elem()))
}
var vals []cty.Value
for i := 0; i < val.Len(); i++ {
vals = append(vals, ToCtyValue(val.Index(i).Interface()))
}
return cty.ListVal(vals)
case reflect.Map:
if val.Len() == 0 {
mapType := reflect.TypeOf(input)
elementType := mapType.Elem()
return cty.MapValEmpty(GoTypeToCtyType(elementType))
}
vals := make(map[string]cty.Value)
iter := val.MapRange()
for iter.Next() {
key := iter.Key().String()
vals[key] = ToCtyValue(iter.Value().Interface())
}
return cty.MapVal(vals)
case reflect.Struct:
vals := make(map[string]cty.Value)
for i := 0; i < val.NumField(); i++ {
fn, _ := fieldName(val.Type().Field(i))
fv := val.Field(i)
vals[fn] = ToCtyValue(fv.Interface())
}
return cty.ObjectVal(vals)
case reflect.Ptr:
if val.IsNil() {
return cty.NilVal
}
return ToCtyValue(val.Elem().Interface())
default:
return cty.NilVal
}
}
func GoTypeToCtyType(goType reflect.Type) cty.Type {
if goType == nil {
return cty.NilType
}
switch goType.Kind() {
case reflect.Bool:
return cty.Bool
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return cty.Number
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return cty.Number
case reflect.Float32, reflect.Float64:
return cty.Number
case reflect.String:
return cty.String
case reflect.Slice:
elemType := GoTypeToCtyType(goType.Elem())
return cty.List(elemType)
case reflect.Map:
valueType := GoTypeToCtyType(goType.Elem())
return cty.Map(valueType)
case reflect.Struct:
return StructToCtyType(goType)
default:
return cty.NilType
}
}
func StructToCtyType(typ reflect.Type) cty.Type {
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
if typ.Kind() != reflect.Struct {
return cty.NilType
}
attrs := make(map[string]cty.Type)
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
fieldName, ok := fieldName(field)
if !ok {
continue
}
fieldType := field.Type
ctyType := GoTypeToCtyType(fieldType)
attrs[fieldName] = ctyType
}
return cty.Object(attrs)
}
func Int(i int) *int {
return &i
}
func CtyValueToString(val cty.Value) string {
if val.IsNull() && val != cty.NilVal {
return "null"
}
switch val.Type() {
case cty.String:
return val.AsString()
case cty.Number:
bf := val.AsBigFloat()
return bf.Text('f', -1)
case cty.Bool:
return fmt.Sprintf("%t", val.True())
case cty.NilType:
return "nil"
default:
if val.Type().IsListType() || val.Type().IsSetType() || val.Type().IsTupleType() {
strs := make([]string, 0, val.LengthInt())
it := val.ElementIterator()
for it.Next() {
_, v := it.Element()
strs = append(strs, CtyValueToString(v))
}
return "[" + strings.Join(strs, ", ") + "]"
}
if val.Type().IsMapType() || val.Type().IsObjectType() {
strs := make([]string, 0, val.LengthInt())
it := val.ElementIterator()
for it.Next() {
k, v := it.Element()
strs = append(strs, fmt.Sprintf("%s: %s", k.AsString(), CtyValueToString(v)))
}
return "{" + strings.Join(strs, ", ") + "}"
}
// For other types, use the GoString method, which will give a
// string representation of the internal structure of the value.
return val.GoString()
}
}