github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/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 if v.Kind() != reflect.Map { 146 return fmt.Errorf("%s: must be a map", k) 147 } 148 if v.Type().Key().Kind() != reflect.String { 149 return fmt.Errorf("%s: keys must strings", k) 150 } 151 for _, mk := range v.MapKeys() { 152 mv := v.MapIndex(mk) 153 vs[mk.String()] = mv.Interface() 154 } 155 } 156 157 if len(vs) == 0 { 158 // The empty string here means the map is removed. 159 w.result[k] = "" 160 return nil 161 } 162 163 // Remove the pure key since we're setting the full map value 164 delete(w.result, k) 165 166 // Set each subkey 167 addrCopy := make([]string, len(addr), len(addr)+1) 168 copy(addrCopy, addr) 169 for subKey, v := range vs { 170 if err := w.set(append(addrCopy, subKey), v); err != nil { 171 return err 172 } 173 } 174 175 // Set the count 176 w.result[k+".#"] = strconv.Itoa(len(vs)) 177 178 return nil 179 } 180 181 func (w *MapFieldWriter) setObject( 182 addr []string, 183 value interface{}, 184 schema *Schema) error { 185 // Set the entire object. First decode into a proper structure 186 var v map[string]interface{} 187 if err := mapstructure.Decode(value, &v); err != nil { 188 return fmt.Errorf("%s: %s", strings.Join(addr, "."), err) 189 } 190 191 // Make space for additional elements in the address 192 addrCopy := make([]string, len(addr), len(addr)+1) 193 copy(addrCopy, addr) 194 195 // Set each element in turn 196 var err error 197 for k1, v1 := range v { 198 if err = w.set(append(addrCopy, k1), v1); err != nil { 199 break 200 } 201 } 202 if err != nil { 203 for k1, _ := range v { 204 w.set(append(addrCopy, k1), nil) 205 } 206 } 207 208 return err 209 } 210 211 func (w *MapFieldWriter) setPrimitive( 212 addr []string, 213 v interface{}, 214 schema *Schema) error { 215 k := strings.Join(addr, ".") 216 217 if v == nil { 218 delete(w.result, k) 219 return nil 220 } 221 222 var set string 223 switch schema.Type { 224 case TypeBool: 225 var b bool 226 if err := mapstructure.Decode(v, &b); err != nil { 227 return fmt.Errorf("%s: %s", k, err) 228 } 229 230 set = strconv.FormatBool(b) 231 case TypeString: 232 if err := mapstructure.Decode(v, &set); err != nil { 233 return fmt.Errorf("%s: %s", k, err) 234 } 235 case TypeInt: 236 var n int 237 if err := mapstructure.Decode(v, &n); err != nil { 238 return fmt.Errorf("%s: %s", k, err) 239 } 240 set = strconv.FormatInt(int64(n), 10) 241 case TypeFloat: 242 var n float64 243 if err := mapstructure.Decode(v, &n); err != nil { 244 return fmt.Errorf("%s: %s", k, err) 245 } 246 set = strconv.FormatFloat(float64(n), 'G', -1, 64) 247 default: 248 return fmt.Errorf("Unknown type: %#v", schema.Type) 249 } 250 251 w.result[k] = set 252 return nil 253 } 254 255 func (w *MapFieldWriter) setSet( 256 addr []string, 257 value interface{}, 258 schema *Schema) error { 259 addrCopy := make([]string, len(addr), len(addr)+1) 260 copy(addrCopy, addr) 261 k := strings.Join(addr, ".") 262 263 if value == nil { 264 w.result[k+".#"] = "0" 265 return nil 266 } 267 268 // If it is a slice, then we have to turn it into a *Set so that 269 // we get the proper order back based on the hash code. 270 if v := reflect.ValueOf(value); v.Kind() == reflect.Slice { 271 // Build a temp *ResourceData to use for the conversion 272 tempSchema := *schema 273 tempSchema.Type = TypeList 274 tempSchemaMap := map[string]*Schema{addr[0]: &tempSchema} 275 tempW := &MapFieldWriter{Schema: tempSchemaMap} 276 277 // Set the entire list, this lets us get sane values out of it 278 if err := tempW.WriteField(addr, value); err != nil { 279 return err 280 } 281 282 // Build the set by going over the list items in order and 283 // hashing them into the set. The reason we go over the list and 284 // not the `value` directly is because this forces all types 285 // to become []interface{} (generic) instead of []string, which 286 // most hash functions are expecting. 287 s := &Set{F: schema.Set} 288 tempR := &MapFieldReader{ 289 Map: BasicMapReader(tempW.Map()), 290 Schema: tempSchemaMap, 291 } 292 for i := 0; i < v.Len(); i++ { 293 is := strconv.FormatInt(int64(i), 10) 294 result, err := tempR.ReadField(append(addrCopy, is)) 295 if err != nil { 296 return err 297 } 298 if !result.Exists { 299 panic("set item just set doesn't exist") 300 } 301 302 s.Add(result.Value) 303 } 304 305 value = s 306 } 307 308 for code, elem := range value.(*Set).m { 309 codeStr := strconv.FormatInt(int64(code), 10) 310 if err := w.set(append(addrCopy, codeStr), elem); err != nil { 311 return err 312 } 313 } 314 315 w.result[k+".#"] = strconv.Itoa(value.(*Set).Len()) 316 return nil 317 }