You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
3.4 KiB
Go
170 lines
3.4 KiB
Go
package nmap
|
|
|
|
import (
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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 val, ok := mp[path]; ok {
|
|
return val, true
|
|
}
|
|
|
|
// no sub key
|
|
if len(mp) == 0 || strings.IndexByte(path, '.') < 1 {
|
|
return nil, false
|
|
}
|
|
|
|
// has sub key. eg. "top.sub"
|
|
keys := strings.Split(path, ".")
|
|
return GetByPathKeys(mp, keys)
|
|
}
|
|
|
|
// 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
|
|
for i, k := range keys[1:] {
|
|
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+2 {
|
|
return tData, true
|
|
}
|
|
|
|
sl := make([]any, 0, len(tData))
|
|
for _, v := range tData {
|
|
if val, ok = GetByPathKeys(v, keys[i+2:]); ok {
|
|
sl = append(sl, val)
|
|
}
|
|
}
|
|
return sl, true
|
|
}
|
|
|
|
// k is index number
|
|
idx, err := strconv.Atoi(k)
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
|
|
if idx >= len(tData) {
|
|
return nil, false
|
|
}
|
|
item = tData[idx]
|
|
default:
|
|
rv := reflect.ValueOf(tData)
|
|
// check is slice
|
|
if rv.Kind() == reflect.Slice {
|
|
i, err := strconv.Atoi(k)
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
if i >= rv.Len() {
|
|
return nil, false
|
|
}
|
|
|
|
item = rv.Index(i).Interface()
|
|
continue
|
|
}
|
|
|
|
// as error
|
|
return nil, false
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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())
|
|
}
|
|
}
|