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