1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
package ctydebug
import (
"fmt"
"sort"
"strings"
"github.com/zclconf/go-cty/cty"
)
// ValueString returns a string representation of a given value that is
// reminiscent of Go syntax calling into the cty package but is mainly
// intended for easy human inspection of values in tests, debug output, etc.
//
// The resulting string will include newlines and indentation in order to
// increase the readability of complex structures. It always ends with a
// newline, so you can print this result directly to your output.
func ValueString(v cty.Value) string {
var b strings.Builder
writeValue(v, &b, 0)
b.WriteByte('\n') // always end with a newline for easier printing
return b.String()
}
func writeValue(v cty.Value, b *strings.Builder, indent int) {
if v == cty.NilVal {
b.WriteString("cty.NilVal")
return
}
if v.IsMarked() {
v, marks := v.Unmark()
writeValue(v, b, indent)
if len(marks) == 1 {
var onlyMark interface{}
for k := range marks {
onlyMark = k
}
fmt.Fprintf(b, ".Mark(%#v)", onlyMark)
} else {
fmt.Fprintf(b, ".WithMarks(%#v)", marks)
}
return
}
ty := v.Type()
switch {
case v.IsNull():
b.WriteString("cty.NullVal(")
writeType(ty, b, indent)
b.WriteString(")")
case !v.IsKnown():
b.WriteString("cty.UnknownVal(")
writeType(ty, b, indent)
b.WriteString(")")
case ty.IsObjectType() || ty.IsMapType():
attrs := v.AsValueMap()
if len(attrs) == 0 {
switch {
case ty.IsObjectType():
b.WriteString("cty.EmptyObjectVal")
case ty.IsMapType():
b.WriteString("cty.MapValEmpty(")
writeType(ty.ElementType(), b, indent)
b.WriteString(")")
default:
b.WriteString(v.GoString())
}
return
}
attrNames := make([]string, 0, len(attrs))
for name := range attrs {
attrNames = append(attrNames, name)
}
sort.Strings(attrNames)
switch {
case ty.IsObjectType():
b.WriteString("cty.ObjectVal(map[string]cty.Value{\n")
case ty.IsMapType():
b.WriteString("cty.MapVal(map[string]cty.Value{\n")
default:
b.WriteString(v.GoString())
return
}
indent++
for _, name := range attrNames {
av := attrs[name]
b.WriteString(indentSpaces(indent))
fmt.Fprintf(b, "%q: ", name)
writeValue(av, b, indent)
b.WriteString(",\n")
}
indent--
b.WriteString(indentSpaces(indent))
b.WriteString("})")
case ty.IsTupleType() || ty.IsListType() || ty.IsSetType():
elems := v.AsValueSlice()
if len(elems) == 0 {
switch {
case ty.IsTupleType():
b.WriteString("cty.EmptyTupleVal")
case ty.IsListType():
b.WriteString("cty.ListValEmpty(")
writeType(ty.ElementType(), b, indent)
b.WriteString(")")
case ty.IsSetType():
b.WriteString("cty.SetValEmpty(")
writeType(ty.ElementType(), b, indent)
b.WriteString(")")
default:
b.WriteString(v.GoString())
}
return
}
switch {
case ty.IsTupleType():
b.WriteString("cty.TupleVal([]cty.Value{\n")
case ty.IsListType():
b.WriteString("cty.ListVal([]cty.Value{\n")
case ty.IsSetType():
b.WriteString("cty.SetVal([]cty.Value{\n")
default:
b.WriteString(v.GoString())
return
}
indent++
for _, ev := range elems {
b.WriteString(indentSpaces(indent))
writeValue(ev, b, indent)
b.WriteString(",\n")
}
indent--
b.WriteString(indentSpaces(indent))
b.WriteString("})")
default:
// For any other type we'll just use its GoString and assume it'll
// follow the usual GoString conventions.
b.WriteString(v.GoString())
}
}
|