8 // MapFieldReader reads fields out of an untyped map[string]string to
9 // the best of its ability.
10 type MapFieldReader struct {
12 Schema map[string]*Schema
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
22 schema := schemaList[len(schemaList)-1]
24 case TypeBool, TypeInt, TypeFloat, TypeString:
25 return r.readPrimitive(address, schema)
27 return readListField(r, address, schema)
29 return r.readMap(k, schema)
31 return r.readSet(address, schema)
33 return readObjectField(r, address, schema.Elem.(map[string]*Schema))
35 panic(fmt.Sprintf("Unknown type: %s", schema.Type))
39 func (r *MapFieldReader) readMap(k string, schema *Schema) (FieldReadResult, error) {
40 result := make(map[string]interface{})
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
46 if v, ok := r.Map.Access(k); ok && v == "" {
51 r.Map.Range(func(k, v string) bool {
52 if strings.HasPrefix(k, prefix) {
55 key := k[len(prefix):]
56 if key != "%" && key != "#" {
64 err := mapValuesToPrimitive(k, result, schema)
66 return FieldReadResult{}, nil
69 var resultVal interface{}
74 return FieldReadResult{
80 func (r *MapFieldReader) readPrimitive(
81 address []string, schema *Schema) (FieldReadResult, error) {
82 k := strings.Join(address, ".")
83 result, ok := r.Map.Access(k)
85 return FieldReadResult{}, nil
88 returnVal, err := stringToPrimitive(result, false, schema)
90 return FieldReadResult{}, err
93 return FieldReadResult{
99 func (r *MapFieldReader) readSet(
100 address []string, schema *Schema) (FieldReadResult, error) {
101 // Get the number of elements in the list
102 countRaw, err := r.readPrimitive(
103 append(address, "#"), &Schema{Type: TypeInt})
105 return FieldReadResult{}, err
107 if !countRaw.Exists {
108 // No count, means we have no list
112 // Create the set that will be our result
113 set := schema.ZeroValue().(*Set)
115 // If we have an empty list, then return an empty list
116 if countRaw.Computed || countRaw.Value.(int) == 0 {
117 return FieldReadResult{
119 Exists: countRaw.Exists,
120 Computed: countRaw.Computed,
124 // Go through the map and find all the set items
125 prefix := strings.Join(address, ".") + "."
126 countExpected := countRaw.Value.(int)
127 countActual := make(map[string]struct{})
128 completed := r.Map.Range(func(k, _ string) bool {
129 if !strings.HasPrefix(k, prefix) {
132 if strings.HasPrefix(k, prefix+"#") {
133 // Ignore the count field
137 // Split the key, since it might be a sub-object like "idx.field"
138 parts := strings.Split(k[len(prefix):], ".")
141 var raw FieldReadResult
142 raw, err = r.ReadField(append(address, idx))
147 // This shouldn't happen because we just verified it does exist
148 panic("missing field in set: " + k + "." + idx)
153 // Due to the way multimap readers work, if we've seen the number
154 // of fields we expect, then exit so that we don't read later values.
155 // For example: the "set" map might have "ports.#", "ports.0", and
156 // "ports.1", but the "state" map might have those plus "ports.2".
157 // We don't want "ports.2"
158 countActual[idx] = struct{}{}
159 if len(countActual) >= countExpected {
165 if !completed && err != nil {
166 return FieldReadResult{}, err
169 return FieldReadResult{
175 // MapReader is an interface that is given to MapFieldReader for accessing
176 // a "map". This can be used to have alternate implementations. For a basic
177 // map[string]string, use BasicMapReader.
178 type MapReader interface {
179 Access(string) (string, bool)
180 Range(func(string, string) bool) bool
183 // BasicMapReader implements MapReader for a single map.
184 type BasicMapReader map[string]string
186 func (r BasicMapReader) Access(k string) (string, bool) {
191 func (r BasicMapReader) Range(f func(string, string) bool) bool {
192 for k, v := range r {
193 if cont := f(k, v); !cont {
201 // MultiMapReader reads over multiple maps, preferring keys that are
202 // founder earlier (lower number index) vs. later (higher number index)
203 type MultiMapReader []map[string]string
205 func (r MultiMapReader) Access(k string) (string, bool) {
206 for _, m := range r {
207 if v, ok := m[k]; ok {
215 func (r MultiMapReader) Range(f func(string, string) bool) bool {
216 done := make(map[string]struct{})
217 for _, m := range r {
218 for k, v := range m {
219 if _, ok := done[k]; ok {
223 if cont := f(k, v); !cont {