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  }