-
Notifications
You must be signed in to change notification settings - Fork 32
/
map.go
122 lines (110 loc) · 3.38 KB
/
map.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
package tftypes
import (
"fmt"
"sort"
)
// Map is a Terraform type representing an unordered collection of elements,
// all of the same type, each identifiable with a unique string key.
type Map struct {
ElementType Type
// used to make this type uncomparable
// see https://golang.org/ref/spec#Comparison_operators
// this enforces the use of Is, instead
_ []struct{}
}
// ApplyTerraform5AttributePathStep applies an AttributePathStep to a Map,
// returning the Type found at that AttributePath within the Map. If the
// AttributePathStep cannot be applied to the Map, an ErrInvalidStep error
// will be returned.
func (m Map) ApplyTerraform5AttributePathStep(step AttributePathStep) (interface{}, error) {
switch step.(type) {
case ElementKeyString:
return m.ElementType, nil
default:
return nil, ErrInvalidStep
}
}
// Equal returns true if the two Maps are exactly equal. Unlike Is, passing in
// a Map with no ElementType will always return false.
func (m Map) Equal(o Type) bool {
v, ok := o.(Map)
if !ok {
return false
}
if v.ElementType == nil || m.ElementType == nil {
// when doing exact comparisons, we can't compare types that
// don't have element types set, so we just consider them not
// equal
return false
}
return m.ElementType.Equal(v.ElementType)
}
// UsableAs returns whether the two Maps are type compatible.
//
// If the other type is DynamicPseudoType, it will return true.
// If the other type is not a Map, it will return false.
// If the other Map does not have a type compatible ElementType, it will
// return false.
func (m Map) UsableAs(o Type) bool {
if o.Is(DynamicPseudoType) {
return true
}
v, ok := o.(Map)
if !ok {
return false
}
return m.ElementType.UsableAs(v.ElementType)
}
// Is returns whether `t` is a Map type or not. It does not perform any
// ElementType checks.
func (m Map) Is(t Type) bool {
_, ok := t.(Map)
return ok
}
func (m Map) String() string {
return "tftypes.Map[" + m.ElementType.String() + "]"
}
func (m Map) private() {}
func (m Map) supportedGoTypes() []string {
return []string{"map[string]tftypes.Value"}
}
// MarshalJSON returns a JSON representation of the full type signature of `m`,
// including its ElementType.
//
// Deprecated: this is not meant to be called by third-party code.
func (m Map) MarshalJSON() ([]byte, error) {
attributeType, err := m.ElementType.MarshalJSON()
if err != nil {
return nil, fmt.Errorf("error marshaling tftypes.Map's attribute type %T to JSON: %w", m.ElementType, err)
}
return []byte(`["map",` + string(attributeType) + `]`), nil
}
func valueFromMap(typ Type, in interface{}) (Value, error) {
switch value := in.(type) {
case map[string]Value:
keys := make([]string, 0, len(value))
for k := range value {
keys = append(keys, k)
}
sort.Strings(keys)
var elType Type
for _, k := range keys {
v := value[k]
if !v.Type().UsableAs(typ) {
return Value{}, NewAttributePath().WithElementKeyString(k).NewErrorf("can't use %s as %s", v.Type(), typ)
}
if elType == nil {
elType = v.Type()
}
if !elType.Equal(v.Type()) {
return Value{}, fmt.Errorf("maps must only contain one type of element, saw %s and %s", elType, v.Type())
}
}
return Value{
typ: Map{ElementType: typ},
value: value,
}, nil
default:
return Value{}, fmt.Errorf("tftypes.NewValue can't use %T as a tftypes.Map; expected types are: %s", in, formattedSupportedGoTypes(Map{}))
}
}