Skip to content

Commit c39a786

Browse files
committed
Add helper for converting cty.Objects to string
1 parent bc80743 commit c39a786

File tree

4 files changed

+139
-2
lines changed

4 files changed

+139
-2
lines changed

internal/command/views/hook_ui.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/hashicorp/terraform/internal/plans"
2020
"github.com/hashicorp/terraform/internal/providers"
2121
"github.com/hashicorp/terraform/internal/terraform"
22+
"github.com/hashicorp/terraform/internal/tfdiags"
2223
)
2324

2425
// How long to wait between sending heartbeat/progress messages
@@ -327,7 +328,7 @@ func (h *UiHook) PrePlanImport(id terraform.HookResourceIdentity, importTarget c
327328
if importTarget.Type().IsObjectType() {
328329
h.println(fmt.Sprintf(
329330
h.view.colorize.Color("[reset][bold]%s: Preparing import... [identity=%s]"),
330-
id.Addr, importTarget.GoString(), // TODO improve
331+
id.Addr, tfdiags.ObjectToString(importTarget),
331332
))
332333
} else {
333334
h.println(fmt.Sprintf(

internal/terraform/node_resource_plan_instance.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
681681
var importValue string
682682
if importTarget.Type().IsObjectType() {
683683
importType = "Identity"
684-
importValue = importTarget.GoString() // TODO improve
684+
importValue = tfdiags.ObjectToString(importTarget)
685685
} else {
686686
importValue = importTarget.AsString()
687687
}

internal/tfdiags/object.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: BUSL-1.1
3+
package tfdiags
4+
5+
import (
6+
"fmt"
7+
"strings"
8+
9+
"github.com/zclconf/go-cty/cty"
10+
)
11+
12+
// ObjectToString is a helper function that converts a go-cty object to a string representation
13+
func ObjectToString(obj cty.Value) string {
14+
if obj.IsNull() {
15+
return "<null>"
16+
}
17+
18+
if !obj.IsKnown() {
19+
return "<unknown>"
20+
}
21+
22+
if obj.Type().IsObjectType() && len(obj.Type().AttributeTypes()) == 0 {
23+
return "<empty>"
24+
}
25+
26+
if obj.Type().IsObjectType() {
27+
result := ""
28+
it := obj.ElementIterator()
29+
for it.Next() {
30+
key, val := it.Element()
31+
keyStr := key.AsString()
32+
33+
if result != "" {
34+
result += ","
35+
}
36+
37+
switch val.Type() {
38+
case cty.Bool:
39+
result += fmt.Sprintf("%s=%t", keyStr, val.True())
40+
case cty.Number:
41+
result += fmt.Sprintf("%s=%s", keyStr, val.AsBigFloat().String())
42+
case cty.String:
43+
result += fmt.Sprintf("%s=%s", keyStr, val.AsString())
44+
case cty.List(cty.Bool):
45+
elements := val.AsValueSlice()
46+
parts := make([]string, len(elements))
47+
for i, element := range elements {
48+
parts[i] = fmt.Sprintf("%t", element.True())
49+
}
50+
result += fmt.Sprintf("%s=[%s]", keyStr, strings.Join(parts, ","))
51+
case cty.List(cty.Number):
52+
elements := val.AsValueSlice()
53+
parts := make([]string, len(elements))
54+
for i, element := range elements {
55+
parts[i] = element.AsBigFloat().String()
56+
}
57+
result += fmt.Sprintf("%s=[%s]", keyStr, strings.Join(parts, ","))
58+
case cty.List(cty.String):
59+
elements := val.AsValueSlice()
60+
parts := make([]string, len(elements))
61+
for i, element := range elements {
62+
parts[i] = element.AsString()
63+
}
64+
result += fmt.Sprintf("%s=[%s]", keyStr, strings.Join(parts, ","))
65+
}
66+
}
67+
68+
return result
69+
}
70+
71+
panic("not an object")
72+
}

internal/tfdiags/object_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: BUSL-1.1
3+
package tfdiags
4+
5+
import (
6+
"testing"
7+
8+
"github.com/zclconf/go-cty/cty"
9+
)
10+
11+
func Test_ObjectToString(t *testing.T) {
12+
testCases := []struct {
13+
name string
14+
value cty.Value
15+
expected string
16+
}{
17+
{
18+
name: "null",
19+
value: cty.NullVal(cty.Object(map[string]cty.Type{})),
20+
expected: "<null>",
21+
},
22+
{
23+
name: "unknown",
24+
value: cty.UnknownVal(cty.Object(map[string]cty.Type{})),
25+
expected: "<unknown>",
26+
},
27+
{
28+
name: "empty",
29+
value: cty.EmptyObjectVal,
30+
expected: "<empty>",
31+
},
32+
{
33+
name: "primitive",
34+
value: cty.ObjectVal(map[string]cty.Value{
35+
"number": cty.NumberIntVal(42),
36+
"string": cty.StringVal("hello"),
37+
"bool": cty.BoolVal(true),
38+
}),
39+
expected: "bool=true,number=42,string=hello",
40+
},
41+
{
42+
name: "list",
43+
value: cty.ObjectVal(map[string]cty.Value{
44+
"string": cty.StringVal("hello"),
45+
"list": cty.ListVal([]cty.Value{
46+
cty.StringVal("a"),
47+
cty.StringVal("b"),
48+
cty.StringVal("c"),
49+
}),
50+
}),
51+
expected: "list=[a,b,c],string=hello",
52+
},
53+
}
54+
55+
for _, tc := range testCases {
56+
t.Run(tc.name, func(t *testing.T) {
57+
actual := ObjectToString(tc.value)
58+
59+
if actual != tc.expected {
60+
t.Errorf("expected %q, got %q", tc.expected, actual)
61+
}
62+
})
63+
}
64+
}

0 commit comments

Comments
 (0)