github.com/jrperritt/terraform@v0.1.1-0.20170525065507-96f391dafc38/helper/schema/field_writer_map.go (about) 1 package schema 2 3 import ( 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. 14 type 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. 22 func (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 32 func (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 func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error { 43 w.lock.Lock() 44 defer w.lock.Unlock() 45 if w.result == nil { 46 w.result = make(map[string]string) 47 } 48 49 schemaList := addrToSchema(addr, w.Schema) 50 if len(schemaList) == 0 { 51 return fmt.Errorf("Invalid address to set: %#v", addr) 52 } 53 54 // If we're setting anything other than a list root or set root, 55 // then disallow it. 56 for _, schema := range schemaList[:len(schemaList)-1] { 57 if schema.Type == TypeList { 58 return fmt.Errorf( 59 "%s: can only set full list", 60 strings.Join(addr, ".")) 61 } 62 63 if schema.Type == TypeMap { 64 return fmt.Errorf( 65 "%s: can only set full map", 66 strings.Join(addr, ".")) 67 } 68 69 if schema.Type == TypeSet { 70 return fmt.Errorf( 71 "%s: can only set full set", 72 strings.Join(addr, ".")) 73 } 74 } 75 76 return w.set(addr, value) 77 } 78 79 func (w *MapFieldWriter) set(addr []string, value interface{}) error { 80 schemaList := addrToSchema(addr, w.Schema) 81 if len(schemaList) == 0 { 82 return fmt.Errorf("Invalid address to set: %#v", addr) 83 } 84 85 schema := schemaList[len(schemaList)-1] 86 switch schema.Type { 87 case TypeBool, TypeInt, TypeFloat, TypeString: 88 return w.setPrimitive(addr, value, schema) 89 case TypeList: 90 return w.setList(addr, value, schema) 91 case TypeMap: 92 return w.setMap(addr, value, schema) 93 case TypeSet: 94 return w.setSet(addr, value, schema) 95 case typeObject: 96 return w.setObject(addr, value, schema) 97 default: 98 panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) 99 } 100 } 101 102 func (w *MapFieldWriter) setList( 103 addr []string, 104 v interface{}, 105 schema *Schema) error { 106 k := strings.Join(addr, ".") 107 setElement := func(idx string, value interface{}) error { 108 addrCopy := make([]string, len(addr), len(addr)+1) 109 copy(addrCopy, addr) 110 return w.set(append(addrCopy, idx), value) 111 } 112 113 var vs []interface{} 114 if err := mapstructure.Decode(v, &vs); err != nil { 115 return fmt.Errorf("%s: %s", k, err) 116 } 117 118 // Set the entire list. 119 var err error 120 for i, elem := range vs { 121 is := strconv.FormatInt(int64(i), 10) 122 err = setElement(is, elem) 123 if err != nil { 124 break 125 } 126 } 127 if err != nil { 128 for i, _ := range vs { 129 is := strconv.FormatInt(int64(i), 10) 130 setElement(is, nil) 131 } 132 133 return err 134 } 135 136 w.result[k+".#"] = strconv.FormatInt(int64(len(vs)), 10) 137 return nil 138 } 139 140 func (w *MapFieldWriter) setMap( 141 addr []string, 142 value interface{}, 143 schema *Schema) error { 144 k := strings.Join(addr, ".") 145 v := reflect.ValueOf(value) 146 vs := make(map[string]interface{}) 147 148 if value == nil { 149 // The empty string here means the map is removed. 150 w.result[k] = "" 151 return nil 152 } 153 154 if v.Kind() != reflect.Map { 155 return fmt.Errorf("%s: must be a map", k) 156 } 157 if v.Type().Key().Kind() != reflect.String { 158 return fmt.Errorf("%s: keys must strings", k) 159 } 160 for _, mk := range v.MapKeys() { 161 mv := v.MapIndex(mk) 162 vs[mk.String()] = mv.Interface() 163 } 164 165 // Remove the pure key since we're setting the full map value 166 delete(w.result, k) 167 168 // Set each subkey 169 addrCopy := make([]string, len(addr), len(addr)+1) 170 copy(addrCopy, addr) 171 for subKey, v := range vs { 172 if err := w.set(append(addrCopy, subKey), v); err != nil { 173 return err 174 } 175 } 176 177 // Set the count 178 w.result[k+".%"] = strconv.Itoa(len(vs)) 179 180 return nil 181 } 182 183 func (w *MapFieldWriter) setObject( 184 addr []string, 185 value interface{}, 186 schema *Schema) error { 187 // Set the entire object. First decode into a proper structure 188 var v map[string]interface{} 189 if err := mapstructure.Decode(value, &v); err != nil { 190 return fmt.Errorf("%s: %s", strings.Join(addr, "."), err) 191 } 192 193 // Make space for additional elements in the address 194 addrCopy := make([]string, len(addr), len(addr)+1) 195 copy(addrCopy, addr) 196 197 // Set each element in turn 198 var err error 199 for k1, v1 := range v { 200 if err = w.set(append(addrCopy, k1), v1); err != nil { 201 break 202 } 203 } 204 if err != nil { 205 for k1, _ := range v { 206 w.set(append(addrCopy, k1), nil) 207 } 208 } 209 210 return err 211 } 212 213 func (w *MapFieldWriter) setPrimitive( 214 addr []string, 215 v interface{}, 216 schema *Schema) error { 217 k := strings.Join(addr, ".") 218 219 if v == nil { 220 // The empty string here means the value is removed. 221 w.result[k] = "" 222 return nil 223 } 224 225 var set string 226 switch schema.Type { 227 case TypeBool: 228 var b bool 229 if err := mapstructure.Decode(v, &b); err != nil { 230 return fmt.Errorf("%s: %s", k, err) 231 } 232 233 set = strconv.FormatBool(b) 234 case TypeString: 235 if err := mapstructure.Decode(v, &set); err != nil { 236 return fmt.Errorf("%s: %s", k, err) 237 } 238 case TypeInt: 239 var n int 240 if err := mapstructure.Decode(v, &n); err != nil { 241 return fmt.Errorf("%s: %s", k, err) 242 } 243 set = strconv.FormatInt(int64(n), 10) 244 case TypeFloat: 245 var n float64 246 if err := mapstructure.Decode(v, &n); err != nil { 247 return fmt.Errorf("%s: %s", k, err) 248 } 249 set = strconv.FormatFloat(float64(n), 'G', -1, 64) 250 default: 251 return fmt.Errorf("Unknown type: %#v", schema.Type) 252 } 253 254 w.result[k] = set 255 return nil 256 } 257 258 func (w *MapFieldWriter) setSet( 259 addr []string, 260 value interface{}, 261 schema *Schema) error { 262 addrCopy := make([]string, len(addr), len(addr)+1) 263 copy(addrCopy, addr) 264 k := strings.Join(addr, ".") 265 266 if value == nil { 267 w.result[k+".#"] = "0" 268 return nil 269 } 270 271 // If it is a slice, then we have to turn it into a *Set so that 272 // we get the proper order back based on the hash code. 273 if v := reflect.ValueOf(value); v.Kind() == reflect.Slice { 274 // Build a temp *ResourceData to use for the conversion 275 tempSchema := *schema 276 tempSchema.Type = TypeList 277 tempSchemaMap := map[string]*Schema{addr[0]: &tempSchema} 278 tempW := &MapFieldWriter{Schema: tempSchemaMap} 279 280 // Set the entire list, this lets us get sane values out of it 281 if err := tempW.WriteField(addr, value); err != nil { 282 return err 283 } 284 285 // Build the set by going over the list items in order and 286 // hashing them into the set. The reason we go over the list and 287 // not the `value` directly is because this forces all types 288 // to become []interface{} (generic) instead of []string, which 289 // most hash functions are expecting. 290 s := schema.ZeroValue().(*Set) 291 tempR := &MapFieldReader{ 292 Map: BasicMapReader(tempW.Map()), 293 Schema: tempSchemaMap, 294 } 295 for i := 0; i < v.Len(); i++ { 296 is := strconv.FormatInt(int64(i), 10) 297 result, err := tempR.ReadField(append(addrCopy, is)) 298 if err != nil { 299 return err 300 } 301 if !result.Exists { 302 panic("set item just set doesn't exist") 303 } 304 305 s.Add(result.Value) 306 } 307 308 value = s 309 } 310 311 for code, elem := range value.(*Set).m { 312 if err := w.set(append(addrCopy, code), elem); err != nil { 313 return err 314 } 315 } 316 317 w.result[k+".#"] = strconv.Itoa(value.(*Set).Len()) 318 return nil 319 }