unused
This commit is contained in:
parent
276dfe634f
commit
4ad7c41ab4
|
@ -1,152 +0,0 @@
|
|||
package flatmap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/configs/hcl2shim"
|
||||
)
|
||||
|
||||
// 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 == hcl2shim.UnknownVariableValue {
|
||||
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{}{}
|
||||
}
|
||||
|
||||
// NOTE: "num" is not necessarily accurate, e.g. if a user tampers
|
||||
// with state, so the following code should not crash when given a
|
||||
// number of items more or less than what's given in num. The
|
||||
// num key is mainly just a hint that this is a list or set.
|
||||
|
||||
// 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{}, len(keysList))
|
||||
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 proceed 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
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
package flatmap
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/configs/hcl2shim"
|
||||
)
|
||||
|
||||
func TestExpand(t *testing.T) {
|
||||
cases := []struct {
|
||||
Map map[string]string
|
||||
Key string
|
||||
Output interface{}
|
||||
}{
|
||||
{
|
||||
Map: map[string]string{
|
||||
"foo": "bar",
|
||||
"bar": "baz",
|
||||
},
|
||||
Key: "foo",
|
||||
Output: "bar",
|
||||
},
|
||||
|
||||
{
|
||||
Map: map[string]string{
|
||||
"foo.#": "2",
|
||||
"foo.0": "one",
|
||||
"foo.1": "two",
|
||||
},
|
||||
Key: "foo",
|
||||
Output: []interface{}{
|
||||
"one",
|
||||
"two",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Map: map[string]string{
|
||||
// # mismatches actual number of keys; actual number should
|
||||
// "win" here, since the # is just a hint that this is a list.
|
||||
"foo.#": "1",
|
||||
"foo.0": "one",
|
||||
"foo.1": "two",
|
||||
"foo.2": "three",
|
||||
},
|
||||
Key: "foo",
|
||||
Output: []interface{}{
|
||||
"one",
|
||||
"two",
|
||||
"three",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Map: map[string]string{
|
||||
// # mismatches actual number of keys; actual number should
|
||||
// "win" here, since the # is just a hint that this is a list.
|
||||
"foo.#": "5",
|
||||
"foo.0": "one",
|
||||
"foo.1": "two",
|
||||
"foo.2": "three",
|
||||
},
|
||||
Key: "foo",
|
||||
Output: []interface{}{
|
||||
"one",
|
||||
"two",
|
||||
"three",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Map: map[string]string{
|
||||
"foo.#": "1",
|
||||
"foo.0.name": "bar",
|
||||
"foo.0.port": "3000",
|
||||
"foo.0.enabled": "true",
|
||||
},
|
||||
Key: "foo",
|
||||
Output: []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "bar",
|
||||
"port": "3000",
|
||||
"enabled": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Map: map[string]string{
|
||||
"foo.#": "1",
|
||||
"foo.0.name": "bar",
|
||||
"foo.0.ports.#": "2",
|
||||
"foo.0.ports.0": "1",
|
||||
"foo.0.ports.1": "2",
|
||||
},
|
||||
Key: "foo",
|
||||
Output: []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "bar",
|
||||
"ports": []interface{}{
|
||||
"1",
|
||||
"2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Map: map[string]string{
|
||||
"list_of_map.#": "2",
|
||||
"list_of_map.0.%": "1",
|
||||
"list_of_map.0.a": "1",
|
||||
"list_of_map.1.%": "2",
|
||||
"list_of_map.1.b": "2",
|
||||
"list_of_map.1.c": "3",
|
||||
},
|
||||
Key: "list_of_map",
|
||||
Output: []interface{}{
|
||||
map[string]interface{}{
|
||||
"a": "1",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"b": "2",
|
||||
"c": "3",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Map: map[string]string{
|
||||
"map_of_list.%": "2",
|
||||
"map_of_list.list2.#": "1",
|
||||
"map_of_list.list2.0": "c",
|
||||
"map_of_list.list1.#": "2",
|
||||
"map_of_list.list1.0": "a",
|
||||
"map_of_list.list1.1": "b",
|
||||
},
|
||||
Key: "map_of_list",
|
||||
Output: map[string]interface{}{
|
||||
"list1": []interface{}{"a", "b"},
|
||||
"list2": []interface{}{"c"},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Map: map[string]string{
|
||||
"set.#": "3",
|
||||
"set.1234": "a",
|
||||
"set.1235": "b",
|
||||
"set.1236": "c",
|
||||
},
|
||||
Key: "set",
|
||||
Output: []interface{}{"a", "b", "c"},
|
||||
},
|
||||
|
||||
{
|
||||
Map: map[string]string{
|
||||
"computed_set.#": "1",
|
||||
"computed_set.~1234.a": "a",
|
||||
"computed_set.~1234.b": "b",
|
||||
"computed_set.~1234.c": "c",
|
||||
},
|
||||
Key: "computed_set",
|
||||
Output: []interface{}{
|
||||
map[string]interface{}{"a": "a", "b": "b", "c": "c"},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Map: map[string]string{
|
||||
"struct.#": "1",
|
||||
"struct.0.name": "hello",
|
||||
"struct.0.rules.#": hcl2shim.UnknownVariableValue,
|
||||
},
|
||||
Key: "struct",
|
||||
Output: []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "hello",
|
||||
"rules": hcl2shim.UnknownVariableValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Map: map[string]string{
|
||||
"struct.#": "1",
|
||||
"struct.0.name": "hello",
|
||||
"struct.0.set.#": "0",
|
||||
"struct.0.set.0.key": "value",
|
||||
},
|
||||
Key: "struct",
|
||||
Output: []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "hello",
|
||||
"set": []interface{}{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Map: map[string]string{
|
||||
"empty_map_of_sets.%": "0",
|
||||
"empty_map_of_sets.set1.#": "0",
|
||||
"empty_map_of_sets.set1.1234": "x",
|
||||
},
|
||||
Key: "empty_map_of_sets",
|
||||
Output: map[string]interface{}{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.Key, func(t *testing.T) {
|
||||
actual := Expand(tc.Map, tc.Key)
|
||||
if !reflect.DeepEqual(actual, tc.Output) {
|
||||
t.Errorf(
|
||||
"Key: %v\nMap:\n\n%#v\n\nOutput:\n\n%#v\n\nExpected:\n\n%#v\n",
|
||||
tc.Key,
|
||||
tc.Map,
|
||||
actual,
|
||||
tc.Output)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
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))
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
package flatmap
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFlatten(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input map[string]interface{}
|
||||
Output map[string]string
|
||||
}{
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"bar": "baz",
|
||||
},
|
||||
Output: map[string]string{
|
||||
"foo": "bar",
|
||||
"bar": "baz",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"foo": []string{
|
||||
"one",
|
||||
"two",
|
||||
},
|
||||
},
|
||||
Output: map[string]string{
|
||||
"foo.#": "2",
|
||||
"foo.0": "one",
|
||||
"foo.1": "two",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"foo": []map[interface{}]interface{}{
|
||||
map[interface{}]interface{}{
|
||||
"name": "bar",
|
||||
"port": 3000,
|
||||
"enabled": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Output: map[string]string{
|
||||
"foo.#": "1",
|
||||
"foo.0.name": "bar",
|
||||
"foo.0.port": "3000",
|
||||
"foo.0.enabled": "true",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"foo": []map[interface{}]interface{}{
|
||||
map[interface{}]interface{}{
|
||||
"name": "bar",
|
||||
"ports": []string{
|
||||
"1",
|
||||
"2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Output: map[string]string{
|
||||
"foo.#": "1",
|
||||
"foo.0.name": "bar",
|
||||
"foo.0.ports.#": "2",
|
||||
"foo.0.ports.0": "1",
|
||||
"foo.0.ports.1": "2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
actual := Flatten(tc.Input)
|
||||
if !reflect.DeepEqual(actual, Map(tc.Output)) {
|
||||
t.Fatalf(
|
||||
"Input:\n\n%#v\n\nOutput:\n\n%#v\n\nExpected:\n\n%#v\n",
|
||||
tc.Input,
|
||||
actual,
|
||||
tc.Output)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
package flatmap
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMapContains(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input map[string]string
|
||||
Key string
|
||||
Result bool
|
||||
}{
|
||||
{
|
||||
Input: map[string]string{
|
||||
"foo": "bar",
|
||||
"bar": "nope",
|
||||
},
|
||||
Key: "foo",
|
||||
Result: true,
|
||||
},
|
||||
|
||||
{
|
||||
Input: map[string]string{
|
||||
"foo": "bar",
|
||||
"bar": "nope",
|
||||
},
|
||||
Key: "baz",
|
||||
Result: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
actual := Map(tc.Input).Contains(tc.Key)
|
||||
if actual != tc.Result {
|
||||
t.Fatalf("case %d bad: %#v", i, tc.Input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDelete(t *testing.T) {
|
||||
m := Flatten(map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"routes": []map[string]string{
|
||||
map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
m.Delete("routes")
|
||||
|
||||
expected := Map(map[string]string{"foo": "bar"})
|
||||
if !reflect.DeepEqual(m, expected) {
|
||||
t.Fatalf("bad: %#v", m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapKeys(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input map[string]string
|
||||
Output []string
|
||||
}{
|
||||
{
|
||||
Input: map[string]string{
|
||||
"foo": "bar",
|
||||
"bar.#": "bar",
|
||||
"bar.0.foo": "bar",
|
||||
"bar.0.baz": "bar",
|
||||
},
|
||||
Output: []string{
|
||||
"bar",
|
||||
"foo",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
actual := Map(tc.Input).Keys()
|
||||
|
||||
// Sort so we have a consistent view of the output
|
||||
sort.Strings(actual)
|
||||
|
||||
if !reflect.DeepEqual(actual, tc.Output) {
|
||||
t.Fatalf("input: %#v\n\nbad: %#v", tc.Input, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapMerge(t *testing.T) {
|
||||
cases := []struct {
|
||||
One map[string]string
|
||||
Two map[string]string
|
||||
Result map[string]string
|
||||
}{
|
||||
{
|
||||
One: map[string]string{
|
||||
"foo": "bar",
|
||||
"bar": "nope",
|
||||
},
|
||||
Two: map[string]string{
|
||||
"bar": "baz",
|
||||
"baz": "buz",
|
||||
},
|
||||
Result: map[string]string{
|
||||
"foo": "bar",
|
||||
"bar": "baz",
|
||||
"baz": "buz",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
Map(tc.One).Merge(Map(tc.Two))
|
||||
if !reflect.DeepEqual(tc.One, tc.Result) {
|
||||
t.Fatalf("case %d bad: %#v", i, tc.One)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue