mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-06 05:01:10 -05:00
Bumps [github.com/gookit/config/v2](https://github.com/gookit/config) from 2.2.6 to 2.2.7. - [Release notes](https://github.com/gookit/config/releases) - [Commits](https://github.com/gookit/config/compare/v2.2.6...v2.2.7) --- updated-dependencies: - dependency-name: github.com/gookit/config/v2 dependency-version: 2.2.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
253 lines
5.4 KiB
Go
253 lines
5.4 KiB
Go
package maputil
|
|
|
|
import (
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gookit/goutil/comdef"
|
|
"github.com/gookit/goutil/reflects"
|
|
)
|
|
|
|
// some consts for separators
|
|
const (
|
|
Wildcard = "*"
|
|
PathSep = "."
|
|
)
|
|
|
|
// DeepGet value by key path. eg "top" "top.sub"
|
|
func DeepGet(mp map[string]any, path string) (val any) {
|
|
val, _ = GetByPath(path, mp)
|
|
return
|
|
}
|
|
|
|
// QuietGet value by key path. eg "top" "top.sub"
|
|
func QuietGet(mp map[string]any, path string) (val any) {
|
|
val, _ = GetByPath(path, mp)
|
|
return
|
|
}
|
|
|
|
// GetFromAny get value by key path from any(map,slice) data. eg "top" "top.sub"
|
|
func GetFromAny(path string, data any) (val any, ok bool) {
|
|
// empty data
|
|
if data == nil {
|
|
return nil, false
|
|
}
|
|
if len(path) == 0 {
|
|
return data, true
|
|
}
|
|
|
|
return getByPathKeys(data, strings.Split(path, "."))
|
|
}
|
|
|
|
// GetByPath get value by key path from a map(map[string]any). eg "top" "top.sub"
|
|
func GetByPath(path string, mp map[string]any) (val any, ok bool) {
|
|
if len(path) == 0 {
|
|
return mp, true
|
|
}
|
|
if val, ok := mp[path]; ok {
|
|
return val, true
|
|
}
|
|
|
|
// no sub key
|
|
if len(mp) == 0 || strings.IndexByte(path, '.') < 1 {
|
|
return nil, false
|
|
}
|
|
|
|
// key is path. eg: "top.sub"
|
|
return GetByPathKeys(mp, strings.Split(path, "."))
|
|
}
|
|
|
|
// GetByPathKeys get value by path keys from a map(map[string]any). eg "top" "top.sub"
|
|
//
|
|
// Example:
|
|
//
|
|
// mp := map[string]any{
|
|
// "top": map[string]any{
|
|
// "sub": "value",
|
|
// },
|
|
// }
|
|
// val, ok := GetByPathKeys(mp, []string{"top", "sub"}) // return "value", true
|
|
func GetByPathKeys(mp map[string]any, keys []string) (val any, ok bool) {
|
|
kl := len(keys)
|
|
if kl == 0 {
|
|
return mp, true
|
|
}
|
|
|
|
// find top item data use top key
|
|
var item any
|
|
topK := keys[0]
|
|
if item, ok = mp[topK]; !ok {
|
|
return
|
|
}
|
|
|
|
// find sub item data use sub key
|
|
return getByPathKeys(item, keys[1:])
|
|
}
|
|
|
|
func getByPathKeys(item any, keys []string) (val any, ok bool) {
|
|
kl := len(keys)
|
|
|
|
for i, k := range keys {
|
|
switch tData := item.(type) {
|
|
case map[string]string: // is string map
|
|
if item, ok = tData[k]; !ok {
|
|
return
|
|
}
|
|
case map[string]any: // is map(decode from toml/json/yaml)
|
|
if item, ok = tData[k]; !ok {
|
|
return
|
|
}
|
|
case map[any]any: // is map(decode from yaml.v2)
|
|
if item, ok = tData[k]; !ok {
|
|
return
|
|
}
|
|
case []map[string]any: // is an any-map slice
|
|
if k == Wildcard {
|
|
if kl == i+1 { // * is last key
|
|
return tData, true
|
|
}
|
|
|
|
// * is not last key, find sub item data
|
|
sl := make([]any, 0, len(tData))
|
|
for _, v := range tData {
|
|
if val, ok = getByPathKeys(v, keys[i+1:]); ok {
|
|
sl = append(sl, val)
|
|
}
|
|
}
|
|
|
|
if len(sl) > 0 {
|
|
return sl, true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// k is index number
|
|
idx, err := strconv.Atoi(k)
|
|
if err != nil || idx >= len(tData) {
|
|
return nil, false
|
|
}
|
|
item = tData[idx]
|
|
default:
|
|
if k == Wildcard && kl == i+1 { // * is last key
|
|
return tData, true
|
|
}
|
|
|
|
rv := reflect.ValueOf(tData)
|
|
// check is slice
|
|
if rv.Kind() == reflect.Slice {
|
|
if k == Wildcard {
|
|
// * is not last key, find sub item data
|
|
sl := make([]any, 0, rv.Len())
|
|
for si := 0; si < rv.Len(); si++ {
|
|
el := reflects.Indirect(rv.Index(si))
|
|
if el.Kind() != reflect.Map {
|
|
return nil, false
|
|
}
|
|
|
|
// el is map value.
|
|
if val, ok = getByPathKeys(el.Interface(), keys[i+1:]); ok {
|
|
sl = append(sl, val)
|
|
}
|
|
}
|
|
|
|
if len(sl) > 0 {
|
|
return sl, true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// check k is index number
|
|
ii, err := strconv.Atoi(k)
|
|
if err != nil || ii >= rv.Len() {
|
|
return nil, false
|
|
}
|
|
|
|
item = rv.Index(ii).Interface()
|
|
continue
|
|
}
|
|
|
|
// as error
|
|
return nil, false
|
|
}
|
|
|
|
// next is last key and it is *
|
|
if kl == i+2 && keys[i+1] == Wildcard {
|
|
return item, true
|
|
}
|
|
}
|
|
|
|
return item, true
|
|
}
|
|
|
|
// Keys get all keys of the given map.
|
|
func Keys(mp any) (keys []string) {
|
|
rftVal := reflect.Indirect(reflect.ValueOf(mp))
|
|
if rftVal.Kind() != reflect.Map {
|
|
return
|
|
}
|
|
|
|
keys = make([]string, 0, rftVal.Len())
|
|
for _, key := range rftVal.MapKeys() {
|
|
keys = append(keys, key.String())
|
|
}
|
|
return
|
|
}
|
|
|
|
// TypedKeys get all keys of the given typed map.
|
|
func TypedKeys[K comdef.SimpleType, V any](mp map[K]V) (keys []K) {
|
|
for key := range mp {
|
|
keys = append(keys, key)
|
|
}
|
|
return
|
|
}
|
|
|
|
// FirstKey returns the first key of the given map.
|
|
func FirstKey[T any](mp map[string]T) string {
|
|
for key := range mp {
|
|
return key
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// Values get all values from the given map.
|
|
func Values(mp any) (values []any) {
|
|
rv := reflect.Indirect(reflect.ValueOf(mp))
|
|
if rv.Kind() != reflect.Map {
|
|
return
|
|
}
|
|
|
|
values = make([]any, 0, rv.Len())
|
|
for _, key := range rv.MapKeys() {
|
|
values = append(values, rv.MapIndex(key).Interface())
|
|
}
|
|
return
|
|
}
|
|
|
|
// TypedValues get all values from the given typed map.
|
|
func TypedValues[K comdef.SimpleType, V any](mp map[K]V) (values []V) {
|
|
for _, val := range mp {
|
|
values = append(values, val)
|
|
}
|
|
return
|
|
}
|
|
|
|
// EachAnyMap iterates the given map and calls the given function for each item.
|
|
func EachAnyMap(mp any, fn func(key string, val any)) {
|
|
rv := reflect.Indirect(reflect.ValueOf(mp))
|
|
if rv.Kind() != reflect.Map {
|
|
panic("not a map value")
|
|
}
|
|
|
|
for _, key := range rv.MapKeys() {
|
|
fn(key.String(), rv.MapIndex(key).Interface())
|
|
}
|
|
}
|
|
|
|
// EachTypedMap iterates the given map and calls the given function for each item.
|
|
func EachTypedMap[K comdef.SimpleType, V any](mp map[K]V, fn func(key K, val V)) {
|
|
for key, val := range mp {
|
|
fn(key, val)
|
|
}
|
|
}
|