]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / helper / schema / field_reader_map.go
1 package schema
2
3 import (
4 "fmt"
5 "strings"
6 )
7
8 // MapFieldReader reads fields out of an untyped map[string]string to
9 // the best of its ability.
10 type MapFieldReader struct {
11 Map MapReader
12 Schema map[string]*Schema
13 }
14
15 func (r *MapFieldReader) ReadField(address []string) (FieldReadResult, error) {
16 k := strings.Join(address, ".")
17 schemaList := addrToSchema(address, r.Schema)
18 if len(schemaList) == 0 {
19 return FieldReadResult{}, nil
20 }
21
22 schema := schemaList[len(schemaList)-1]
23 switch schema.Type {
24 case TypeBool, TypeInt, TypeFloat, TypeString:
25 return r.readPrimitive(address, schema)
26 case TypeList:
27 return readListField(r, address, schema)
28 case TypeMap:
29 return r.readMap(k, schema)
30 case TypeSet:
31 return r.readSet(address, schema)
32 case typeObject:
33 return readObjectField(r, address, schema.Elem.(map[string]*Schema))
34 default:
35 panic(fmt.Sprintf("Unknown type: %s", schema.Type))
36 }
37 }
38
39 func (r *MapFieldReader) readMap(k string, schema *Schema) (FieldReadResult, error) {
40 result := make(map[string]interface{})
41 resultSet := false
42
43 // If the name of the map field is directly in the map with an
44 // empty string, it means that the map is being deleted, so mark
45 // that is is set.
46 if v, ok := r.Map.Access(k); ok && v == "" {
47 resultSet = true
48 }
49
50 prefix := k + "."
51 r.Map.Range(func(k, v string) bool {
52 if strings.HasPrefix(k, prefix) {
53 resultSet = true
54
55 key := k[len(prefix):]
56 if key != "%" && key != "#" {
57 result[key] = v
58 }
59 }
60
61 return true
62 })
63
64 err := mapValuesToPrimitive(k, result, schema)
65 if err != nil {
66 return FieldReadResult{}, nil
67 }
68
69 var resultVal interface{}
70 if resultSet {
71 resultVal = result
72 }
73
74 return FieldReadResult{
75 Value: resultVal,
76 Exists: resultSet,
77 }, nil
78 }
79
80 func (r *MapFieldReader) readPrimitive(
81 address []string, schema *Schema) (FieldReadResult, error) {
82 k := strings.Join(address, ".")
83 result, ok := r.Map.Access(k)
84 if !ok {
85 return FieldReadResult{}, nil
86 }
87
88 returnVal, err := stringToPrimitive(result, false, schema)
89 if err != nil {
90 return FieldReadResult{}, err
91 }
92
93 return FieldReadResult{
94 Value: returnVal,
95 Exists: true,
96 }, nil
97 }
98
99 func (r *MapFieldReader) readSet(
100 address []string, schema *Schema) (FieldReadResult, error) {
101 // copy address to ensure we don't modify the argument
102 address = append([]string(nil), address...)
103
104 // Get the number of elements in the list
105 countRaw, err := r.readPrimitive(
106 append(address, "#"), &Schema{Type: TypeInt})
107 if err != nil {
108 return FieldReadResult{}, err
109 }
110 if !countRaw.Exists {
111 // No count, means we have no list
112 countRaw.Value = 0
113 }
114
115 // Create the set that will be our result
116 set := schema.ZeroValue().(*Set)
117
118 // If we have an empty list, then return an empty list
119 if countRaw.Computed || countRaw.Value.(int) == 0 {
120 return FieldReadResult{
121 Value: set,
122 Exists: countRaw.Exists,
123 Computed: countRaw.Computed,
124 }, nil
125 }
126
127 // Go through the map and find all the set items
128 prefix := strings.Join(address, ".") + "."
129 countExpected := countRaw.Value.(int)
130 countActual := make(map[string]struct{})
131 completed := r.Map.Range(func(k, _ string) bool {
132 if !strings.HasPrefix(k, prefix) {
133 return true
134 }
135 if strings.HasPrefix(k, prefix+"#") {
136 // Ignore the count field
137 return true
138 }
139
140 // Split the key, since it might be a sub-object like "idx.field"
141 parts := strings.Split(k[len(prefix):], ".")
142 idx := parts[0]
143
144 var raw FieldReadResult
145 raw, err = r.ReadField(append(address, idx))
146 if err != nil {
147 return false
148 }
149 if !raw.Exists {
150 // This shouldn't happen because we just verified it does exist
151 panic("missing field in set: " + k + "." + idx)
152 }
153
154 set.Add(raw.Value)
155
156 // Due to the way multimap readers work, if we've seen the number
157 // of fields we expect, then exit so that we don't read later values.
158 // For example: the "set" map might have "ports.#", "ports.0", and
159 // "ports.1", but the "state" map might have those plus "ports.2".
160 // We don't want "ports.2"
161 countActual[idx] = struct{}{}
162 if len(countActual) >= countExpected {
163 return false
164 }
165
166 return true
167 })
168 if !completed && err != nil {
169 return FieldReadResult{}, err
170 }
171
172 return FieldReadResult{
173 Value: set,
174 Exists: true,
175 }, nil
176 }
177
178 // MapReader is an interface that is given to MapFieldReader for accessing
179 // a "map". This can be used to have alternate implementations. For a basic
180 // map[string]string, use BasicMapReader.
181 type MapReader interface {
182 Access(string) (string, bool)
183 Range(func(string, string) bool) bool
184 }
185
186 // BasicMapReader implements MapReader for a single map.
187 type BasicMapReader map[string]string
188
189 func (r BasicMapReader) Access(k string) (string, bool) {
190 v, ok := r[k]
191 return v, ok
192 }
193
194 func (r BasicMapReader) Range(f func(string, string) bool) bool {
195 for k, v := range r {
196 if cont := f(k, v); !cont {
197 return false
198 }
199 }
200
201 return true
202 }
203
204 // MultiMapReader reads over multiple maps, preferring keys that are
205 // founder earlier (lower number index) vs. later (higher number index)
206 type MultiMapReader []map[string]string
207
208 func (r MultiMapReader) Access(k string) (string, bool) {
209 for _, m := range r {
210 if v, ok := m[k]; ok {
211 return v, ok
212 }
213 }
214
215 return "", false
216 }
217
218 func (r MultiMapReader) Range(f func(string, string) bool) bool {
219 done := make(map[string]struct{})
220 for _, m := range r {
221 for k, v := range m {
222 if _, ok := done[k]; ok {
223 continue
224 }
225
226 if cont := f(k, v); !cont {
227 return false
228 }
229
230 done[k] = struct{}{}
231 }
232 }
233
234 return true
235 }