1package schema
2
3import (
4	"fmt"
5	"strings"
6)
7
8// MapFieldReader reads fields out of an untyped map[string]string to
9// the best of its ability.
10type MapFieldReader struct {
11	Map    MapReader
12	Schema map[string]*Schema
13}
14
15func (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
39func (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
80func (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
99func (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.
181type MapReader interface {
182	Access(string) (string, bool)
183	Range(func(string, string) bool) bool
184}
185
186// BasicMapReader implements MapReader for a single map.
187type BasicMapReader map[string]string
188
189func (r BasicMapReader) Access(k string) (string, bool) {
190	v, ok := r[k]
191	return v, ok
192}
193
194func (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)
206type MultiMapReader []map[string]string
207
208func (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
218func (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}
236