-
Notifications
You must be signed in to change notification settings - Fork 91
/
list.go
217 lines (197 loc) · 6.35 KB
/
list.go
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
package types
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/internal/reflect"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)
var (
_ attr.Type = ListType{}
_ attr.Value = &List{}
)
// ListType is an AttributeType representing a list of values. All values must
// be of the same type, which the provider must specify as the ElemType
// property.
type ListType struct {
ElemType attr.Type
}
// ElementType returns the attr.Type elements will be created from.
func (l ListType) ElementType() attr.Type {
return l.ElemType
}
// WithElementType returns a ListType that is identical to `l`, but with the
// element type set to `typ`.
func (l ListType) WithElementType(typ attr.Type) attr.TypeWithElementType {
return ListType{ElemType: typ}
}
// TerraformType returns the tftypes.Type that should be used to
// represent this type. This constrains what user input will be
// accepted and what kind of data can be set in state. The framework
// will use this to translate the AttributeType to something Terraform
// can understand.
func (l ListType) TerraformType(ctx context.Context) tftypes.Type {
return tftypes.List{
ElementType: l.ElemType.TerraformType(ctx),
}
}
// ValueFromTerraform returns an AttributeValue given a tftypes.Value.
// This is meant to convert the tftypes.Value into a more convenient Go
// type for the provider to consume the data with.
func (l ListType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
list := List{
ElemType: l.ElemType,
}
if in.Type() == nil {
list.Null = true
return list, nil
}
if !in.Type().Equal(l.TerraformType(ctx)) {
return nil, fmt.Errorf("can't use %s as value of List with ElementType %T, can only use %s values", in.String(), l.ElemType, l.ElemType.TerraformType(ctx).String())
}
if !in.IsKnown() {
list.Unknown = true
return list, nil
}
if in.IsNull() {
list.Null = true
return list, nil
}
val := []tftypes.Value{}
err := in.As(&val)
if err != nil {
return nil, err
}
elems := make([]attr.Value, 0, len(val))
for _, elem := range val {
av, err := l.ElemType.ValueFromTerraform(ctx, elem)
if err != nil {
return nil, err
}
elems = append(elems, av)
}
list.Elems = elems
return list, nil
}
// Equal returns true if `o` is also a ListType and has the same ElemType.
func (l ListType) Equal(o attr.Type) bool {
if l.ElemType == nil {
return false
}
other, ok := o.(ListType)
if !ok {
return false
}
return l.ElemType.Equal(other.ElemType)
}
// ApplyTerraform5AttributePathStep applies the given AttributePathStep to the
// list.
func (l ListType) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) {
if _, ok := step.(tftypes.ElementKeyInt); !ok {
return nil, fmt.Errorf("cannot apply step %T to ListType", step)
}
return l.ElemType, nil
}
// String returns a human-friendly description of the ListType.
func (l ListType) String() string {
return "types.ListType[" + l.ElemType.String() + "]"
}
// List represents a list of AttributeValues, all of the same type, indicated
// by ElemType.
type List struct {
// Unknown will be set to true if the entire list is an unknown value.
// If only some of the elements in the list are unknown, their known or
// unknown status will be represented however that AttributeValue
// surfaces that information. The List's Unknown property only tracks
// if the number of elements in a List is known, not whether the
// elements that are in the list are known.
Unknown bool
// Null will be set to true if the list is null, either because it was
// omitted from the configuration, state, or plan, or because it was
// explicitly set to null.
Null bool
// Elems are the elements in the list.
Elems []attr.Value
// ElemType is the tftypes.Type of the elements in the list. All
// elements in the list must be of this type.
ElemType attr.Type
}
// ElementsAs populates `target` with the elements of the List, throwing an
// error if the elements cannot be stored in `target`.
func (l List) ElementsAs(ctx context.Context, target interface{}, allowUnhandled bool) diag.Diagnostics {
// we need a tftypes.Value for this List to be able to use it with our
// reflection code
values, err := l.ToTerraformValue(ctx)
if err != nil {
return diag.Diagnostics{
diag.NewErrorDiagnostic(
"List Element Conversion Error",
"An unexpected error was encountered trying to convert list elements. This is always an error in the provider. Please report the following to the provider developer:\n\n"+err.Error(),
),
}
}
return reflect.Into(ctx, ListType{ElemType: l.ElemType}, tftypes.NewValue(tftypes.List{
ElementType: l.ElemType.TerraformType(ctx),
}, values), target, reflect.Options{
UnhandledNullAsEmpty: allowUnhandled,
UnhandledUnknownAsEmpty: allowUnhandled,
})
}
// Type returns a ListType with the same element type as `l`.
func (l List) Type(ctx context.Context) attr.Type {
return ListType{ElemType: l.ElemType}
}
// ToTerraformValue returns the data contained in the AttributeValue as
// a Go type that tftypes.NewValue will accept.
func (l List) ToTerraformValue(ctx context.Context) (interface{}, error) {
if l.Unknown {
return tftypes.UnknownValue, nil
}
if l.Null {
return nil, nil
}
vals := make([]tftypes.Value, 0, len(l.Elems))
for _, elem := range l.Elems {
val, err := elem.ToTerraformValue(ctx)
if err != nil {
return nil, err
}
err = tftypes.ValidateValue(l.ElemType.TerraformType(ctx), val)
if err != nil {
return nil, fmt.Errorf("error validating terraform type: %w", err)
}
vals = append(vals, tftypes.NewValue(l.ElemType.TerraformType(ctx), val))
}
return vals, nil
}
// Equal must return true if the AttributeValue is considered
// semantically equal to the AttributeValue passed as an argument.
func (l List) Equal(o attr.Value) bool {
other, ok := o.(List)
if !ok {
return false
}
if l.Unknown != other.Unknown {
return false
}
if l.Null != other.Null {
return false
}
if l.ElemType == nil && other.ElemType != nil {
return false
}
if l.ElemType != nil && !l.ElemType.Equal(other.ElemType) {
return false
}
if len(l.Elems) != len(other.Elems) {
return false
}
for pos, lElem := range l.Elems {
oElem := other.Elems[pos]
if !lElem.Equal(oElem) {
return false
}
}
return true
}