]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / helper / schema / field_writer_map.go
CommitLineData
bae9f6d2
JC
1package schema
2
3import (
4 "fmt"
5 "reflect"
6 "strconv"
7 "strings"
8 "sync"
9
10 "github.com/mitchellh/mapstructure"
11)
12
13// MapFieldWriter writes data into a single map[string]string structure.
14type MapFieldWriter struct {
15 Schema map[string]*Schema
16
17 lock sync.Mutex
18 result map[string]string
19}
20
21// Map returns the underlying map that is being written to.
22func (w *MapFieldWriter) Map() map[string]string {
23 w.lock.Lock()
24 defer w.lock.Unlock()
25 if w.result == nil {
26 w.result = make(map[string]string)
27 }
28
29 return w.result
30}
31
32func (w *MapFieldWriter) unsafeWriteField(addr string, value string) {
33 w.lock.Lock()
34 defer w.lock.Unlock()
35 if w.result == nil {
36 w.result = make(map[string]string)
37 }
38
39 w.result[addr] = value
40}
41
15c0b25d
AP
42// clearTree clears a field and any sub-fields of the given address out of the
43// map. This should be used to reset some kind of complex structures (namely
44// sets) before writing to make sure that any conflicting data is removed (for
45// example, if the set was previously written to the writer's layer).
46func (w *MapFieldWriter) clearTree(addr []string) {
47 prefix := strings.Join(addr, ".") + "."
48 for k := range w.result {
49 if strings.HasPrefix(k, prefix) {
50 delete(w.result, k)
51 }
52 }
53}
54
bae9f6d2
JC
55func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error {
56 w.lock.Lock()
57 defer w.lock.Unlock()
58 if w.result == nil {
59 w.result = make(map[string]string)
60 }
61
62 schemaList := addrToSchema(addr, w.Schema)
63 if len(schemaList) == 0 {
64 return fmt.Errorf("Invalid address to set: %#v", addr)
65 }
66
67 // If we're setting anything other than a list root or set root,
68 // then disallow it.
69 for _, schema := range schemaList[:len(schemaList)-1] {
70 if schema.Type == TypeList {
71 return fmt.Errorf(
72 "%s: can only set full list",
73 strings.Join(addr, "."))
74 }
75
76 if schema.Type == TypeMap {
77 return fmt.Errorf(
78 "%s: can only set full map",
79 strings.Join(addr, "."))
80 }
81
82 if schema.Type == TypeSet {
83 return fmt.Errorf(
84 "%s: can only set full set",
85 strings.Join(addr, "."))
86 }
87 }
88
89 return w.set(addr, value)
90}
91
92func (w *MapFieldWriter) set(addr []string, value interface{}) error {
93 schemaList := addrToSchema(addr, w.Schema)
94 if len(schemaList) == 0 {
95 return fmt.Errorf("Invalid address to set: %#v", addr)
96 }
97
98 schema := schemaList[len(schemaList)-1]
99 switch schema.Type {
100 case TypeBool, TypeInt, TypeFloat, TypeString:
101 return w.setPrimitive(addr, value, schema)
102 case TypeList:
103 return w.setList(addr, value, schema)
104 case TypeMap:
105 return w.setMap(addr, value, schema)
106 case TypeSet:
107 return w.setSet(addr, value, schema)
108 case typeObject:
109 return w.setObject(addr, value, schema)
110 default:
111 panic(fmt.Sprintf("Unknown type: %#v", schema.Type))
112 }
113}
114
115func (w *MapFieldWriter) setList(
116 addr []string,
117 v interface{},
118 schema *Schema) error {
119 k := strings.Join(addr, ".")
120 setElement := func(idx string, value interface{}) error {
121 addrCopy := make([]string, len(addr), len(addr)+1)
122 copy(addrCopy, addr)
123 return w.set(append(addrCopy, idx), value)
124 }
125
126 var vs []interface{}
127 if err := mapstructure.Decode(v, &vs); err != nil {
128 return fmt.Errorf("%s: %s", k, err)
129 }
130
15c0b25d
AP
131 // Wipe the set from the current writer prior to writing if it exists.
132 // Multiple writes to the same layer is a lot safer for lists than sets due
133 // to the fact that indexes are always deterministic and the length will
134 // always be updated with the current length on the last write, but making
135 // sure we have a clean namespace removes any chance for edge cases to pop up
136 // and ensures that the last write to the set is the correct value.
137 w.clearTree(addr)
138
bae9f6d2
JC
139 // Set the entire list.
140 var err error
141 for i, elem := range vs {
142 is := strconv.FormatInt(int64(i), 10)
143 err = setElement(is, elem)
144 if err != nil {
145 break
146 }
147 }
148 if err != nil {
149 for i, _ := range vs {
150 is := strconv.FormatInt(int64(i), 10)
151 setElement(is, nil)
152 }
153
154 return err
155 }
156
157 w.result[k+".#"] = strconv.FormatInt(int64(len(vs)), 10)
158 return nil
159}
160
161func (w *MapFieldWriter) setMap(
162 addr []string,
163 value interface{},
164 schema *Schema) error {
165 k := strings.Join(addr, ".")
166 v := reflect.ValueOf(value)
167 vs := make(map[string]interface{})
168
169 if value == nil {
170 // The empty string here means the map is removed.
171 w.result[k] = ""
172 return nil
173 }
174
175 if v.Kind() != reflect.Map {
176 return fmt.Errorf("%s: must be a map", k)
177 }
178 if v.Type().Key().Kind() != reflect.String {
179 return fmt.Errorf("%s: keys must strings", k)
180 }
181 for _, mk := range v.MapKeys() {
182 mv := v.MapIndex(mk)
183 vs[mk.String()] = mv.Interface()
184 }
185
15c0b25d
AP
186 // Wipe this address tree. The contents of the map should always reflect the
187 // last write made to it.
188 w.clearTree(addr)
189
bae9f6d2
JC
190 // Remove the pure key since we're setting the full map value
191 delete(w.result, k)
192
193 // Set each subkey
194 addrCopy := make([]string, len(addr), len(addr)+1)
195 copy(addrCopy, addr)
196 for subKey, v := range vs {
197 if err := w.set(append(addrCopy, subKey), v); err != nil {
198 return err
199 }
200 }
201
202 // Set the count
203 w.result[k+".%"] = strconv.Itoa(len(vs))
204
205 return nil
206}
207
208func (w *MapFieldWriter) setObject(
209 addr []string,
210 value interface{},
211 schema *Schema) error {
212 // Set the entire object. First decode into a proper structure
213 var v map[string]interface{}
214 if err := mapstructure.Decode(value, &v); err != nil {
215 return fmt.Errorf("%s: %s", strings.Join(addr, "."), err)
216 }
217
218 // Make space for additional elements in the address
219 addrCopy := make([]string, len(addr), len(addr)+1)
220 copy(addrCopy, addr)
221
222 // Set each element in turn
223 var err error
224 for k1, v1 := range v {
225 if err = w.set(append(addrCopy, k1), v1); err != nil {
226 break
227 }
228 }
229 if err != nil {
230 for k1, _ := range v {
231 w.set(append(addrCopy, k1), nil)
232 }
233 }
234
235 return err
236}
237
238func (w *MapFieldWriter) setPrimitive(
239 addr []string,
240 v interface{},
241 schema *Schema) error {
242 k := strings.Join(addr, ".")
243
244 if v == nil {
245 // The empty string here means the value is removed.
246 w.result[k] = ""
247 return nil
248 }
249
250 var set string
251 switch schema.Type {
252 case TypeBool:
253 var b bool
254 if err := mapstructure.Decode(v, &b); err != nil {
255 return fmt.Errorf("%s: %s", k, err)
256 }
257
258 set = strconv.FormatBool(b)
259 case TypeString:
260 if err := mapstructure.Decode(v, &set); err != nil {
261 return fmt.Errorf("%s: %s", k, err)
262 }
263 case TypeInt:
264 var n int
265 if err := mapstructure.Decode(v, &n); err != nil {
266 return fmt.Errorf("%s: %s", k, err)
267 }
268 set = strconv.FormatInt(int64(n), 10)
269 case TypeFloat:
270 var n float64
271 if err := mapstructure.Decode(v, &n); err != nil {
272 return fmt.Errorf("%s: %s", k, err)
273 }
274 set = strconv.FormatFloat(float64(n), 'G', -1, 64)
275 default:
276 return fmt.Errorf("Unknown type: %#v", schema.Type)
277 }
278
279 w.result[k] = set
280 return nil
281}
282
283func (w *MapFieldWriter) setSet(
284 addr []string,
285 value interface{},
286 schema *Schema) error {
287 addrCopy := make([]string, len(addr), len(addr)+1)
288 copy(addrCopy, addr)
289 k := strings.Join(addr, ".")
290
291 if value == nil {
292 w.result[k+".#"] = "0"
293 return nil
294 }
295
296 // If it is a slice, then we have to turn it into a *Set so that
297 // we get the proper order back based on the hash code.
298 if v := reflect.ValueOf(value); v.Kind() == reflect.Slice {
299 // Build a temp *ResourceData to use for the conversion
107c1cdb 300 tempAddr := addr[len(addr)-1:]
bae9f6d2
JC
301 tempSchema := *schema
302 tempSchema.Type = TypeList
107c1cdb 303 tempSchemaMap := map[string]*Schema{tempAddr[0]: &tempSchema}
bae9f6d2
JC
304 tempW := &MapFieldWriter{Schema: tempSchemaMap}
305
306 // Set the entire list, this lets us get sane values out of it
107c1cdb 307 if err := tempW.WriteField(tempAddr, value); err != nil {
bae9f6d2
JC
308 return err
309 }
310
311 // Build the set by going over the list items in order and
312 // hashing them into the set. The reason we go over the list and
313 // not the `value` directly is because this forces all types
314 // to become []interface{} (generic) instead of []string, which
315 // most hash functions are expecting.
316 s := schema.ZeroValue().(*Set)
317 tempR := &MapFieldReader{
318 Map: BasicMapReader(tempW.Map()),
319 Schema: tempSchemaMap,
320 }
321 for i := 0; i < v.Len(); i++ {
322 is := strconv.FormatInt(int64(i), 10)
107c1cdb 323 result, err := tempR.ReadField(append(tempAddr, is))
bae9f6d2
JC
324 if err != nil {
325 return err
326 }
327 if !result.Exists {
328 panic("set item just set doesn't exist")
329 }
330
331 s.Add(result.Value)
332 }
333
334 value = s
335 }
336
15c0b25d
AP
337 // Clear any keys that match the set address first. This is necessary because
338 // it's always possible and sometimes may be necessary to write to a certain
339 // writer layer more than once with different set data each time, which will
340 // lead to different keys being inserted, which can lead to determinism
341 // problems when the old data isn't wiped first.
342 w.clearTree(addr)
343
107c1cdb
ND
344 if value.(*Set) == nil {
345 w.result[k+".#"] = "0"
346 return nil
347 }
348
bae9f6d2
JC
349 for code, elem := range value.(*Set).m {
350 if err := w.set(append(addrCopy, code), elem); err != nil {
351 return err
352 }
353 }
354
355 w.result[k+".#"] = strconv.Itoa(value.(*Set).Len())
356 return nil
357}