8 // FieldReaders are responsible for decoding fields out of data into
9 // the proper typed representation. ResourceData uses this to query data
10 // out of multiple sources: config, state, diffs, etc.
11 type FieldReader interface {
12 ReadField([]string) (FieldReadResult, error)
15 // FieldReadResult encapsulates all the resulting data from reading
17 type FieldReadResult struct {
18 // Value is the actual read value. NegValue is the _negative_ value
19 // or the items that should be removed (if they existed). NegValue
20 // doesn't make sense for primitives but is important for any
21 // container types such as maps, sets, lists.
23 ValueProcessed interface{}
25 // Exists is true if the field was found in the data. False means
26 // it wasn't found if there was no error.
29 // Computed is true if the field was found but the value
34 // ValueOrZero returns the value of this result or the zero value of the
35 // schema type, ensuring a consistent non-nil return value.
36 func (r *FieldReadResult) ValueOrZero(s *Schema) interface{} {
44 // addrToSchema finds the final element schema for the given address
45 // and the given schema. It returns all the schemas that led to the final
46 // schema. These are in order of the address (out to in).
47 func addrToSchema(addr []string, schemaMap map[string]*Schema) []*Schema {
53 // If we aren't given an address, then the user is requesting the
54 // full object, so we return the special value which is the full object.
56 return []*Schema{current}
59 result := make([]*Schema, 0, len(addr))
65 // We want to trim off the first "typeObject" since its not a
66 // real lookup that people do. i.e. []string{"foo"} in a structure
67 // isn't {typeObject, typeString}, its just a {typeString}.
68 if len(result) > 0 || current.Type != typeObject {
69 result = append(result, current)
72 switch t := current.Type; t {
73 case TypeBool, TypeInt, TypeFloat, TypeString:
77 case TypeList, TypeSet:
78 isIndex := len(addr) > 0 && addr[0] == "#"
80 switch v := current.Elem.(type) {
89 current = &Schema{Type: v}
91 // we may not know the Elem type and are just looking for the
98 // we've processed the address, so return what we've
104 if _, err := strconv.Atoi(addr[0]); err == nil {
105 // we're indexing a value without a schema. This can
106 // happen if the list is nested in another schema type.
107 // Default to a TypeString like we do with a map
108 current = &Schema{Type: TypeString}
116 // If we only have one more thing and the next thing
117 // is a #, then we're accessing the index which is always
120 current = &Schema{Type: TypeInt}
126 switch v := current.Elem.(type) {
128 current = &Schema{Type: v}
130 // maps default to string values. This is all we can have
131 // if this is nested in another list or map.
132 current = &Schema{Type: TypeString}
136 // If we're already in the object, then we want to handle Sets
137 // and Lists specially. Basically, their next key is the lookup
138 // key (the set value or the list element). For these scenarios,
139 // we just want to skip it and move to the next element if there
142 lastType := result[len(result)-2].Type
143 if lastType == TypeSet || lastType == TypeList {
153 m := current.Elem.(map[string]*Schema)
167 // readListField is a generic method for reading a list field out of a
168 // a FieldReader. It does this based on the assumption that there is a key
169 // "foo.#" for a list "foo" and that the indexes are "foo.0", "foo.1", etc.
172 r FieldReader, addr []string, schema *Schema) (FieldReadResult, error) {
173 addrPadded := make([]string, len(addr)+1)
174 copy(addrPadded, addr)
175 addrPadded[len(addrPadded)-1] = "#"
177 // Get the number of elements in the list
178 countResult, err := r.ReadField(addrPadded)
180 return FieldReadResult{}, err
182 if !countResult.Exists {
183 // No count, means we have no list
184 countResult.Value = 0
187 // If we have an empty list, then return an empty list
188 if countResult.Computed || countResult.Value.(int) == 0 {
189 return FieldReadResult{
190 Value: []interface{}{},
191 Exists: countResult.Exists,
192 Computed: countResult.Computed,
196 // Go through each count, and get the item value out of it
197 result := make([]interface{}, countResult.Value.(int))
198 for i, _ := range result {
199 is := strconv.FormatInt(int64(i), 10)
200 addrPadded[len(addrPadded)-1] = is
201 rawResult, err := r.ReadField(addrPadded)
203 return FieldReadResult{}, err
205 if !rawResult.Exists {
206 // This should never happen, because by the time the data
207 // gets to the FieldReaders, all the defaults should be set by
209 rawResult.Value = nil
212 result[i] = rawResult.Value
215 return FieldReadResult{
221 // readObjectField is a generic method for reading objects out of FieldReaders
222 // based on the assumption that building an address of []string{k, FIELD}
223 // will result in the proper field data.
224 func readObjectField(
227 schema map[string]*Schema) (FieldReadResult, error) {
228 result := make(map[string]interface{})
230 for field, s := range schema {
231 addrRead := make([]string, len(addr), len(addr)+1)
233 addrRead = append(addrRead, field)
234 rawResult, err := r.ReadField(addrRead)
236 return FieldReadResult{}, err
238 if rawResult.Exists {
242 result[field] = rawResult.ValueOrZero(s)
245 return FieldReadResult{
251 // convert map values to the proper primitive type based on schema.Elem
252 func mapValuesToPrimitive(m map[string]interface{}, schema *Schema) error {
254 elemType := TypeString
255 if et, ok := schema.Elem.(ValueType); ok {
260 case TypeInt, TypeFloat, TypeBool:
261 for k, v := range m {
267 v, err := stringToPrimitive(vs, false, &Schema{Type: elemType})
278 func stringToPrimitive(
279 value string, computed bool, schema *Schema) (interface{}, error) {
280 var returnVal interface{}
291 v, err := strconv.ParseBool(value)
306 v, err := strconv.ParseFloat(value, 64)
321 v, err := strconv.ParseInt(value, 0, 0)
330 panic(fmt.Sprintf("Unknown type: %s", schema.Type))
333 return returnVal, nil