summaryrefslogtreecommitdiff
path: root/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go319
1 files changed, 319 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go
new file mode 100644
index 00000000..689ed8d1
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go
@@ -0,0 +1,319 @@
+package schema
+
+import (
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/mitchellh/mapstructure"
+)
+
+// MapFieldWriter writes data into a single map[string]string structure.
+type MapFieldWriter struct {
+ Schema map[string]*Schema
+
+ lock sync.Mutex
+ result map[string]string
+}
+
+// Map returns the underlying map that is being written to.
+func (w *MapFieldWriter) Map() map[string]string {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+ if w.result == nil {
+ w.result = make(map[string]string)
+ }
+
+ return w.result
+}
+
+func (w *MapFieldWriter) unsafeWriteField(addr string, value string) {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+ if w.result == nil {
+ w.result = make(map[string]string)
+ }
+
+ w.result[addr] = value
+}
+
+func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+ if w.result == nil {
+ w.result = make(map[string]string)
+ }
+
+ schemaList := addrToSchema(addr, w.Schema)
+ if len(schemaList) == 0 {
+ return fmt.Errorf("Invalid address to set: %#v", addr)
+ }
+
+ // If we're setting anything other than a list root or set root,
+ // then disallow it.
+ for _, schema := range schemaList[:len(schemaList)-1] {
+ if schema.Type == TypeList {
+ return fmt.Errorf(
+ "%s: can only set full list",
+ strings.Join(addr, "."))
+ }
+
+ if schema.Type == TypeMap {
+ return fmt.Errorf(
+ "%s: can only set full map",
+ strings.Join(addr, "."))
+ }
+
+ if schema.Type == TypeSet {
+ return fmt.Errorf(
+ "%s: can only set full set",
+ strings.Join(addr, "."))
+ }
+ }
+
+ return w.set(addr, value)
+}
+
+func (w *MapFieldWriter) set(addr []string, value interface{}) error {
+ schemaList := addrToSchema(addr, w.Schema)
+ if len(schemaList) == 0 {
+ return fmt.Errorf("Invalid address to set: %#v", addr)
+ }
+
+ schema := schemaList[len(schemaList)-1]
+ switch schema.Type {
+ case TypeBool, TypeInt, TypeFloat, TypeString:
+ return w.setPrimitive(addr, value, schema)
+ case TypeList:
+ return w.setList(addr, value, schema)
+ case TypeMap:
+ return w.setMap(addr, value, schema)
+ case TypeSet:
+ return w.setSet(addr, value, schema)
+ case typeObject:
+ return w.setObject(addr, value, schema)
+ default:
+ panic(fmt.Sprintf("Unknown type: %#v", schema.Type))
+ }
+}
+
+func (w *MapFieldWriter) setList(
+ addr []string,
+ v interface{},
+ schema *Schema) error {
+ k := strings.Join(addr, ".")
+ setElement := func(idx string, value interface{}) error {
+ addrCopy := make([]string, len(addr), len(addr)+1)
+ copy(addrCopy, addr)
+ return w.set(append(addrCopy, idx), value)
+ }
+
+ var vs []interface{}
+ if err := mapstructure.Decode(v, &vs); err != nil {
+ return fmt.Errorf("%s: %s", k, err)
+ }
+
+ // Set the entire list.
+ var err error
+ for i, elem := range vs {
+ is := strconv.FormatInt(int64(i), 10)
+ err = setElement(is, elem)
+ if err != nil {
+ break
+ }
+ }
+ if err != nil {
+ for i, _ := range vs {
+ is := strconv.FormatInt(int64(i), 10)
+ setElement(is, nil)
+ }
+
+ return err
+ }
+
+ w.result[k+".#"] = strconv.FormatInt(int64(len(vs)), 10)
+ return nil
+}
+
+func (w *MapFieldWriter) setMap(
+ addr []string,
+ value interface{},
+ schema *Schema) error {
+ k := strings.Join(addr, ".")
+ v := reflect.ValueOf(value)
+ vs := make(map[string]interface{})
+
+ if value == nil {
+ // The empty string here means the map is removed.
+ w.result[k] = ""
+ return nil
+ }
+
+ if v.Kind() != reflect.Map {
+ return fmt.Errorf("%s: must be a map", k)
+ }
+ if v.Type().Key().Kind() != reflect.String {
+ return fmt.Errorf("%s: keys must strings", k)
+ }
+ for _, mk := range v.MapKeys() {
+ mv := v.MapIndex(mk)
+ vs[mk.String()] = mv.Interface()
+ }
+
+ // Remove the pure key since we're setting the full map value
+ delete(w.result, k)
+
+ // Set each subkey
+ addrCopy := make([]string, len(addr), len(addr)+1)
+ copy(addrCopy, addr)
+ for subKey, v := range vs {
+ if err := w.set(append(addrCopy, subKey), v); err != nil {
+ return err
+ }
+ }
+
+ // Set the count
+ w.result[k+".%"] = strconv.Itoa(len(vs))
+
+ return nil
+}
+
+func (w *MapFieldWriter) setObject(
+ addr []string,
+ value interface{},
+ schema *Schema) error {
+ // Set the entire object. First decode into a proper structure
+ var v map[string]interface{}
+ if err := mapstructure.Decode(value, &v); err != nil {
+ return fmt.Errorf("%s: %s", strings.Join(addr, "."), err)
+ }
+
+ // Make space for additional elements in the address
+ addrCopy := make([]string, len(addr), len(addr)+1)
+ copy(addrCopy, addr)
+
+ // Set each element in turn
+ var err error
+ for k1, v1 := range v {
+ if err = w.set(append(addrCopy, k1), v1); err != nil {
+ break
+ }
+ }
+ if err != nil {
+ for k1, _ := range v {
+ w.set(append(addrCopy, k1), nil)
+ }
+ }
+
+ return err
+}
+
+func (w *MapFieldWriter) setPrimitive(
+ addr []string,
+ v interface{},
+ schema *Schema) error {
+ k := strings.Join(addr, ".")
+
+ if v == nil {
+ // The empty string here means the value is removed.
+ w.result[k] = ""
+ return nil
+ }
+
+ var set string
+ switch schema.Type {
+ case TypeBool:
+ var b bool
+ if err := mapstructure.Decode(v, &b); err != nil {
+ return fmt.Errorf("%s: %s", k, err)
+ }
+
+ set = strconv.FormatBool(b)
+ case TypeString:
+ if err := mapstructure.Decode(v, &set); err != nil {
+ return fmt.Errorf("%s: %s", k, err)
+ }
+ case TypeInt:
+ var n int
+ if err := mapstructure.Decode(v, &n); err != nil {
+ return fmt.Errorf("%s: %s", k, err)
+ }
+ set = strconv.FormatInt(int64(n), 10)
+ case TypeFloat:
+ var n float64
+ if err := mapstructure.Decode(v, &n); err != nil {
+ return fmt.Errorf("%s: %s", k, err)
+ }
+ set = strconv.FormatFloat(float64(n), 'G', -1, 64)
+ default:
+ return fmt.Errorf("Unknown type: %#v", schema.Type)
+ }
+
+ w.result[k] = set
+ return nil
+}
+
+func (w *MapFieldWriter) setSet(
+ addr []string,
+ value interface{},
+ schema *Schema) error {
+ addrCopy := make([]string, len(addr), len(addr)+1)
+ copy(addrCopy, addr)
+ k := strings.Join(addr, ".")
+
+ if value == nil {
+ w.result[k+".#"] = "0"
+ return nil
+ }
+
+ // If it is a slice, then we have to turn it into a *Set so that
+ // we get the proper order back based on the hash code.
+ if v := reflect.ValueOf(value); v.Kind() == reflect.Slice {
+ // Build a temp *ResourceData to use for the conversion
+ tempSchema := *schema
+ tempSchema.Type = TypeList
+ tempSchemaMap := map[string]*Schema{addr[0]: &tempSchema}
+ tempW := &MapFieldWriter{Schema: tempSchemaMap}
+
+ // Set the entire list, this lets us get sane values out of it
+ if err := tempW.WriteField(addr, value); err != nil {
+ return err
+ }
+
+ // Build the set by going over the list items in order and
+ // hashing them into the set. The reason we go over the list and
+ // not the `value` directly is because this forces all types
+ // to become []interface{} (generic) instead of []string, which
+ // most hash functions are expecting.
+ s := schema.ZeroValue().(*Set)
+ tempR := &MapFieldReader{
+ Map: BasicMapReader(tempW.Map()),
+ Schema: tempSchemaMap,
+ }
+ for i := 0; i < v.Len(); i++ {
+ is := strconv.FormatInt(int64(i), 10)
+ result, err := tempR.ReadField(append(addrCopy, is))
+ if err != nil {
+ return err
+ }
+ if !result.Exists {
+ panic("set item just set doesn't exist")
+ }
+
+ s.Add(result.Value)
+ }
+
+ value = s
+ }
+
+ for code, elem := range value.(*Set).m {
+ if err := w.set(append(addrCopy, code), elem); err != nil {
+ return err
+ }
+ }
+
+ w.result[k+".#"] = strconv.Itoa(value.(*Set).Len())
+ return nil
+}