github.com/paulmey/terraform@v0.5.2-0.20150519145237-046e9b4c884d/helper/schema/field_reader_map.go (about) 1 package schema 2 3 import ( 4 "fmt" 5 "strings" 6 ) 7 8 // MapFieldReader reads fields out of an untyped map[string]string to 9 // the best of its ability. 10 type MapFieldReader struct { 11 Map MapReader 12 Schema map[string]*Schema 13 } 14 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 20 } 21 22 schema := schemaList[len(schemaList)-1] 23 switch schema.Type { 24 case TypeBool: 25 fallthrough 26 case TypeInt: 27 fallthrough 28 case TypeFloat: 29 fallthrough 30 case TypeString: 31 return r.readPrimitive(address, schema) 32 case TypeList: 33 return readListField(r, address, schema) 34 case TypeMap: 35 return r.readMap(k) 36 case TypeSet: 37 return r.readSet(address, schema) 38 case typeObject: 39 return readObjectField(r, address, schema.Elem.(map[string]*Schema)) 40 default: 41 panic(fmt.Sprintf("Unknown type: %s", schema.Type)) 42 } 43 } 44 45 func (r *MapFieldReader) readMap(k string) (FieldReadResult, error) { 46 result := make(map[string]interface{}) 47 resultSet := false 48 49 // If the name of the map field is directly in the map with an 50 // empty string, it means that the map is being deleted, so mark 51 // that is is set. 52 if v, ok := r.Map.Access(k); ok && v == "" { 53 resultSet = true 54 } 55 56 prefix := k + "." 57 r.Map.Range(func(k, v string) bool { 58 if strings.HasPrefix(k, prefix) { 59 resultSet = true 60 61 key := k[len(prefix):] 62 if key != "#" { 63 result[key] = v 64 } 65 } 66 67 return true 68 }) 69 70 var resultVal interface{} 71 if resultSet { 72 resultVal = result 73 } 74 75 return FieldReadResult{ 76 Value: resultVal, 77 Exists: resultSet, 78 }, nil 79 } 80 81 func (r *MapFieldReader) readPrimitive( 82 address []string, schema *Schema) (FieldReadResult, error) { 83 k := strings.Join(address, ".") 84 result, ok := r.Map.Access(k) 85 if !ok { 86 return FieldReadResult{}, nil 87 } 88 89 returnVal, err := stringToPrimitive(result, false, schema) 90 if err != nil { 91 return FieldReadResult{}, err 92 } 93 94 return FieldReadResult{ 95 Value: returnVal, 96 Exists: true, 97 }, nil 98 } 99 100 func (r *MapFieldReader) readSet( 101 address []string, schema *Schema) (FieldReadResult, error) { 102 // Get the number of elements in the list 103 countRaw, err := r.readPrimitive( 104 append(address, "#"), &Schema{Type: TypeInt}) 105 if err != nil { 106 return FieldReadResult{}, err 107 } 108 if !countRaw.Exists { 109 // No count, means we have no list 110 countRaw.Value = 0 111 } 112 113 // Create the set that will be our result 114 set := &Set{F: schema.Set} 115 116 // If we have an empty list, then return an empty list 117 if countRaw.Computed || countRaw.Value.(int) == 0 { 118 return FieldReadResult{ 119 Value: set, 120 Exists: countRaw.Exists, 121 Computed: countRaw.Computed, 122 }, nil 123 } 124 125 // Go through the map and find all the set items 126 prefix := strings.Join(address, ".") + "." 127 countExpected := countRaw.Value.(int) 128 countActual := make(map[string]struct{}) 129 completed := r.Map.Range(func(k, _ string) bool { 130 if !strings.HasPrefix(k, prefix) { 131 return true 132 } 133 if strings.HasPrefix(k, prefix+"#") { 134 // Ignore the count field 135 return true 136 } 137 138 // Split the key, since it might be a sub-object like "idx.field" 139 parts := strings.Split(k[len(prefix):], ".") 140 idx := parts[0] 141 142 var raw FieldReadResult 143 raw, err = r.ReadField(append(address, idx)) 144 if err != nil { 145 return false 146 } 147 if !raw.Exists { 148 // This shouldn't happen because we just verified it does exist 149 panic("missing field in set: " + k + "." + idx) 150 } 151 152 set.Add(raw.Value) 153 154 // Due to the way multimap readers work, if we've seen the number 155 // of fields we expect, then exit so that we don't read later values. 156 // For example: the "set" map might have "ports.#", "ports.0", and 157 // "ports.1", but the "state" map might have those plus "ports.2". 158 // We don't want "ports.2" 159 countActual[idx] = struct{}{} 160 if len(countActual) >= countExpected { 161 return false 162 } 163 164 return true 165 }) 166 if !completed && err != nil { 167 return FieldReadResult{}, err 168 } 169 170 return FieldReadResult{ 171 Value: set, 172 Exists: true, 173 }, nil 174 } 175 176 // MapReader is an interface that is given to MapFieldReader for accessing 177 // a "map". This can be used to have alternate implementations. For a basic 178 // map[string]string, use BasicMapReader. 179 type MapReader interface { 180 Access(string) (string, bool) 181 Range(func(string, string) bool) bool 182 } 183 184 // BasicMapReader implements MapReader for a single map. 185 type BasicMapReader map[string]string 186 187 func (r BasicMapReader) Access(k string) (string, bool) { 188 v, ok := r[k] 189 return v, ok 190 } 191 192 func (r BasicMapReader) Range(f func(string, string) bool) bool { 193 for k, v := range r { 194 if cont := f(k, v); !cont { 195 return false 196 } 197 } 198 199 return true 200 } 201 202 // MultiMapReader reads over multiple maps, preferring keys that are 203 // founder earlier (lower number index) vs. later (higher number index) 204 type MultiMapReader []map[string]string 205 206 func (r MultiMapReader) Access(k string) (string, bool) { 207 for _, m := range r { 208 if v, ok := m[k]; ok { 209 return v, ok 210 } 211 } 212 213 return "", false 214 } 215 216 func (r MultiMapReader) Range(f func(string, string) bool) bool { 217 done := make(map[string]struct{}) 218 for _, m := range r { 219 for k, v := range m { 220 if _, ok := done[k]; ok { 221 continue 222 } 223 224 if cont := f(k, v); !cont { 225 return false 226 } 227 228 done[k] = struct{}{} 229 } 230 } 231 232 return true 233 }