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