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