summaryrefslogtreecommitdiff
path: root/vendor/github.com/hashicorp/terraform/flatmap
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/flatmap')
-rw-r--r--vendor/github.com/hashicorp/terraform/flatmap/expand.go147
-rw-r--r--vendor/github.com/hashicorp/terraform/flatmap/flatten.go71
-rw-r--r--vendor/github.com/hashicorp/terraform/flatmap/map.go82
3 files changed, 300 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/flatmap/expand.go b/vendor/github.com/hashicorp/terraform/flatmap/expand.go
new file mode 100644
index 00000000..2bfb3fe3
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/flatmap/expand.go
@@ -0,0 +1,147 @@
+package flatmap
+
+import (
+ "fmt"
+ "sort"
+ "strconv"
+ "strings"
+
+ "github.com/hashicorp/hil"
+)
+
+// Expand takes a map and a key (prefix) and expands that value into
+// a more complex structure. This is the reverse of the Flatten operation.
+func Expand(m map[string]string, key string) interface{} {
+ // If the key is exactly a key in the map, just return it
+ if v, ok := m[key]; ok {
+ if v == "true" {
+ return true
+ } else if v == "false" {
+ return false
+ }
+
+ return v
+ }
+
+ // Check if the key is an array, and if so, expand the array
+ if v, ok := m[key+".#"]; ok {
+ // If the count of the key is unknown, then just put the unknown
+ // value in the value itself. This will be detected by Terraform
+ // core later.
+ if v == hil.UnknownValue {
+ return v
+ }
+
+ return expandArray(m, key)
+ }
+
+ // Check if this is a prefix in the map
+ prefix := key + "."
+ for k := range m {
+ if strings.HasPrefix(k, prefix) {
+ return expandMap(m, prefix)
+ }
+ }
+
+ return nil
+}
+
+func expandArray(m map[string]string, prefix string) []interface{} {
+ num, err := strconv.ParseInt(m[prefix+".#"], 0, 0)
+ if err != nil {
+ panic(err)
+ }
+
+ // If the number of elements in this array is 0, then return an
+ // empty slice as there is nothing to expand. Trying to expand it
+ // anyway could lead to crashes as any child maps, arrays or sets
+ // that no longer exist are still shown as empty with a count of 0.
+ if num == 0 {
+ return []interface{}{}
+ }
+
+ // The Schema "Set" type stores its values in an array format, but
+ // using numeric hash values instead of ordinal keys. Take the set
+ // of keys regardless of value, and expand them in numeric order.
+ // See GH-11042 for more details.
+ keySet := map[int]bool{}
+ computed := map[string]bool{}
+ for k := range m {
+ if !strings.HasPrefix(k, prefix+".") {
+ continue
+ }
+
+ key := k[len(prefix)+1:]
+ idx := strings.Index(key, ".")
+ if idx != -1 {
+ key = key[:idx]
+ }
+
+ // skip the count value
+ if key == "#" {
+ continue
+ }
+
+ // strip the computed flag if there is one
+ if strings.HasPrefix(key, "~") {
+ key = key[1:]
+ computed[key] = true
+ }
+
+ k, err := strconv.Atoi(key)
+ if err != nil {
+ panic(err)
+ }
+ keySet[int(k)] = true
+ }
+
+ keysList := make([]int, 0, num)
+ for key := range keySet {
+ keysList = append(keysList, key)
+ }
+ sort.Ints(keysList)
+
+ result := make([]interface{}, num)
+ for i, key := range keysList {
+ keyString := strconv.Itoa(key)
+ if computed[keyString] {
+ keyString = "~" + keyString
+ }
+ result[i] = Expand(m, fmt.Sprintf("%s.%s", prefix, keyString))
+ }
+
+ return result
+}
+
+func expandMap(m map[string]string, prefix string) map[string]interface{} {
+ // Submaps may not have a '%' key, so we can't count on this value being
+ // here. If we don't have a count, just procede as if we have have a map.
+ if count, ok := m[prefix+"%"]; ok && count == "0" {
+ return map[string]interface{}{}
+ }
+
+ result := make(map[string]interface{})
+ for k := range m {
+ if !strings.HasPrefix(k, prefix) {
+ continue
+ }
+
+ key := k[len(prefix):]
+ idx := strings.Index(key, ".")
+ if idx != -1 {
+ key = key[:idx]
+ }
+ if _, ok := result[key]; ok {
+ continue
+ }
+
+ // skip the map count value
+ if key == "%" {
+ continue
+ }
+
+ result[key] = Expand(m, k[:len(prefix)+len(key)])
+ }
+
+ return result
+}
diff --git a/vendor/github.com/hashicorp/terraform/flatmap/flatten.go b/vendor/github.com/hashicorp/terraform/flatmap/flatten.go
new file mode 100644
index 00000000..9ff6e426
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/flatmap/flatten.go
@@ -0,0 +1,71 @@
+package flatmap
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// Flatten takes a structure and turns into a flat map[string]string.
+//
+// Within the "thing" parameter, only primitive values are allowed. Structs are
+// not supported. Therefore, it can only be slices, maps, primitives, and
+// any combination of those together.
+//
+// See the tests for examples of what inputs are turned into.
+func Flatten(thing map[string]interface{}) Map {
+ result := make(map[string]string)
+
+ for k, raw := range thing {
+ flatten(result, k, reflect.ValueOf(raw))
+ }
+
+ return Map(result)
+}
+
+func flatten(result map[string]string, prefix string, v reflect.Value) {
+ if v.Kind() == reflect.Interface {
+ v = v.Elem()
+ }
+
+ switch v.Kind() {
+ case reflect.Bool:
+ if v.Bool() {
+ result[prefix] = "true"
+ } else {
+ result[prefix] = "false"
+ }
+ case reflect.Int:
+ result[prefix] = fmt.Sprintf("%d", v.Int())
+ case reflect.Map:
+ flattenMap(result, prefix, v)
+ case reflect.Slice:
+ flattenSlice(result, prefix, v)
+ case reflect.String:
+ result[prefix] = v.String()
+ default:
+ panic(fmt.Sprintf("Unknown: %s", v))
+ }
+}
+
+func flattenMap(result map[string]string, prefix string, v reflect.Value) {
+ for _, k := range v.MapKeys() {
+ if k.Kind() == reflect.Interface {
+ k = k.Elem()
+ }
+
+ if k.Kind() != reflect.String {
+ panic(fmt.Sprintf("%s: map key is not string: %s", prefix, k))
+ }
+
+ flatten(result, fmt.Sprintf("%s.%s", prefix, k.String()), v.MapIndex(k))
+ }
+}
+
+func flattenSlice(result map[string]string, prefix string, v reflect.Value) {
+ prefix = prefix + "."
+
+ result[prefix+"#"] = fmt.Sprintf("%d", v.Len())
+ for i := 0; i < v.Len(); i++ {
+ flatten(result, fmt.Sprintf("%s%d", prefix, i), v.Index(i))
+ }
+}
diff --git a/vendor/github.com/hashicorp/terraform/flatmap/map.go b/vendor/github.com/hashicorp/terraform/flatmap/map.go
new file mode 100644
index 00000000..46b72c40
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/flatmap/map.go
@@ -0,0 +1,82 @@
+package flatmap
+
+import (
+ "strings"
+)
+
+// Map is a wrapper around map[string]string that provides some helpers
+// above it that assume the map is in the format that flatmap expects
+// (the result of Flatten).
+//
+// All modifying functions such as Delete are done in-place unless
+// otherwise noted.
+type Map map[string]string
+
+// Contains returns true if the map contains the given key.
+func (m Map) Contains(key string) bool {
+ for _, k := range m.Keys() {
+ if k == key {
+ return true
+ }
+ }
+
+ return false
+}
+
+// Delete deletes a key out of the map with the given prefix.
+func (m Map) Delete(prefix string) {
+ for k, _ := range m {
+ match := k == prefix
+ if !match {
+ if !strings.HasPrefix(k, prefix) {
+ continue
+ }
+
+ if k[len(prefix):len(prefix)+1] != "." {
+ continue
+ }
+ }
+
+ delete(m, k)
+ }
+}
+
+// Keys returns all of the top-level keys in this map
+func (m Map) Keys() []string {
+ ks := make(map[string]struct{})
+ for k, _ := range m {
+ idx := strings.Index(k, ".")
+ if idx == -1 {
+ idx = len(k)
+ }
+
+ ks[k[:idx]] = struct{}{}
+ }
+
+ result := make([]string, 0, len(ks))
+ for k, _ := range ks {
+ result = append(result, k)
+ }
+
+ return result
+}
+
+// Merge merges the contents of the other Map into this one.
+//
+// This merge is smarter than a simple map iteration because it
+// will fully replace arrays and other complex structures that
+// are present in this map with the other map's. For example, if
+// this map has a 3 element "foo" list, and m2 has a 2 element "foo"
+// list, then the result will be that m has a 2 element "foo"
+// list.
+func (m Map) Merge(m2 Map) {
+ for _, prefix := range m2.Keys() {
+ m.Delete(prefix)
+
+ for k, v := range m2 {
+ if strings.HasPrefix(k, prefix) {
+ m[k] = v
+ }
+ }
+ }
+}