forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/knadh/koanf/maps/maps.go

532 lines
8.7 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
// Package maps provides reusable functions for manipulating nested
2024-02-18 10:42:21 +00:00
// map[string]interface{} maps are common unmarshal products from
2024-02-18 10:42:21 +00:00
// various serializers such as json, yaml etc.
2024-02-18 10:42:21 +00:00
package maps
import (
"fmt"
"reflect"
"strings"
2024-06-14 08:41:36 +00:00
"github.com/mitchellh/copystructure"
2024-02-18 10:42:21 +00:00
)
// Flatten takes a map[string]interface{} and traverses it and flattens
2024-02-18 10:42:21 +00:00
// nested children into keys delimited by delim.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// It's important to note that all nested maps should be
2024-02-18 10:42:21 +00:00
// map[string]interface{} and not map[interface{}]interface{}.
2024-02-18 10:42:21 +00:00
// Use IntfaceKeysToStrings() to convert if necessary.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// eg: `{ "parent": { "child": 123 }}` becomes `{ "parent.child": 123 }`
2024-02-18 10:42:21 +00:00
// In addition, it keeps track of and returns a map of the delimited keypaths with
2024-02-18 10:42:21 +00:00
// a slice of key parts, for eg: { "parent.child": ["parent", "child"] }. This
2024-02-18 10:42:21 +00:00
// parts list is used to remember the key path's original structure to
2024-02-18 10:42:21 +00:00
// unflatten later.
2024-02-18 10:42:21 +00:00
func Flatten(m map[string]interface{}, keys []string, delim string) (map[string]interface{}, map[string][]string) {
2024-02-18 10:42:21 +00:00
var (
out = make(map[string]interface{})
2024-02-18 10:42:21 +00:00
keyMap = make(map[string][]string)
)
flatten(m, keys, delim, out, keyMap)
2024-02-18 10:42:21 +00:00
return out, keyMap
2024-02-18 10:42:21 +00:00
}
func flatten(m map[string]interface{}, keys []string, delim string, out map[string]interface{}, keyMap map[string][]string) {
2024-02-18 10:42:21 +00:00
for key, val := range m {
2024-02-18 10:42:21 +00:00
// Copy the incoming key paths into a fresh list
2024-02-18 10:42:21 +00:00
// and append the current key in the iteration.
2024-02-18 10:42:21 +00:00
kp := make([]string, 0, len(keys)+1)
2024-02-18 10:42:21 +00:00
kp = append(kp, keys...)
2024-02-18 10:42:21 +00:00
kp = append(kp, key)
switch cur := val.(type) {
2024-02-18 10:42:21 +00:00
case map[string]interface{}:
2024-02-18 10:42:21 +00:00
// Empty map.
2024-02-18 10:42:21 +00:00
if len(cur) == 0 {
2024-02-18 10:42:21 +00:00
newKey := strings.Join(kp, delim)
2024-02-18 10:42:21 +00:00
out[newKey] = val
2024-02-18 10:42:21 +00:00
keyMap[newKey] = kp
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
// It's a nested map. Flatten it recursively.
2024-02-18 10:42:21 +00:00
flatten(cur, kp, delim, out, keyMap)
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
newKey := strings.Join(kp, delim)
2024-02-18 10:42:21 +00:00
out[newKey] = val
2024-02-18 10:42:21 +00:00
keyMap[newKey] = kp
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// Unflatten takes a flattened key:value map (non-nested with delimited keys)
2024-02-18 10:42:21 +00:00
// and returns a nested map where the keys are split into hierarchies by the given
2024-02-18 10:42:21 +00:00
// delimiter. For instance, `parent.child.key: 1` to `{parent: {child: {key: 1}}}`
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// It's important to note that all nested maps should be
2024-02-18 10:42:21 +00:00
// map[string]interface{} and not map[interface{}]interface{}.
2024-02-18 10:42:21 +00:00
// Use IntfaceKeysToStrings() to convert if necessary.
2024-02-18 10:42:21 +00:00
func Unflatten(m map[string]interface{}, delim string) map[string]interface{} {
2024-02-18 10:42:21 +00:00
out := make(map[string]interface{})
// Iterate through the flat conf map.
2024-02-18 10:42:21 +00:00
for k, v := range m {
2024-02-18 10:42:21 +00:00
var (
keys = strings.Split(k, delim)
2024-02-18 10:42:21 +00:00
next = out
)
// Iterate through key parts, for eg:, parent.child.key
2024-02-18 10:42:21 +00:00
// will be ["parent", "child", "key"]
2024-02-18 10:42:21 +00:00
for _, k := range keys[:len(keys)-1] {
2024-02-18 10:42:21 +00:00
sub, ok := next[k]
2024-02-18 10:42:21 +00:00
if !ok {
2024-02-18 10:42:21 +00:00
// If the key does not exist in the map, create it.
2024-02-18 10:42:21 +00:00
sub = make(map[string]interface{})
2024-02-18 10:42:21 +00:00
next[k] = sub
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if n, ok := sub.(map[string]interface{}); ok {
2024-02-18 10:42:21 +00:00
next = n
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// Assign the value.
2024-02-18 10:42:21 +00:00
next[keys[len(keys)-1]] = v
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return out
2024-02-18 10:42:21 +00:00
}
// Merge recursively merges map a into b (left to right), mutating
2024-02-18 10:42:21 +00:00
// and expanding map b. Note that there's no copying involved, so
2024-02-18 10:42:21 +00:00
// map b will retain references to map a.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// It's important to note that all nested maps should be
2024-02-18 10:42:21 +00:00
// map[string]interface{} and not map[interface{}]interface{}.
2024-02-18 10:42:21 +00:00
// Use IntfaceKeysToStrings() to convert if necessary.
2024-02-18 10:42:21 +00:00
func Merge(a, b map[string]interface{}) {
2024-02-18 10:42:21 +00:00
for key, val := range a {
2024-02-18 10:42:21 +00:00
// Does the key exist in the target map?
2024-02-18 10:42:21 +00:00
// If no, add it and move on.
2024-02-18 10:42:21 +00:00
bVal, ok := b[key]
2024-02-18 10:42:21 +00:00
if !ok {
2024-02-18 10:42:21 +00:00
b[key] = val
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
// If the incoming val is not a map, do a direct merge.
2024-02-18 10:42:21 +00:00
if _, ok := val.(map[string]interface{}); !ok {
2024-02-18 10:42:21 +00:00
b[key] = val
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
// The source key and target keys are both maps. Merge them.
2024-02-18 10:42:21 +00:00
switch v := bVal.(type) {
2024-02-18 10:42:21 +00:00
case map[string]interface{}:
2024-02-18 10:42:21 +00:00
Merge(val.(map[string]interface{}), v)
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
b[key] = val
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// MergeStrict recursively merges map a into b (left to right), mutating
2024-02-18 10:42:21 +00:00
// and expanding map b. Note that there's no copying involved, so
2024-02-18 10:42:21 +00:00
// map b will retain references to map a.
2024-02-18 10:42:21 +00:00
// If an equal key in either of the maps has a different value type, it will return the first error.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// It's important to note that all nested maps should be
2024-02-18 10:42:21 +00:00
// map[string]interface{} and not map[interface{}]interface{}.
2024-02-18 10:42:21 +00:00
// Use IntfaceKeysToStrings() to convert if necessary.
2024-02-18 10:42:21 +00:00
func MergeStrict(a, b map[string]interface{}) error {
2024-02-18 10:42:21 +00:00
return mergeStrict(a, b, "")
2024-02-18 10:42:21 +00:00
}
func mergeStrict(a, b map[string]interface{}, fullKey string) error {
2024-02-18 10:42:21 +00:00
for key, val := range a {
2024-02-18 10:42:21 +00:00
// Does the key exist in the target map?
2024-02-18 10:42:21 +00:00
// If no, add it and move on.
2024-02-18 10:42:21 +00:00
bVal, ok := b[key]
2024-02-18 10:42:21 +00:00
if !ok {
2024-02-18 10:42:21 +00:00
b[key] = val
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
newFullKey := key
2024-02-18 10:42:21 +00:00
if fullKey != "" {
2024-02-18 10:42:21 +00:00
newFullKey = fmt.Sprintf("%v.%v", fullKey, key)
2024-02-18 10:42:21 +00:00
}
// If the incoming val is not a map, do a direct merge between the same types.
2024-02-18 10:42:21 +00:00
if _, ok := val.(map[string]interface{}); !ok {
2024-02-18 10:42:21 +00:00
if reflect.TypeOf(b[key]) == reflect.TypeOf(val) {
2024-02-18 10:42:21 +00:00
b[key] = val
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
return fmt.Errorf("incorrect types at key %v, type %T != %T", fullKey, b[key], val)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
// The source key and target keys are both maps. Merge them.
2024-02-18 10:42:21 +00:00
switch v := bVal.(type) {
2024-02-18 10:42:21 +00:00
case map[string]interface{}:
2024-02-18 10:42:21 +00:00
return mergeStrict(val.(map[string]interface{}), v, newFullKey)
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
b[key] = val
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
// Delete removes the entry present at a given path, from the map. The path
2024-02-18 10:42:21 +00:00
// is the key map slice, for eg:, parent.child.key -> [parent child key].
2024-02-18 10:42:21 +00:00
// Any empty, nested map on the path, is recursively deleted.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// It's important to note that all nested maps should be
2024-02-18 10:42:21 +00:00
// map[string]interface{} and not map[interface{}]interface{}.
2024-02-18 10:42:21 +00:00
// Use IntfaceKeysToStrings() to convert if necessary.
2024-02-18 10:42:21 +00:00
func Delete(mp map[string]interface{}, path []string) {
2024-02-18 10:42:21 +00:00
next, ok := mp[path[0]]
2024-02-18 10:42:21 +00:00
if ok {
2024-02-18 10:42:21 +00:00
if len(path) == 1 {
2024-02-18 10:42:21 +00:00
delete(mp, path[0])
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
switch nval := next.(type) {
2024-02-18 10:42:21 +00:00
case map[string]interface{}:
2024-02-18 10:42:21 +00:00
Delete(nval, path[1:])
2024-02-18 10:42:21 +00:00
// Delete map if it has no keys.
2024-02-18 10:42:21 +00:00
if len(nval) == 0 {
2024-02-18 10:42:21 +00:00
delete(mp, path[0])
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// Search recursively searches a map for a given path. The path is
2024-02-18 10:42:21 +00:00
// the key map slice, for eg:, parent.child.key -> [parent child key].
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// It's important to note that all nested maps should be
2024-02-18 10:42:21 +00:00
// map[string]interface{} and not map[interface{}]interface{}.
2024-02-18 10:42:21 +00:00
// Use IntfaceKeysToStrings() to convert if necessary.
2024-02-18 10:42:21 +00:00
func Search(mp map[string]interface{}, path []string) interface{} {
2024-02-18 10:42:21 +00:00
next, ok := mp[path[0]]
2024-02-18 10:42:21 +00:00
if ok {
2024-02-18 10:42:21 +00:00
if len(path) == 1 {
2024-02-18 10:42:21 +00:00
return next
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
switch m := next.(type) {
2024-02-18 10:42:21 +00:00
case map[string]interface{}:
2024-02-18 10:42:21 +00:00
return Search(m, path[1:])
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
} //
2024-02-18 10:42:21 +00:00
// It's important to note that all nested maps should be
2024-02-18 10:42:21 +00:00
// map[string]interface{} and not map[interface{}]interface{}.
2024-02-18 10:42:21 +00:00
// Use IntfaceKeysToStrings() to convert if necessary.
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
// Copy returns a deep copy of a conf map.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// It's important to note that all nested maps should be
2024-02-18 10:42:21 +00:00
// map[string]interface{} and not map[interface{}]interface{}.
2024-02-18 10:42:21 +00:00
// Use IntfaceKeysToStrings() to convert if necessary.
2024-02-18 10:42:21 +00:00
func Copy(mp map[string]interface{}) map[string]interface{} {
2024-02-18 10:42:21 +00:00
out, _ := copystructure.Copy(&mp)
2024-02-18 10:42:21 +00:00
if res, ok := out.(*map[string]interface{}); ok {
2024-02-18 10:42:21 +00:00
return *res
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return map[string]interface{}{}
2024-02-18 10:42:21 +00:00
}
// IntfaceKeysToStrings recursively converts map[interface{}]interface{} to
2024-02-18 10:42:21 +00:00
// map[string]interface{}. Some parses such as YAML unmarshal return this.
2024-02-18 10:42:21 +00:00
func IntfaceKeysToStrings(mp map[string]interface{}) {
2024-02-18 10:42:21 +00:00
for key, val := range mp {
2024-02-18 10:42:21 +00:00
switch cur := val.(type) {
2024-02-18 10:42:21 +00:00
case map[interface{}]interface{}:
2024-02-18 10:42:21 +00:00
x := make(map[string]interface{})
2024-02-18 10:42:21 +00:00
for k, v := range cur {
2024-02-18 10:42:21 +00:00
x[fmt.Sprintf("%v", k)] = v
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
mp[key] = x
2024-02-18 10:42:21 +00:00
IntfaceKeysToStrings(x)
2024-02-18 10:42:21 +00:00
case []interface{}:
2024-02-18 10:42:21 +00:00
for i, v := range cur {
2024-02-18 10:42:21 +00:00
switch sub := v.(type) {
2024-02-18 10:42:21 +00:00
case map[interface{}]interface{}:
2024-02-18 10:42:21 +00:00
x := make(map[string]interface{})
2024-02-18 10:42:21 +00:00
for k, v := range sub {
2024-02-18 10:42:21 +00:00
x[fmt.Sprintf("%v", k)] = v
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
cur[i] = x
2024-02-18 10:42:21 +00:00
IntfaceKeysToStrings(x)
2024-02-18 10:42:21 +00:00
case map[string]interface{}:
2024-02-18 10:42:21 +00:00
IntfaceKeysToStrings(sub)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
case map[string]interface{}:
2024-02-18 10:42:21 +00:00
IntfaceKeysToStrings(cur)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// StringSliceToLookupMap takes a slice of strings and returns a lookup map
2024-02-18 10:42:21 +00:00
// with the slice values as keys with true values.
2024-02-18 10:42:21 +00:00
func StringSliceToLookupMap(s []string) map[string]bool {
2024-02-18 10:42:21 +00:00
mp := make(map[string]bool, len(s))
2024-02-18 10:42:21 +00:00
for _, v := range s {
2024-02-18 10:42:21 +00:00
mp[v] = true
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return mp
2024-02-18 10:42:21 +00:00
}
// Int64SliceToLookupMap takes a slice of int64s and returns a lookup map
2024-02-18 10:42:21 +00:00
// with the slice values as keys with true values.
2024-02-18 10:42:21 +00:00
func Int64SliceToLookupMap(s []int64) map[int64]bool {
2024-02-18 10:42:21 +00:00
mp := make(map[int64]bool, len(s))
2024-02-18 10:42:21 +00:00
for _, v := range s {
2024-02-18 10:42:21 +00:00
mp[v] = true
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return mp
2024-02-18 10:42:21 +00:00
}