@@ -14,6 +14,19 @@ type ObjectType struct {
14
14
AttributeTypes map [string ]attr.Type
15
15
}
16
16
17
+ // WithAttributesTypes returns a new copy of the type with its
18
+ // attribute types set.
19
+ func (o ObjectType ) WithAttributesTypes (typs map [string ]attr.Type ) attr.AttributesType {
20
+ return ObjectType {
21
+ AttributeTypes : typs ,
22
+ }
23
+ }
24
+
25
+ // AttributesTypes returns the type's attribute types.
26
+ func (o ObjectType ) AttributesTypes () map [string ]attr.Type {
27
+ return o .AttributeTypes
28
+ }
29
+
17
30
// TerraformType returns the tftypes.Type that should be used to
18
31
// represent this type. This constrains what user input will be
19
32
// accepted and what kind of data can be set in state. The framework
@@ -33,11 +46,37 @@ func (o ObjectType) TerraformType(ctx context.Context) tftypes.Type {
33
46
// This is meant to convert the tftypes.Value into a more convenient Go
34
47
// type for the provider to consume the data with.
35
48
func (o ObjectType ) ValueFromTerraform (ctx context.Context , in tftypes.Value ) (attr.Value , error ) {
36
- object := & Object {
49
+ object := Object {
37
50
AttributeTypes : o .AttributeTypes ,
38
51
}
39
- err := object .SetTerraformValue (ctx , in )
40
- return object , err
52
+ if ! in .Type ().Is (o .TerraformType (ctx )) {
53
+ return nil , fmt .Errorf ("expected %s, got %s" , o .TerraformType (ctx ), in .Type ())
54
+ }
55
+ if ! in .IsKnown () {
56
+ object .Unknown = true
57
+ return object , nil
58
+ }
59
+ if in .IsNull () {
60
+ object .Null = true
61
+ return object , nil
62
+ }
63
+ attributes := map [string ]attr.Value {}
64
+
65
+ val := map [string ]tftypes.Value {}
66
+ err := in .As (& val )
67
+ if err != nil {
68
+ return nil , err
69
+ }
70
+
71
+ for k , v := range val {
72
+ a , err := object .AttributeTypes [k ].ValueFromTerraform (ctx , v )
73
+ if err != nil {
74
+ return nil , err
75
+ }
76
+ attributes [k ] = a
77
+ }
78
+ object .Attributes = attributes
79
+ return object , nil
41
80
}
42
81
43
82
// Equal returns true if `candidate` is also an ObjectType and has the same
@@ -82,47 +121,53 @@ type Object struct {
82
121
AttributeTypes map [string ]attr.Type
83
122
}
84
123
124
+ // ObjectAsOptions is a collection of toggles to control the behavior of
125
+ // Object.As.
126
+ type ObjectAsOptions struct {
127
+ // UnhandledNullAsEmpty controls what happens when As needs to put a
128
+ // null value in a type that has no way to preserve that distinction.
129
+ // When set to true, the type's empty value will be used. When set to
130
+ // false, an error will be returned.
131
+ UnhandledNullAsEmpty bool
132
+
133
+ // UnhandledUnknownAsEmpty controls what happens when As needs to put
134
+ // an unknown value in a type that has no way to preserve that
135
+ // distinction. When set to true, the type's empty value will be used.
136
+ // When set to false, an error will be returned.
137
+ UnhandledUnknownAsEmpty bool
138
+ }
139
+
85
140
// As populates `target` with the data in the Object, throwing an error if the
86
141
// data cannot be stored in `target`.
87
- func (o * Object ) As (ctx context.Context , target interface {}, allowUnhandled bool ) error {
142
+ func (o Object ) As (ctx context.Context , target interface {}, opts ObjectAsOptions ) error {
88
143
// we need a tftypes.Value for this Object to be able to use it with
89
144
// our reflection code
90
- values := map [string ]tftypes.Value {}
91
- types := map [string ]tftypes.Type {}
92
- for key , attr := range o .Attributes {
93
- val , err := attr .ToTerraformValue (ctx )
94
- if err != nil {
95
- return fmt .Errorf ("error getting Terraform value for attribute %q: %w" , key , err )
96
- }
97
- typ , ok := o .AttributeTypes [key ]
98
- if ! ok {
99
- return fmt .Errorf ("no AttributeType defined for attribute %q: %w" , key , err )
100
- }
101
- types [key ] = typ .TerraformType (ctx )
102
- err = tftypes .ValidateValue (typ .TerraformType (ctx ), val )
103
- if err != nil {
104
- return fmt .Errorf ("error using created Terraform value for element %q: %w" , key , err )
105
- }
106
- values [key ] = tftypes .NewValue (typ .TerraformType (ctx ), val )
145
+ obj := ObjectType {AttributeTypes : o .AttributeTypes }
146
+ typ := obj .TerraformType (ctx )
147
+ val , err := o .ToTerraformValue (ctx )
148
+ if err != nil {
149
+ return err
150
+ }
151
+ err = tftypes .ValidateValue (typ , val )
152
+ if err != nil {
153
+ return err
107
154
}
108
- return reflect .Into (ctx , tftypes .NewValue (tftypes.Object {
109
- AttributeTypes : types ,
110
- }, values ), target , reflect.Options {
111
- UnhandledNullAsEmpty : allowUnhandled ,
112
- UnhandledUnknownAsEmpty : allowUnhandled ,
155
+ return reflect .Into (ctx , obj , tftypes .NewValue (typ , val ), target , reflect.Options {
156
+ UnhandledNullAsEmpty : opts .UnhandledNullAsEmpty ,
157
+ UnhandledUnknownAsEmpty : opts .UnhandledUnknownAsEmpty ,
113
158
})
114
159
}
115
160
116
161
// ToTerraformValue returns the data contained in the AttributeValue as
117
162
// a Go type that tftypes.NewValue will accept.
118
- func (o * Object ) ToTerraformValue (ctx context.Context ) (interface {}, error ) {
163
+ func (o Object ) ToTerraformValue (ctx context.Context ) (interface {}, error ) {
119
164
if o .Unknown {
120
165
return tftypes .UnknownValue , nil
121
166
}
122
167
if o .Null {
123
168
return nil , nil
124
169
}
125
- var vals map [string ]tftypes.Value
170
+ vals := map [string ]tftypes.Value {}
126
171
127
172
for k , v := range o .Attributes {
128
173
val , err := v .ToTerraformValue (ctx )
@@ -140,8 +185,8 @@ func (o *Object) ToTerraformValue(ctx context.Context) (interface{}, error) {
140
185
141
186
// Equal must return true if the AttributeValue is considered
142
187
// semantically equal to the AttributeValue passed as an argument.
143
- func (o * Object ) Equal (c attr.Value ) bool {
144
- other , ok := c .(* Object )
188
+ func (o Object ) Equal (c attr.Value ) bool {
189
+ other , ok := c .(Object )
145
190
if ! ok {
146
191
return false
147
192
}
@@ -178,38 +223,3 @@ func (o *Object) Equal(c attr.Value) bool {
178
223
179
224
return true
180
225
}
181
-
182
- // SetTerraformValue updates `o` to reflect the data stored in `in`.
183
- func (o * Object ) SetTerraformValue (ctx context.Context , in tftypes.Value ) error {
184
- o .Unknown = false
185
- o .Null = false
186
- o .Attributes = nil
187
- if ! in .IsKnown () {
188
- o .Unknown = true
189
- return nil
190
- }
191
- if in .IsNull () {
192
- o .Null = true
193
- return nil
194
- }
195
- attributes := map [string ]attr.Value {}
196
-
197
- val := map [string ]tftypes.Value {}
198
- err := in .As (& val )
199
- if err != nil {
200
- return err
201
- }
202
-
203
- for k , v := range val {
204
- a , err := o .AttributeTypes [k ].ValueFromTerraform (ctx , v )
205
- if err != nil {
206
- return err
207
- }
208
- attributes [k ] = a
209
- }
210
- o .Attributes = attributes
211
- // we can't set AttributeTypes, we have no way of knowing them
212
- // callers should set them before or after calling SetTerraformValue
213
- // this is largely because reflection has no way of knowing about them
214
- return nil
215
- }
0 commit comments