]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / helper / schema / field_writer_map.go
1 package schema
2
3 import (
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.
14 type 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.
22 func (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
32 func (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
42 func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error {
43 w.lock.Lock()
44 defer w.lock.Unlock()
45 if w.result == nil {
46 w.result = make(map[string]string)
47 }
48
49 schemaList := addrToSchema(addr, w.Schema)
50 if len(schemaList) == 0 {
51 return fmt.Errorf("Invalid address to set: %#v", addr)
52 }
53
54 // If we're setting anything other than a list root or set root,
55 // then disallow it.
56 for _, schema := range schemaList[:len(schemaList)-1] {
57 if schema.Type == TypeList {
58 return fmt.Errorf(
59 "%s: can only set full list",
60 strings.Join(addr, "."))
61 }
62
63 if schema.Type == TypeMap {
64 return fmt.Errorf(
65 "%s: can only set full map",
66 strings.Join(addr, "."))
67 }
68
69 if schema.Type == TypeSet {
70 return fmt.Errorf(
71 "%s: can only set full set",
72 strings.Join(addr, "."))
73 }
74 }
75
76 return w.set(addr, value)
77 }
78
79 func (w *MapFieldWriter) set(addr []string, value interface{}) error {
80 schemaList := addrToSchema(addr, w.Schema)
81 if len(schemaList) == 0 {
82 return fmt.Errorf("Invalid address to set: %#v", addr)
83 }
84
85 schema := schemaList[len(schemaList)-1]
86 switch schema.Type {
87 case TypeBool, TypeInt, TypeFloat, TypeString:
88 return w.setPrimitive(addr, value, schema)
89 case TypeList:
90 return w.setList(addr, value, schema)
91 case TypeMap:
92 return w.setMap(addr, value, schema)
93 case TypeSet:
94 return w.setSet(addr, value, schema)
95 case typeObject:
96 return w.setObject(addr, value, schema)
97 default:
98 panic(fmt.Sprintf("Unknown type: %#v", schema.Type))
99 }
100 }
101
102 func (w *MapFieldWriter) setList(
103 addr []string,
104 v interface{},
105 schema *Schema) error {
106 k := strings.Join(addr, ".")
107 setElement := func(idx string, value interface{}) error {
108 addrCopy := make([]string, len(addr), len(addr)+1)
109 copy(addrCopy, addr)
110 return w.set(append(addrCopy, idx), value)
111 }
112
113 var vs []interface{}
114 if err := mapstructure.Decode(v, &vs); err != nil {
115 return fmt.Errorf("%s: %s", k, err)
116 }
117
118 // Set the entire list.
119 var err error
120 for i, elem := range vs {
121 is := strconv.FormatInt(int64(i), 10)
122 err = setElement(is, elem)
123 if err != nil {
124 break
125 }
126 }
127 if err != nil {
128 for i, _ := range vs {
129 is := strconv.FormatInt(int64(i), 10)
130 setElement(is, nil)
131 }
132
133 return err
134 }
135
136 w.result[k+".#"] = strconv.FormatInt(int64(len(vs)), 10)
137 return nil
138 }
139
140 func (w *MapFieldWriter) setMap(
141 addr []string,
142 value interface{},
143 schema *Schema) error {
144 k := strings.Join(addr, ".")
145 v := reflect.ValueOf(value)
146 vs := make(map[string]interface{})
147
148 if value == nil {
149 // The empty string here means the map is removed.
150 w.result[k] = ""
151 return nil
152 }
153
154 if v.Kind() != reflect.Map {
155 return fmt.Errorf("%s: must be a map", k)
156 }
157 if v.Type().Key().Kind() != reflect.String {
158 return fmt.Errorf("%s: keys must strings", k)
159 }
160 for _, mk := range v.MapKeys() {
161 mv := v.MapIndex(mk)
162 vs[mk.String()] = mv.Interface()
163 }
164
165 // Remove the pure key since we're setting the full map value
166 delete(w.result, k)
167
168 // Set each subkey
169 addrCopy := make([]string, len(addr), len(addr)+1)
170 copy(addrCopy, addr)
171 for subKey, v := range vs {
172 if err := w.set(append(addrCopy, subKey), v); err != nil {
173 return err
174 }
175 }
176
177 // Set the count
178 w.result[k+".%"] = strconv.Itoa(len(vs))
179
180 return nil
181 }
182
183 func (w *MapFieldWriter) setObject(
184 addr []string,
185 value interface{},
186 schema *Schema) error {
187 // Set the entire object. First decode into a proper structure
188 var v map[string]interface{}
189 if err := mapstructure.Decode(value, &v); err != nil {
190 return fmt.Errorf("%s: %s", strings.Join(addr, "."), err)
191 }
192
193 // Make space for additional elements in the address
194 addrCopy := make([]string, len(addr), len(addr)+1)
195 copy(addrCopy, addr)
196
197 // Set each element in turn
198 var err error
199 for k1, v1 := range v {
200 if err = w.set(append(addrCopy, k1), v1); err != nil {
201 break
202 }
203 }
204 if err != nil {
205 for k1, _ := range v {
206 w.set(append(addrCopy, k1), nil)
207 }
208 }
209
210 return err
211 }
212
213 func (w *MapFieldWriter) setPrimitive(
214 addr []string,
215 v interface{},
216 schema *Schema) error {
217 k := strings.Join(addr, ".")
218
219 if v == nil {
220 // The empty string here means the value is removed.
221 w.result[k] = ""
222 return nil
223 }
224
225 var set string
226 switch schema.Type {
227 case TypeBool:
228 var b bool
229 if err := mapstructure.Decode(v, &b); err != nil {
230 return fmt.Errorf("%s: %s", k, err)
231 }
232
233 set = strconv.FormatBool(b)
234 case TypeString:
235 if err := mapstructure.Decode(v, &set); err != nil {
236 return fmt.Errorf("%s: %s", k, err)
237 }
238 case TypeInt:
239 var n int
240 if err := mapstructure.Decode(v, &n); err != nil {
241 return fmt.Errorf("%s: %s", k, err)
242 }
243 set = strconv.FormatInt(int64(n), 10)
244 case TypeFloat:
245 var n float64
246 if err := mapstructure.Decode(v, &n); err != nil {
247 return fmt.Errorf("%s: %s", k, err)
248 }
249 set = strconv.FormatFloat(float64(n), 'G', -1, 64)
250 default:
251 return fmt.Errorf("Unknown type: %#v", schema.Type)
252 }
253
254 w.result[k] = set
255 return nil
256 }
257
258 func (w *MapFieldWriter) setSet(
259 addr []string,
260 value interface{},
261 schema *Schema) error {
262 addrCopy := make([]string, len(addr), len(addr)+1)
263 copy(addrCopy, addr)
264 k := strings.Join(addr, ".")
265
266 if value == nil {
267 w.result[k+".#"] = "0"
268 return nil
269 }
270
271 // If it is a slice, then we have to turn it into a *Set so that
272 // we get the proper order back based on the hash code.
273 if v := reflect.ValueOf(value); v.Kind() == reflect.Slice {
274 // Build a temp *ResourceData to use for the conversion
275 tempSchema := *schema
276 tempSchema.Type = TypeList
277 tempSchemaMap := map[string]*Schema{addr[0]: &tempSchema}
278 tempW := &MapFieldWriter{Schema: tempSchemaMap}
279
280 // Set the entire list, this lets us get sane values out of it
281 if err := tempW.WriteField(addr, value); err != nil {
282 return err
283 }
284
285 // Build the set by going over the list items in order and
286 // hashing them into the set. The reason we go over the list and
287 // not the `value` directly is because this forces all types
288 // to become []interface{} (generic) instead of []string, which
289 // most hash functions are expecting.
290 s := schema.ZeroValue().(*Set)
291 tempR := &MapFieldReader{
292 Map: BasicMapReader(tempW.Map()),
293 Schema: tempSchemaMap,
294 }
295 for i := 0; i < v.Len(); i++ {
296 is := strconv.FormatInt(int64(i), 10)
297 result, err := tempR.ReadField(append(addrCopy, is))
298 if err != nil {
299 return err
300 }
301 if !result.Exists {
302 panic("set item just set doesn't exist")
303 }
304
305 s.Add(result.Value)
306 }
307
308 value = s
309 }
310
311 for code, elem := range value.(*Set).m {
312 if err := w.set(append(addrCopy, code), elem); err != nil {
313 return err
314 }
315 }
316
317 w.result[k+".#"] = strconv.Itoa(value.(*Set).Len())
318 return nil
319 }