Skip to content

Commit 2050100

Browse files
author
Paddy Carver
committed
Text objects, fix slices, simplify primitives.
Add some more tests for object to struct conversion. Remove a log line. Oops. Simplify primitives by passing a concrete type to tftypes.Value.As and then setting a reflect.Value from the result. This avoids shenanigans around pointers on reflect.Value types. Make elements of a slice always settable by adding the reflect.Value to the slice, then retrieving it by index, before recursing into it with the `into` function. This means even when we add a new reflect.Value to a slice, we can still set its value. Add TODOs for the object tests we should probably write and the fact that we've just ignored maps entirely so far.
1 parent 4438137 commit 2050100

File tree

6 files changed

+130
-13
lines changed

6 files changed

+130
-13
lines changed

internal/reflect/object.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package reflect
33
import (
44
"context"
55
"fmt"
6-
"log"
76
"reflect"
87
"strings"
98

@@ -61,7 +60,6 @@ func reflectObjectIntoStruct(ctx context.Context, object tftypes.Value, target r
6160
structValue := trueReflectValue(target)
6261
for field, structFieldPos := range targetFields {
6362
structField := structValue.Field(structFieldPos)
64-
log.Println("reflecting", objectFields[field], "into", structField.Type())
6563
err := into(ctx, objectFields[field], structField, opts, path.WithAttributeName(field))
6664
if err != nil {
6765
return err

internal/reflect/object_test.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,112 @@ func TestReflectObjectIntoStruct_primitives(t *testing.T) {
120120
if err != nil {
121121
t.Errorf("Unexpected error: %s", err)
122122
}
123+
if s.A != "hello" {
124+
t.Errorf("Expected s.A to be %q, was %q", "hello", s.A)
125+
}
126+
if s.B.Cmp(big.NewFloat(123)) != 0 {
127+
t.Errorf("Expected s.B to be %v, was %v", big.NewFloat(123), s.B)
128+
}
129+
if s.C != true {
130+
t.Errorf("Expected s.C to be %v, was %v", true, s.C)
131+
}
123132
}
124133

125134
func TestReflectObjectIntoStruct_complex(t *testing.T) {
126135
t.Parallel()
136+
137+
var s struct {
138+
Slice []string `tfsdk:"slice"`
139+
SliceOfStructs []struct {
140+
A string `tfsdk:"a"`
141+
B int `tfsdk:"b"`
142+
} `tfsdk:"slice_of_structs"`
143+
Struct struct {
144+
A bool `tfsdk:"a"`
145+
Slice []float64 `tfsdk:"slice"`
146+
} `tfsdk:"struct"`
147+
// TODO: add map
148+
// TODO: add tfsdk.AttributeValue
149+
// TODO: add setUnknownAble
150+
// TODO: add setNullable
151+
// TODO: add tftypes.ValueConverter
152+
}
153+
err := reflectObjectIntoStruct(context.Background(), tftypes.NewValue(tftypes.Object{
154+
AttributeTypes: map[string]tftypes.Type{
155+
"slice": tftypes.List{
156+
ElementType: tftypes.String,
157+
},
158+
"slice_of_structs": tftypes.List{
159+
ElementType: tftypes.Object{
160+
AttributeTypes: map[string]tftypes.Type{
161+
"a": tftypes.String,
162+
"b": tftypes.Number,
163+
},
164+
},
165+
},
166+
"struct": tftypes.Object{
167+
AttributeTypes: map[string]tftypes.Type{
168+
"a": tftypes.Bool,
169+
"slice": tftypes.List{
170+
ElementType: tftypes.Number,
171+
},
172+
},
173+
},
174+
},
175+
}, map[string]tftypes.Value{
176+
"slice": tftypes.NewValue(tftypes.List{
177+
ElementType: tftypes.String,
178+
}, []tftypes.Value{
179+
tftypes.NewValue(tftypes.String, "red"),
180+
tftypes.NewValue(tftypes.String, "blue"),
181+
tftypes.NewValue(tftypes.String, "green"),
182+
}),
183+
"slice_of_structs": tftypes.NewValue(tftypes.List{
184+
ElementType: tftypes.Object{
185+
AttributeTypes: map[string]tftypes.Type{
186+
"a": tftypes.String,
187+
"b": tftypes.Number,
188+
},
189+
},
190+
}, []tftypes.Value{
191+
tftypes.NewValue(tftypes.Object{
192+
AttributeTypes: map[string]tftypes.Type{
193+
"a": tftypes.String,
194+
"b": tftypes.Number,
195+
},
196+
}, map[string]tftypes.Value{
197+
"a": tftypes.NewValue(tftypes.String, "hello, world"),
198+
"b": tftypes.NewValue(tftypes.Number, 123),
199+
}),
200+
tftypes.NewValue(tftypes.Object{
201+
AttributeTypes: map[string]tftypes.Type{
202+
"a": tftypes.String,
203+
"b": tftypes.Number,
204+
},
205+
}, map[string]tftypes.Value{
206+
"a": tftypes.NewValue(tftypes.String, "goodnight, moon"),
207+
"b": tftypes.NewValue(tftypes.Number, 456),
208+
}),
209+
}),
210+
"struct": tftypes.NewValue(tftypes.Object{
211+
AttributeTypes: map[string]tftypes.Type{
212+
"a": tftypes.Bool,
213+
"slice": tftypes.List{
214+
ElementType: tftypes.Number,
215+
},
216+
},
217+
}, map[string]tftypes.Value{
218+
"a": tftypes.NewValue(tftypes.Bool, true),
219+
"slice": tftypes.NewValue(tftypes.List{
220+
ElementType: tftypes.Number,
221+
}, []tftypes.Value{
222+
tftypes.NewValue(tftypes.Number, 123),
223+
tftypes.NewValue(tftypes.Number, 456),
224+
tftypes.NewValue(tftypes.Number, 789),
225+
}),
226+
}),
227+
}), reflect.ValueOf(&s), Options{}, tftypes.NewAttributePath())
228+
if err != nil {
229+
t.Errorf("Unexpected error: %s", err)
230+
}
127231
}

internal/reflect/primitive.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,24 @@ import (
99

1010
func reflectPrimitive(ctx context.Context, val tftypes.Value, target reflect.Value, path *tftypes.AttributePath) error {
1111
realValue := trueReflectValue(target)
12-
if !realValue.CanAddr() {
13-
return path.NewErrorf("can't obtain address of %s", target.Type())
12+
if !realValue.CanSet() {
13+
return path.NewErrorf("can't set %s", target.Type())
1414
}
15-
err := val.As(realValue.Addr().Interface())
16-
if err != nil {
17-
return path.NewError(err)
15+
switch realValue.Kind() {
16+
case reflect.Bool:
17+
var b bool
18+
err := val.As(&b)
19+
if err != nil {
20+
return path.NewError(err)
21+
}
22+
realValue.SetBool(b)
23+
case reflect.String:
24+
var s string
25+
err := val.As(&s)
26+
if err != nil {
27+
return path.NewError(err)
28+
}
29+
realValue.SetString(s)
1830
}
1931
return nil
2032
}

internal/reflect/primitive_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func TestReflectPrimitive_string(t *testing.T) {
1717
if err == nil {
1818
t.Error("Expected error, didn't get one")
1919
}
20-
if expected := ": can't obtain address of string"; expected != err.Error() {
20+
if expected := ": can't set string"; expected != err.Error() {
2121
t.Errorf("Expected error to be %q, got %q", expected, err.Error())
2222
}
2323
}

internal/reflect/reflection.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ func into(ctx context.Context, val tftypes.Value, target reflect.Value, opts Opt
116116
return reflectNumber(ctx, val, target, opts, path)
117117
case reflect.Slice:
118118
return reflectSlice(ctx, val, target, opts, path)
119+
case reflect.Map:
120+
// TODO: handle reflect.Map
121+
return path.NewErrorf("reflecting into maps is not implemented yet.")
119122
default:
120123
return path.NewErrorf("don't know how to reflect %s into %s", val.Type(), target.Type())
121124
}

internal/reflect/slice.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,19 @@ func reflectSlice(ctx context.Context, val tftypes.Value, target reflect.Value,
3939
// type for them, and add it to our new slice
4040
for pos, value := range values {
4141
// create a new Go value of the type that can go in the slice
42-
targetValue := reflect.New(elemType)
42+
targetValue := reflect.Zero(elemType)
43+
44+
// add the new target to our slice
45+
sliced = reflect.Append(sliced, targetValue)
4346

4447
// update our path so we can have nice errors
4548
path := path.WithElementKeyInt(int64(pos))
4649

4750
// reflect the value into our new target
48-
err := into(ctx, value, targetValue, opts, path)
51+
err := into(ctx, value, sliced.Index(sliced.Len()-1), opts, path)
4952
if err != nil {
5053
return err
5154
}
52-
53-
// add the new target to our slice
54-
sliced = reflect.Append(sliced, targetValue)
5555
}
5656

5757
// update the target to be our slice

0 commit comments

Comments
 (0)