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