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