62 lines
1.5 KiB
Go
62 lines
1.5 KiB
Go
package mxj
|
||
|
||
import (
|
||
"errors"
|
||
"strings"
|
||
)
|
||
|
||
// RenameKey renames a key in a Map.
|
||
// It works only for nested maps.
|
||
// It doesn't work for cases when the key is in a list.
|
||
func (mv Map) RenameKey(path string, newName string) error {
|
||
var v bool
|
||
var err error
|
||
if v, err = mv.Exists(path); err == nil && !v {
|
||
return errors.New("RenameKey: path not found: " + path)
|
||
} else if err != nil {
|
||
return err
|
||
}
|
||
if v, err = mv.Exists(parentPath(path) + "." + newName); err == nil && v {
|
||
return errors.New("RenameKey: key already exists: " + newName)
|
||
} else if err != nil {
|
||
return err
|
||
}
|
||
|
||
m := map[string]interface{}(mv)
|
||
return renameKey(m, path, newName)
|
||
}
|
||
|
||
func renameKey(m interface{}, path string, newName string) error {
|
||
val, err := prevValueByPath(m, path)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
oldName := lastKey(path)
|
||
val[newName] = val[oldName]
|
||
delete(val, oldName)
|
||
|
||
return nil
|
||
}
|
||
|
||
// returns a value which contains a last key in the path
|
||
// For example: prevValueByPath("a.b.c", {a{b{c: 3}}}) returns {c: 3}
|
||
func prevValueByPath(m interface{}, path string) (map[string]interface{}, error) {
|
||
keys := strings.Split(path, ".")
|
||
|
||
switch mValue := m.(type) {
|
||
case map[string]interface{}:
|
||
for key, value := range mValue {
|
||
if key == keys[0] {
|
||
if len(keys) == 1 {
|
||
return mValue, nil
|
||
} else {
|
||
// keep looking for the full path to the key
|
||
return prevValueByPath(value, strings.Join(keys[1:], "."))
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return nil, errors.New("prevValueByPath: didn't find path – " + path)
|
||
}
|