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
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
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
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
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
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
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
300		tempAddr := addr[len(addr)-1:]
301		tempSchema := *schema
302		tempSchema.Type = TypeList
303		tempSchemaMap := map[string]*Schema{tempAddr[0]: &tempSchema}
304		tempW := &MapFieldWriter{Schema: tempSchemaMap}
305
306		// Set the entire list, this lets us get sane values out of it
307		if err := tempW.WriteField(tempAddr, value); err != nil {
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)
323			result, err := tempR.ReadField(append(tempAddr, is))
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
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
344	if value.(*Set) == nil {
345		w.result[k+".#"] = "0"
346		return nil
347	}
348
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}
358