github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/legacy/helper/schema/field_writer_map.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package schema 5 6 import ( 7 "fmt" 8 "reflect" 9 "strconv" 10 "strings" 11 "sync" 12 13 "github.com/mitchellh/mapstructure" 14 ) 15 16 // MapFieldWriter writes data into a single map[string]string structure. 17 type MapFieldWriter struct { 18 Schema map[string]*Schema 19 20 lock sync.Mutex 21 result map[string]string 22 } 23 24 // Map returns the underlying map that is being written to. 25 func (w *MapFieldWriter) Map() map[string]string { 26 w.lock.Lock() 27 defer w.lock.Unlock() 28 if w.result == nil { 29 w.result = make(map[string]string) 30 } 31 32 return w.result 33 } 34 35 func (w *MapFieldWriter) unsafeWriteField(addr string, value string) { 36 w.lock.Lock() 37 defer w.lock.Unlock() 38 if w.result == nil { 39 w.result = make(map[string]string) 40 } 41 42 w.result[addr] = value 43 } 44 45 // clearTree clears a field and any sub-fields of the given address out of the 46 // map. This should be used to reset some kind of complex structures (namely 47 // sets) before writing to make sure that any conflicting data is removed (for 48 // example, if the set was previously written to the writer's layer). 49 func (w *MapFieldWriter) clearTree(addr []string) { 50 prefix := strings.Join(addr, ".") + "." 51 for k := range w.result { 52 if strings.HasPrefix(k, prefix) { 53 delete(w.result, k) 54 } 55 } 56 } 57 58 func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error { 59 w.lock.Lock() 60 defer w.lock.Unlock() 61 if w.result == nil { 62 w.result = make(map[string]string) 63 } 64 65 schemaList := addrToSchema(addr, w.Schema) 66 if len(schemaList) == 0 { 67 return fmt.Errorf("Invalid address to set: %#v", addr) 68 } 69 70 // If we're setting anything other than a list root or set root, 71 // then disallow it. 72 for _, schema := range schemaList[:len(schemaList)-1] { 73 if schema.Type == TypeList { 74 return fmt.Errorf( 75 "%s: can only set full list", 76 strings.Join(addr, ".")) 77 } 78 79 if schema.Type == TypeMap { 80 return fmt.Errorf( 81 "%s: can only set full map", 82 strings.Join(addr, ".")) 83 } 84 85 if schema.Type == TypeSet { 86 return fmt.Errorf( 87 "%s: can only set full set", 88 strings.Join(addr, ".")) 89 } 90 } 91 92 return w.set(addr, value) 93 } 94 95 func (w *MapFieldWriter) set(addr []string, value interface{}) error { 96 schemaList := addrToSchema(addr, w.Schema) 97 if len(schemaList) == 0 { 98 return fmt.Errorf("Invalid address to set: %#v", addr) 99 } 100 101 schema := schemaList[len(schemaList)-1] 102 switch schema.Type { 103 case TypeBool, TypeInt, TypeFloat, TypeString: 104 return w.setPrimitive(addr, value, schema) 105 case TypeList: 106 return w.setList(addr, value, schema) 107 case TypeMap: 108 return w.setMap(addr, value, schema) 109 case TypeSet: 110 return w.setSet(addr, value, schema) 111 case typeObject: 112 return w.setObject(addr, value, schema) 113 default: 114 panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) 115 } 116 } 117 118 func (w *MapFieldWriter) setList( 119 addr []string, 120 v interface{}, 121 schema *Schema) error { 122 k := strings.Join(addr, ".") 123 setElement := func(idx string, value interface{}) error { 124 addrCopy := make([]string, len(addr), len(addr)+1) 125 copy(addrCopy, addr) 126 return w.set(append(addrCopy, idx), value) 127 } 128 129 var vs []interface{} 130 if err := mapstructure.Decode(v, &vs); err != nil { 131 return fmt.Errorf("%s: %s", k, err) 132 } 133 134 // Wipe the set from the current writer prior to writing if it exists. 135 // Multiple writes to the same layer is a lot safer for lists than sets due 136 // to the fact that indexes are always deterministic and the length will 137 // always be updated with the current length on the last write, but making 138 // sure we have a clean namespace removes any chance for edge cases to pop up 139 // and ensures that the last write to the set is the correct value. 140 w.clearTree(addr) 141 142 // Set the entire list. 143 var err error 144 for i, elem := range vs { 145 is := strconv.FormatInt(int64(i), 10) 146 err = setElement(is, elem) 147 if err != nil { 148 break 149 } 150 } 151 if err != nil { 152 for i, _ := range vs { 153 is := strconv.FormatInt(int64(i), 10) 154 setElement(is, nil) 155 } 156 157 return err 158 } 159 160 w.result[k+".#"] = strconv.FormatInt(int64(len(vs)), 10) 161 return nil 162 } 163 164 func (w *MapFieldWriter) setMap( 165 addr []string, 166 value interface{}, 167 schema *Schema) error { 168 k := strings.Join(addr, ".") 169 v := reflect.ValueOf(value) 170 vs := make(map[string]interface{}) 171 172 if value == nil { 173 // The empty string here means the map is removed. 174 w.result[k] = "" 175 return nil 176 } 177 178 if v.Kind() != reflect.Map { 179 return fmt.Errorf("%s: must be a map", k) 180 } 181 if v.Type().Key().Kind() != reflect.String { 182 return fmt.Errorf("%s: keys must strings", k) 183 } 184 for _, mk := range v.MapKeys() { 185 mv := v.MapIndex(mk) 186 vs[mk.String()] = mv.Interface() 187 } 188 189 // Wipe this address tree. The contents of the map should always reflect the 190 // last write made to it. 191 w.clearTree(addr) 192 193 // Remove the pure key since we're setting the full map value 194 delete(w.result, k) 195 196 // Set each subkey 197 addrCopy := make([]string, len(addr), len(addr)+1) 198 copy(addrCopy, addr) 199 for subKey, v := range vs { 200 if err := w.set(append(addrCopy, subKey), v); err != nil { 201 return err 202 } 203 } 204 205 // Set the count 206 w.result[k+".%"] = strconv.Itoa(len(vs)) 207 208 return nil 209 } 210 211 func (w *MapFieldWriter) setObject( 212 addr []string, 213 value interface{}, 214 schema *Schema) error { 215 // Set the entire object. First decode into a proper structure 216 var v map[string]interface{} 217 if err := mapstructure.Decode(value, &v); err != nil { 218 return fmt.Errorf("%s: %s", strings.Join(addr, "."), err) 219 } 220 221 // Make space for additional elements in the address 222 addrCopy := make([]string, len(addr), len(addr)+1) 223 copy(addrCopy, addr) 224 225 // Set each element in turn 226 var err error 227 for k1, v1 := range v { 228 if err = w.set(append(addrCopy, k1), v1); err != nil { 229 break 230 } 231 } 232 if err != nil { 233 for k1, _ := range v { 234 w.set(append(addrCopy, k1), nil) 235 } 236 } 237 238 return err 239 } 240 241 func (w *MapFieldWriter) setPrimitive( 242 addr []string, 243 v interface{}, 244 schema *Schema) error { 245 k := strings.Join(addr, ".") 246 247 if v == nil { 248 // The empty string here means the value is removed. 249 w.result[k] = "" 250 return nil 251 } 252 253 var set string 254 switch schema.Type { 255 case TypeBool: 256 var b bool 257 if err := mapstructure.Decode(v, &b); err != nil { 258 return fmt.Errorf("%s: %s", k, err) 259 } 260 261 set = strconv.FormatBool(b) 262 case TypeString: 263 if err := mapstructure.Decode(v, &set); err != nil { 264 return fmt.Errorf("%s: %s", k, err) 265 } 266 case TypeInt: 267 var n int 268 if err := mapstructure.Decode(v, &n); err != nil { 269 return fmt.Errorf("%s: %s", k, err) 270 } 271 set = strconv.FormatInt(int64(n), 10) 272 case TypeFloat: 273 var n float64 274 if err := mapstructure.Decode(v, &n); err != nil { 275 return fmt.Errorf("%s: %s", k, err) 276 } 277 set = strconv.FormatFloat(float64(n), 'G', -1, 64) 278 default: 279 return fmt.Errorf("Unknown type: %#v", schema.Type) 280 } 281 282 w.result[k] = set 283 return nil 284 } 285 286 func (w *MapFieldWriter) setSet( 287 addr []string, 288 value interface{}, 289 schema *Schema) error { 290 addrCopy := make([]string, len(addr), len(addr)+1) 291 copy(addrCopy, addr) 292 k := strings.Join(addr, ".") 293 294 if value == nil { 295 w.result[k+".#"] = "0" 296 return nil 297 } 298 299 // If it is a slice, then we have to turn it into a *Set so that 300 // we get the proper order back based on the hash code. 301 if v := reflect.ValueOf(value); v.Kind() == reflect.Slice { 302 // Build a temp *ResourceData to use for the conversion 303 tempAddr := addr[len(addr)-1:] 304 tempSchema := *schema 305 tempSchema.Type = TypeList 306 tempSchemaMap := map[string]*Schema{tempAddr[0]: &tempSchema} 307 tempW := &MapFieldWriter{Schema: tempSchemaMap} 308 309 // Set the entire list, this lets us get values out of it 310 if err := tempW.WriteField(tempAddr, value); err != nil { 311 return err 312 } 313 314 // Build the set by going over the list items in order and 315 // hashing them into the set. The reason we go over the list and 316 // not the `value` directly is because this forces all types 317 // to become []interface{} (generic) instead of []string, which 318 // most hash functions are expecting. 319 s := schema.ZeroValue().(*Set) 320 tempR := &MapFieldReader{ 321 Map: BasicMapReader(tempW.Map()), 322 Schema: tempSchemaMap, 323 } 324 for i := 0; i < v.Len(); i++ { 325 is := strconv.FormatInt(int64(i), 10) 326 result, err := tempR.ReadField(append(tempAddr, is)) 327 if err != nil { 328 return err 329 } 330 if !result.Exists { 331 panic("set item just set doesn't exist") 332 } 333 334 s.Add(result.Value) 335 } 336 337 value = s 338 } 339 340 // Clear any keys that match the set address first. This is necessary because 341 // it's always possible and sometimes may be necessary to write to a certain 342 // writer layer more than once with different set data each time, which will 343 // lead to different keys being inserted, which can lead to determinism 344 // problems when the old data isn't wiped first. 345 w.clearTree(addr) 346 347 if value.(*Set) == nil { 348 w.result[k+".#"] = "0" 349 return nil 350 } 351 352 for code, elem := range value.(*Set).m { 353 if err := w.set(append(addrCopy, code), elem); err != nil { 354 return err 355 } 356 } 357 358 w.result[k+".#"] = strconv.Itoa(value.(*Set).Len()) 359 return nil 360 }