mirror of
https://github.com/turbot/steampipe.git
synced 2026-02-22 14:00:14 -05:00
269 lines
5.7 KiB
Go
269 lines
5.7 KiB
Go
package utils
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
"github.com/zclconf/go-cty/cty/gocty"
|
|
"github.com/zclconf/go-cty/cty/json"
|
|
)
|
|
|
|
// CtyToJSON converts a cty value to it;s JSON representation
|
|
func CtyToJSON(val cty.Value) (string, error) {
|
|
|
|
if !val.IsWhollyKnown() {
|
|
return "", fmt.Errorf("cannot serialize unknown values")
|
|
}
|
|
|
|
if val.IsNull() {
|
|
return "{}", nil
|
|
}
|
|
|
|
buf, err := json.Marshal(val, val.Type())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(buf), nil
|
|
|
|
}
|
|
|
|
// CtyToPostgresString convert a cty value into a postgres representation of the value
|
|
func CtyToPostgresString(v cty.Value) (valStr string, err error) {
|
|
|
|
ty := v.Type()
|
|
switch {
|
|
case ty.IsTupleType(), ty.IsListType():
|
|
{
|
|
|
|
var array []string
|
|
if array, err = ctyTupleToArrayOfPgStrings(v); err == nil {
|
|
valStr = fmt.Sprintf("array[%s]", strings.Join(array, ","))
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
switch ty {
|
|
case cty.Bool:
|
|
var target bool
|
|
if err = gocty.FromCtyValue(v, &target); err == nil {
|
|
valStr = fmt.Sprintf("%v", target)
|
|
}
|
|
case cty.Number:
|
|
var target int
|
|
if err = gocty.FromCtyValue(v, &target); err == nil {
|
|
valStr = fmt.Sprintf("%d", target)
|
|
return
|
|
} else {
|
|
var targetf float64
|
|
if err = gocty.FromCtyValue(v, &targetf); err == nil {
|
|
valStr = fmt.Sprintf("%d", target)
|
|
}
|
|
}
|
|
case cty.String:
|
|
var target string
|
|
if err := gocty.FromCtyValue(v, &target); err == nil {
|
|
valStr = fmt.Sprintf("'%s'", target)
|
|
}
|
|
|
|
default:
|
|
var json string
|
|
// wrap as postgres string
|
|
if json, err = CtyToJSON(v); err == nil {
|
|
valStr = fmt.Sprintf("'%s'::jsonb", json)
|
|
}
|
|
|
|
}
|
|
|
|
return valStr, err
|
|
}
|
|
|
|
// CtyToString convert a cty value into a string representation of the value
|
|
func CtyToString(v cty.Value) (valStr string, err error) {
|
|
if v.IsNull() || !v.IsWhollyKnown() {
|
|
return "", nil
|
|
}
|
|
ty := v.Type()
|
|
switch {
|
|
case ty.IsTupleType(), ty.IsListType():
|
|
{
|
|
|
|
var array []string
|
|
if array, err = ctyTupleToArrayOfPgStrings(v); err == nil {
|
|
valStr = fmt.Sprintf("[%s]", strings.Join(array, ","))
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
switch ty {
|
|
case cty.Bool:
|
|
var target bool
|
|
if err = gocty.FromCtyValue(v, &target); err == nil {
|
|
valStr = fmt.Sprintf("%v", target)
|
|
}
|
|
case cty.Number:
|
|
var target int
|
|
if err = gocty.FromCtyValue(v, &target); err == nil {
|
|
valStr = fmt.Sprintf("%d", target)
|
|
} else {
|
|
var targetf float64
|
|
if err = gocty.FromCtyValue(v, &targetf); err == nil {
|
|
valStr = fmt.Sprintf("%d", target)
|
|
}
|
|
}
|
|
case cty.String:
|
|
var target string
|
|
if err := gocty.FromCtyValue(v, &target); err == nil {
|
|
valStr = target
|
|
}
|
|
default:
|
|
var json string
|
|
// wrap as postgres string
|
|
if json, err = CtyToJSON(v); err == nil {
|
|
valStr = json
|
|
}
|
|
|
|
}
|
|
|
|
return valStr, err
|
|
}
|
|
|
|
func CtyToGo(v cty.Value) (val interface{}, err error) {
|
|
if v.IsNull() {
|
|
return nil, nil
|
|
}
|
|
ty := v.Type()
|
|
switch {
|
|
case ty.IsTupleType(), ty.IsListType():
|
|
{
|
|
var array []string
|
|
if array, err = ctyTupleToArrayOfStrings(v); err == nil {
|
|
val = array
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
switch ty {
|
|
case cty.Bool:
|
|
var target bool
|
|
if err = gocty.FromCtyValue(v, &target); err == nil {
|
|
val = target
|
|
}
|
|
|
|
case cty.Number:
|
|
var target int
|
|
if err = gocty.FromCtyValue(v, &target); err == nil {
|
|
val = target
|
|
} else {
|
|
var targetf float64
|
|
if err = gocty.FromCtyValue(v, &targetf); err == nil {
|
|
val = targetf
|
|
}
|
|
}
|
|
case cty.String:
|
|
var target string
|
|
if err := gocty.FromCtyValue(v, &target); err == nil {
|
|
val = target
|
|
}
|
|
|
|
default:
|
|
var json string
|
|
// wrap as postgres string
|
|
if json, err = CtyToJSON(v); err == nil {
|
|
val = json
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// CtyTypeToHclType converts a cty type to a hcl type
|
|
// accept multiple types and use the first non null and non dynamic one
|
|
func CtyTypeToHclType(types ...cty.Type) string {
|
|
// find which if any of the types are non nil and not dynamic
|
|
t := getKnownType(types)
|
|
if t == cty.NilType {
|
|
return ""
|
|
}
|
|
|
|
friendlyName := t.FriendlyName()
|
|
|
|
// func to convert from ctyt aggregate syntax to hcl
|
|
convertAggregate := func(prefix string) (string, bool) {
|
|
if strings.HasPrefix(friendlyName, prefix) {
|
|
return fmt.Sprintf("%s(%s)", strings.TrimSuffix(prefix, " of "), strings.TrimPrefix(friendlyName, prefix)), true
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
if convertedName, isList := convertAggregate("list of "); isList {
|
|
return convertedName
|
|
}
|
|
if convertedName, isMap := convertAggregate("map of "); isMap {
|
|
return convertedName
|
|
}
|
|
if convertedName, isSet := convertAggregate("set of "); isSet {
|
|
return convertedName
|
|
}
|
|
if friendlyName == "tuple" {
|
|
elementTypes := t.TupleElementTypes()
|
|
if len(elementTypes) == 0 {
|
|
// we cannot determine the eleemnt type
|
|
return "list"
|
|
}
|
|
// if there are element types, use the first one (assume homogeneous)
|
|
underlyingType := elementTypes[0]
|
|
return fmt.Sprintf("list(%s)", CtyTypeToHclType(underlyingType))
|
|
}
|
|
if friendlyName == "dynamic" {
|
|
return ""
|
|
}
|
|
return friendlyName
|
|
}
|
|
|
|
// from a list oif cty typoes, return the first which is non nil and not dynamic
|
|
func getKnownType(types []cty.Type) cty.Type {
|
|
for _, t := range types {
|
|
if t != cty.NilType && !t.HasDynamicTypes() {
|
|
return t
|
|
}
|
|
}
|
|
return cty.NilType
|
|
}
|
|
|
|
func ctyTupleToArrayOfPgStrings(val cty.Value) ([]string, error) {
|
|
var res []string
|
|
it := val.ElementIterator()
|
|
for it.Next() {
|
|
_, v := it.Element()
|
|
// decode the value into a postgres compatible
|
|
valStr, err := CtyToPostgresString(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res = append(res, valStr)
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func ctyTupleToArrayOfStrings(val cty.Value) ([]string, error) {
|
|
var res []string
|
|
it := val.ElementIterator()
|
|
for it.Next() {
|
|
_, v := it.Element()
|
|
|
|
var valStr string
|
|
if err := gocty.FromCtyValue(v, &valStr); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res = append(res, valStr)
|
|
}
|
|
return res, nil
|
|
}
|