-
Notifications
You must be signed in to change notification settings - Fork 32
/
map.go
132 lines (115 loc) · 3.44 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
123
124
125
126
127
128
129
130
131
132
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package tftypes
import (
"bytes"
"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) {
var buf bytes.Buffer
buf.WriteString(`["map",`)
// MarshalJSON is always error safe
elementTypeBytes, _ := m.ElementType.MarshalJSON()
buf.Write(elementTypeBytes)
buf.WriteString(`]`)
return buf.Bytes(), 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{}))
}
}