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