helmfile/pkg/maputil/maputil.go

192 lines
3.4 KiB
Go

package maputil
import (
"fmt"
"strings"
)
func CastKeysToStrings(s any) (map[string]any, error) {
new := map[string]any{}
switch src := s.(type) {
case map[any]any:
for k, v := range src {
var strK string
switch typedK := k.(type) {
case string:
strK = typedK
default:
return nil, fmt.Errorf("unexpected type of key in map: expected string, got %T: value=%v, map=%v", typedK, typedK, src)
}
castedV, err := recursivelyStringifyMapKey(v)
if err != nil {
return nil, err
}
new[strK] = castedV
}
case map[string]any:
for k, v := range src {
castedV, err := recursivelyStringifyMapKey(v)
if err != nil {
return nil, err
}
new[k] = castedV
}
}
return new, nil
}
func recursivelyStringifyMapKey(v any) (any, error) {
var castedV any
switch typedV := v.(type) {
case map[any]any, map[string]any:
tmp, err := CastKeysToStrings(typedV)
if err != nil {
return nil, err
}
castedV = tmp
case []any:
a := []any{}
for i := range typedV {
res, err := recursivelyStringifyMapKey(typedV[i])
if err != nil {
return nil, err
}
a = append(a, res)
}
castedV = a
default:
castedV = typedV
}
return castedV, nil
}
type arg interface {
getMap(map[string]any) map[string]any
set(map[string]any, string)
}
type keyArg struct {
key string
}
func (a keyArg) getMap(m map[string]any) map[string]any {
_, ok := m[a.key]
if !ok {
m[a.key] = map[string]any{}
}
switch t := m[a.key].(type) {
case map[string]any:
return t
default:
panic(fmt.Errorf("unexpected type: %v(%T)", t, t))
}
}
func (a keyArg) set(m map[string]any, value string) {
m[a.key] = value
}
type indexedKeyArg struct {
key string
index int
}
func (a indexedKeyArg) getArray(m map[string]any) []any {
_, ok := m[a.key]
if !ok {
m[a.key] = make([]any, a.index+1)
}
switch t := m[a.key].(type) {
case []any:
if len(t) <= a.index {
t2 := make([]any, a.index+1)
copy(t2, t)
t = t2
}
return t
default:
panic(fmt.Errorf("unexpected type: %v(%T)", t, t))
}
}
func (a indexedKeyArg) getMap(m map[string]any) map[string]any {
t := a.getArray(m)
if t[a.index] == nil {
t[a.index] = map[string]any{}
}
switch t := t[a.index].(type) {
case map[string]any:
return t
default:
panic(fmt.Errorf("unexpected type: %v(%T)", t, t))
}
}
func (a indexedKeyArg) set(m map[string]any, value string) {
t := a.getArray(m)
t[a.index] = value
m[a.key] = t
}
func getCursor(key string) arg {
key = strings.ReplaceAll(key, "[", " ")
key = strings.ReplaceAll(key, "]", " ")
k := key
idx := 0
n, err := fmt.Sscanf(key, "%s %d", &k, &idx)
if n == 2 && err == nil {
return indexedKeyArg{
key: k,
index: idx,
}
}
return keyArg{
key: key,
}
}
func ParseKey(key string) []string {
r := []string{}
part := ""
escaped := false
for _, rune := range key {
split := false
switch {
case !escaped && rune == '\\':
escaped = true
continue
case rune == '.':
split = !escaped
}
escaped = false
if split {
r = append(r, part)
part = ""
} else {
part += string(rune)
}
}
if len(part) > 0 {
r = append(r, part)
}
return r
}
func Set(m map[string]any, key []string, value string) {
if len(key) == 0 {
panic(fmt.Errorf("bug: unexpected length of key: %d", len(key)))
}
for len(key) > 1 {
m, key = getCursor(key[0]).getMap(m), key[1:]
}
getCursor(key[0]).set(m, value)
}