Skip to content

Commit b559048

Browse files
bring back tftype serialization, add tests
1 parent 1ffc615 commit b559048

File tree

3 files changed

+779
-8
lines changed

3 files changed

+779
-8
lines changed

pkg/valueshim/tftype_json.go

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// Copyright 2016-2025, Pulumi Corporation.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package valueshim
16+
17+
import (
18+
"encoding/json"
19+
"math/big"
20+
21+
"github.com/hashicorp/terraform-plugin-go/tftypes"
22+
)
23+
24+
// Inverse of tftypes.ValueFromJson.
25+
func tftypeValueToJSON(typ tftypes.Type, v tftypes.Value) ([]byte, error) {
26+
raw, err := jsonMarshal(v, typ, tftypes.NewAttributePath())
27+
if err != nil {
28+
return nil, err
29+
}
30+
return json.Marshal(raw)
31+
}
32+
33+
func jsonMarshal(v tftypes.Value, typ tftypes.Type, p *tftypes.AttributePath) (interface{}, error) {
34+
if v.IsNull() {
35+
return nil, nil
36+
}
37+
if !v.IsKnown() {
38+
return nil, p.NewErrorf("unknown values cannot be serialized to JSON")
39+
}
40+
switch {
41+
case typ.Is(tftypes.String):
42+
return jsonMarshalString(v, typ, p)
43+
case typ.Is(tftypes.Number):
44+
return jsonMarshalNumber(v, typ, p)
45+
case typ.Is(tftypes.Bool):
46+
return jsonMarshalBool(v, typ, p)
47+
case typ.Is(tftypes.DynamicPseudoType):
48+
return jsonMarshalDynamicPseudoType(v, typ, p)
49+
case typ.Is(tftypes.List{}):
50+
return jsonMarshalList(v, typ.(tftypes.List).ElementType, p)
51+
case typ.Is(tftypes.Set{}):
52+
return jsonMarshalSet(v, typ.(tftypes.Set).ElementType, p)
53+
case typ.Is(tftypes.Map{}):
54+
return jsonMarshalMap(v, typ.(tftypes.Map).ElementType, p)
55+
case typ.Is(tftypes.Tuple{}):
56+
return jsonMarshalTuple(v, typ.(tftypes.Tuple).ElementTypes, p)
57+
case typ.Is(tftypes.Object{}):
58+
return jsonMarshalObject(v, typ.(tftypes.Object).AttributeTypes, p)
59+
}
60+
61+
return nil, p.NewErrorf("unknown type %s", typ)
62+
}
63+
64+
func jsonMarshalString(v tftypes.Value, typ tftypes.Type, p *tftypes.AttributePath) (interface{}, error) {
65+
var stringValue string
66+
err := v.As(&stringValue)
67+
if err != nil {
68+
return nil, p.NewError(err)
69+
}
70+
return stringValue, nil
71+
}
72+
73+
func jsonMarshalNumber(v tftypes.Value, typ tftypes.Type, p *tftypes.AttributePath) (interface{}, error) {
74+
var n big.Float
75+
err := v.As(&n)
76+
if err != nil {
77+
return nil, p.NewError(err)
78+
}
79+
f64, _ := n.Float64()
80+
return f64, nil
81+
}
82+
83+
func jsonMarshalBool(v tftypes.Value, typ tftypes.Type, p *tftypes.AttributePath) (interface{}, error) {
84+
var b bool
85+
err := v.As(&b)
86+
if err != nil {
87+
return nil, p.NewError(err)
88+
}
89+
return b, nil
90+
}
91+
92+
func jsonMarshalDynamicPseudoType(v tftypes.Value, _ tftypes.Type, p *tftypes.AttributePath) (interface{}, error) {
93+
valType := v.Type()
94+
typeJSON, err := valType.MarshalJSON()
95+
if err != nil {
96+
return nil, p.NewError(err)
97+
}
98+
valJSON, err := jsonMarshal(v, valType, p)
99+
if err != nil {
100+
return nil, p.NewError(err)
101+
}
102+
return map[string]interface{}{
103+
"type": string(typeJSON),
104+
"value": valJSON,
105+
}, nil
106+
}
107+
108+
func jsonMarshalList(v tftypes.Value, elementType tftypes.Type, p *tftypes.AttributePath) (interface{}, error) {
109+
var vs []tftypes.Value
110+
err := v.As(&vs)
111+
if err != nil {
112+
return nil, p.NewError(err)
113+
}
114+
var res []interface{}
115+
for i, v := range vs {
116+
ep := p.WithElementKeyInt(i)
117+
e, err := jsonMarshal(v, elementType, ep)
118+
if err != nil {
119+
return nil, ep.NewError(err)
120+
}
121+
res = append(res, e)
122+
}
123+
return res, nil
124+
}
125+
126+
func jsonMarshalSet(v tftypes.Value, elementType tftypes.Type, p *tftypes.AttributePath) (interface{}, error) {
127+
var vs []tftypes.Value
128+
err := v.As(&vs)
129+
if err != nil {
130+
return nil, p.NewError(err)
131+
}
132+
var res []interface{}
133+
for _, v := range vs {
134+
ep := p.WithElementKeyValue(v)
135+
e, err := jsonMarshal(v, elementType, ep)
136+
if err != nil {
137+
return nil, ep.NewError(err)
138+
}
139+
res = append(res, e)
140+
}
141+
return res, nil
142+
}
143+
144+
func jsonMarshalMap(v tftypes.Value, elementType tftypes.Type, p *tftypes.AttributePath) (interface{}, error) {
145+
var vs map[string]tftypes.Value
146+
err := v.As(&vs)
147+
if err != nil {
148+
return nil, p.NewError(err)
149+
}
150+
res := map[string]interface{}{}
151+
for k, v := range vs {
152+
ep := p.WithElementKeyValue(v)
153+
e, err := jsonMarshal(v, elementType, ep)
154+
if err != nil {
155+
return nil, ep.NewError(err)
156+
}
157+
res[k] = e
158+
}
159+
return res, nil
160+
}
161+
162+
func jsonMarshalTuple(v tftypes.Value, elementTypes []tftypes.Type, p *tftypes.AttributePath) (interface{}, error) {
163+
var vs []tftypes.Value
164+
err := v.As(&vs)
165+
if err != nil {
166+
return nil, p.NewError(err)
167+
}
168+
var res []interface{}
169+
for i, v := range vs {
170+
ep := p.WithElementKeyInt(i)
171+
e, err := jsonMarshal(v, elementTypes[i], ep)
172+
if err != nil {
173+
return nil, ep.NewError(err)
174+
}
175+
res = append(res, e)
176+
}
177+
return res, nil
178+
}
179+
180+
func jsonMarshalObject(
181+
v tftypes.Value,
182+
elementTypes map[string]tftypes.Type,
183+
p *tftypes.AttributePath,
184+
) (interface{}, error) {
185+
var vs map[string]tftypes.Value
186+
err := v.As(&vs)
187+
if err != nil {
188+
return nil, p.NewError(err)
189+
}
190+
res := map[string]interface{}{}
191+
for k, v := range vs {
192+
ep := p.WithAttributeName(k)
193+
e, err := jsonMarshal(v, elementTypes[k], ep)
194+
if err != nil {
195+
return nil, ep.NewError(err)
196+
}
197+
res[k] = e
198+
}
199+
return res, nil
200+
}

0 commit comments

Comments
 (0)