switch to go vendoring

This commit is contained in:
Michael Barz
2023-04-19 20:24:34 +02:00
parent 632fa05ef9
commit afc6ed1e41
8527 changed files with 3004916 additions and 2 deletions
+72
View File
@@ -0,0 +1,72 @@
# Map Utils
`maputil` provide map data util functions. eg: convert, sub-value get, simple merge
- use `map[string]any` as Data
- deep get value by key path
- deep set value by key path
## Install
```bash
go get github.com/gookit/goutil/maputil
```
## Go docs
- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/maputil)
## Usage
### Deep get value
```go
mp := map[string]any {
"top1": "val1",
"arr1": []string{"ab", "cd"}
"map1": map[string]any{
"sub1": "val2",
},
}
fmt.Println(maputil.DeepGet(mp, "map1.sub1")) // Output: VAL3
// get value from slice.
fmt.Println(maputil.DeepGet(mp, "arr1.1")) // Output: cd
fmt.Println(maputil.DeepGet(mp, "arr1[1]")) // Output: cd
```
### Deep set value
```go
mp := map[string]any {
"top1": "val1",
"arr1": []string{"ab"}
"map1": map[string]any{
"sub1": "val2",
},
}
err := maputil.SetByPath(&mp, "map1.newKey", "VAL3")
fmt.Println(maputil.DeepGet(mp, "map1.newKey")) // Output: VAL3
```
## Code Check & Testing
```bash
gofmt -w -l ./
golint ./...
```
**Testing**:
```shell
go test -v ./maputil/...
```
**Test limit by regexp**:
```shell
go test -v -run ^TestSetByKeys ./maputil/...
```
+44
View File
@@ -0,0 +1,44 @@
package maputil
import "fmt"
// Aliases implemented a simple string alias map.
type Aliases map[string]string
// AddAlias to the Aliases
func (as Aliases) AddAlias(real, alias string) {
if rn, ok := as[alias]; ok {
panic(fmt.Sprintf("The alias '%s' is already used by '%s'", alias, rn))
}
as[alias] = real
}
// AddAliases to the Aliases
func (as Aliases) AddAliases(real string, aliases []string) {
for _, a := range aliases {
as.AddAlias(real, a)
}
}
// AddAliasMap to the Aliases
func (as Aliases) AddAliasMap(alias2real map[string]string) {
for a, r := range alias2real {
as.AddAlias(r, a)
}
}
// HasAlias in the Aliases
func (as Aliases) HasAlias(alias string) bool {
if _, ok := as[alias]; ok {
return true
}
return false
}
// ResolveAlias by given name.
func (as Aliases) ResolveAlias(alias string) string {
if name, ok := as[alias]; ok {
return name
}
return alias
}
+46
View File
@@ -0,0 +1,46 @@
package maputil
import (
"reflect"
"github.com/gookit/goutil/reflects"
)
// HasKey check of the given map.
func HasKey(mp, key any) (ok bool) {
rftVal := reflect.Indirect(reflect.ValueOf(mp))
if rftVal.Kind() != reflect.Map {
return
}
for _, keyRv := range rftVal.MapKeys() {
if reflects.IsEqual(keyRv.Interface(), key) {
return true
}
}
return
}
// HasAllKeys check of the given map.
func HasAllKeys(mp any, keys ...any) (ok bool, noKey any) {
rftVal := reflect.Indirect(reflect.ValueOf(mp))
if rftVal.Kind() != reflect.Map {
return
}
for _, key := range keys {
var exist bool
for _, keyRv := range rftVal.MapKeys() {
if reflects.IsEqual(keyRv.Interface(), key) {
exist = true
break
}
}
if !exist {
return false, key
}
}
return true, nil
}
+108
View File
@@ -0,0 +1,108 @@
package maputil
import (
"reflect"
"strings"
"github.com/gookit/goutil/reflects"
"github.com/gookit/goutil/strutil"
)
// KeyToLower convert keys to lower case.
func KeyToLower(src map[string]string) map[string]string {
newMp := make(map[string]string, len(src))
for k, v := range src {
k = strings.ToLower(k)
newMp[k] = v
}
return newMp
}
// ToStringMap convert map[string]any to map[string]string
func ToStringMap(src map[string]any) map[string]string {
newMp := make(map[string]string, len(src))
for k, v := range src {
newMp[k] = strutil.MustString(v)
}
return newMp
}
// HttpQueryString convert map[string]any data to http query string.
func HttpQueryString(data map[string]any) string {
ss := make([]string, 0, len(data))
for k, v := range data {
ss = append(ss, k+"="+strutil.QuietString(v))
}
return strings.Join(ss, "&")
}
// ToString simple and quickly convert map[string]any to string.
func ToString(mp map[string]any) string {
if mp == nil {
return ""
}
if len(mp) == 0 {
return "{}"
}
buf := make([]byte, 0, len(mp)*16)
buf = append(buf, '{')
for k, val := range mp {
buf = append(buf, k...)
buf = append(buf, ':')
str := strutil.QuietString(val)
buf = append(buf, str...)
buf = append(buf, ',', ' ')
}
// remove last ', '
buf = append(buf[:len(buf)-2], '}')
return strutil.Byte2str(buf)
}
// ToString2 simple and quickly convert a map to string.
func ToString2(mp any) string {
return NewFormatter(mp).Format()
}
// FormatIndent format map data to string with newline and indent.
func FormatIndent(mp any, indent string) string {
return NewFormatter(mp).WithIndent(indent).Format()
}
/*************************************************************
* Flat convert tree map to flatten key-value map.
*************************************************************/
// Flatten convert tree map to flat key-value map.
//
// Examples:
//
// {"top": {"sub": "value", "sub2": "value2"} }
// ->
// {"top.sub": "value", "top.sub2": "value2" }
func Flatten(mp map[string]any) map[string]any {
if mp == nil {
return nil
}
flatMp := make(map[string]any, len(mp)*2)
reflects.FlatMap(reflect.ValueOf(mp), func(path string, val reflect.Value) {
flatMp[path] = val.Interface()
})
return flatMp
}
// FlatWithFunc flat a tree-map with custom collect handle func
func FlatWithFunc(mp map[string]any, fn reflects.FlatFunc) {
if mp == nil || fn == nil {
return
}
reflects.FlatMap(reflect.ValueOf(mp), fn)
}
+219
View File
@@ -0,0 +1,219 @@
package maputil
import (
"strings"
"github.com/gookit/goutil/mathutil"
"github.com/gookit/goutil/strutil"
)
// Data an map data type
type Data map[string]any
type Map = Data
// Has value on the data map
func (d Data) Has(key string) bool {
_, ok := d.GetByPath(key)
return ok
}
// IsEmtpy if the data map
func (d Data) IsEmtpy() bool {
return len(d) == 0
}
// Value get from the data map
func (d Data) Value(key string) (any, bool) {
val, ok := d.GetByPath(key)
return val, ok
}
// Get value from the data map.
// Supports dot syntax to get deep values. eg: top.sub
func (d Data) Get(key string) any {
if val, ok := d.GetByPath(key); ok {
return val
}
return nil
}
// GetByPath get value from the data map by path. eg: top.sub
// Supports dot syntax to get deep values.
func (d Data) GetByPath(path string) (any, bool) {
if val, ok := d[path]; ok {
return val, true
}
// is key path.
if strings.ContainsRune(path, '.') {
val, ok := GetByPath(path, d)
if ok {
return val, true
}
}
return nil, false
}
// Set value to the data map
func (d Data) Set(key string, val any) {
d[key] = val
}
// SetByPath sets a value in the map.
// Supports dot syntax to set deep values.
//
// For example:
//
// d.SetByPath("name.first", "Mat")
func (d Data) SetByPath(path string, value any) error {
if path == "" {
return nil
}
return d.SetByKeys(strings.Split(path, KeySepStr), value)
}
// SetByKeys sets a value in the map by path keys.
// Supports dot syntax to set deep values.
//
// For example:
//
// d.SetByKeys([]string{"name", "first"}, "Mat")
func (d Data) SetByKeys(keys []string, value any) error {
kln := len(keys)
if kln == 0 {
return nil
}
// special handle d is empty.
if len(d) == 0 {
if kln == 1 {
d.Set(keys[0], value)
} else {
d.Set(keys[0], MakeByKeys(keys[1:], value))
}
return nil
}
return SetByKeys((*map[string]any)(&d), keys, value)
// It's ok, but use `func (d *Data)`
// return SetByKeys((*map[string]any)(d), keys, value)
}
// Default get value from the data map with default value
func (d Data) Default(key string, def any) any {
if val, ok := d.GetByPath(key); ok {
return val
}
return def
}
// Int value get
func (d Data) Int(key string) int {
if val, ok := d.GetByPath(key); ok {
return mathutil.QuietInt(val)
}
return 0
}
// Int64 value get
func (d Data) Int64(key string) int64 {
if val, ok := d.GetByPath(key); ok {
return mathutil.QuietInt64(val)
}
return 0
}
// Str value get by key
func (d Data) Str(key string) string {
if val, ok := d.GetByPath(key); ok {
return strutil.QuietString(val)
}
return ""
}
// Bool value get
func (d Data) Bool(key string) bool {
val, ok := d.GetByPath(key)
if !ok {
return false
}
if bl, ok := val.(bool); ok {
return bl
}
if str, ok := val.(string); ok {
return strutil.QuietBool(str)
}
return false
}
// Strings get []string value
func (d Data) Strings(key string) []string {
val, ok := d.GetByPath(key)
if !ok {
return nil
}
if ss, ok := val.([]string); ok {
return ss
}
return nil
}
// StrSplit get strings by split key value
func (d Data) StrSplit(key, sep string) []string {
if val, ok := d.GetByPath(key); ok {
return strings.Split(strutil.QuietString(val), sep)
}
return nil
}
// StringsByStr value get by key
func (d Data) StringsByStr(key string) []string {
if val, ok := d.GetByPath(key); ok {
return strings.Split(strutil.QuietString(val), ",")
}
return nil
}
// StringMap get map[string]string value
func (d Data) StringMap(key string) map[string]string {
val, ok := d.GetByPath(key)
if !ok {
return nil
}
if smp, ok := val.(map[string]string); ok {
return smp
}
return nil
}
// Sub get sub value as new Data
func (d Data) Sub(key string) Data {
if val, ok := d.GetByPath(key); ok {
if sub, ok := val.(map[string]any); ok {
return sub
}
}
return nil
}
// Keys of the data map
func (d Data) Keys() []string {
keys := make([]string, 0, len(d))
for k := range d {
keys = append(keys, k)
}
return keys
}
// ToStringMap convert to map[string]string
func (d Data) ToStringMap() map[string]string {
return ToStringMap(d)
}
// String data to string
func (d Data) String() string {
return ToString(d)
}
+125
View File
@@ -0,0 +1,125 @@
package maputil
import (
"io"
"reflect"
"github.com/gookit/goutil/comdef"
"github.com/gookit/goutil/strutil"
)
// MapFormatter struct
type MapFormatter struct {
comdef.BaseFormatter
// Prefix string for each element
Prefix string
// Indent string for each element
Indent string
// ClosePrefix string for last "}"
ClosePrefix string
// AfterReset after reset on call Format().
// AfterReset bool
}
// NewFormatter instance
func NewFormatter(mp any) *MapFormatter {
f := &MapFormatter{}
f.Src = mp
return f
}
// WithFn for config self
func (f *MapFormatter) WithFn(fn func(f *MapFormatter)) *MapFormatter {
fn(f)
return f
}
// WithIndent string
func (f *MapFormatter) WithIndent(indent string) *MapFormatter {
f.Indent = indent
return f
}
// FormatTo to custom buffer
func (f *MapFormatter) FormatTo(w io.Writer) {
f.SetOutput(w)
f.doFormat()
}
// Format to string
func (f *MapFormatter) String() string {
return f.Format()
}
// Format to string
func (f *MapFormatter) Format() string {
f.doFormat()
return f.BsWriter().String()
}
// Format map data to string.
//
//goland:noinspection GoUnhandledErrorResult
func (f *MapFormatter) doFormat() {
if f.Src == nil {
return
}
rv, ok := f.Src.(reflect.Value)
if !ok {
rv = reflect.ValueOf(f.Src)
}
rv = reflect.Indirect(rv)
if rv.Kind() != reflect.Map {
return
}
buf := f.BsWriter()
ln := rv.Len()
if ln == 0 {
buf.WriteString("{}")
return
}
// buf.Grow(ln * 16)
buf.WriteByte('{')
indentLn := len(f.Indent)
if indentLn > 0 {
buf.WriteByte('\n')
}
for i, key := range rv.MapKeys() {
kStr := strutil.QuietString(key.Interface())
if indentLn > 0 {
buf.WriteString(f.Indent)
}
buf.WriteString(kStr)
buf.WriteByte(':')
vStr := strutil.QuietString(rv.MapIndex(key).Interface())
buf.WriteString(vStr)
if i < ln-1 {
buf.WriteByte(',')
// no indent, with space
if indentLn == 0 {
buf.WriteByte(' ')
}
}
// with newline
if indentLn > 0 {
buf.WriteByte('\n')
}
}
if f.ClosePrefix != "" {
buf.WriteString(f.ClosePrefix)
}
buf.WriteByte('}')
}
+114
View File
@@ -0,0 +1,114 @@
package maputil
import (
"reflect"
"strconv"
"strings"
)
// 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.ContainsRune(path, '.') {
return nil, false
}
// has sub key. eg. "top.sub"
keys := strings.Split(path, ".")
topK := keys[0]
// find top item data use top key
var item any
if item, ok = mp[topK]; !ok {
return
}
for _, k := range keys[1:] {
switch tData := item.(type) {
case map[string]string: // is simple map
if item, ok = tData[k]; !ok {
return
}
case map[string]any: // is map(decode from toml/json)
if item, ok = tData[k]; !ok {
return
}
case map[any]any: // is map(decode from yaml)
if item, ok = tData[k]; !ok {
return
}
case []any: // is a slice
if item, ok = getBySlice(k, tData); !ok {
return
}
case []string, []int, []float32, []float64, []bool, []rune:
slice := reflect.ValueOf(tData)
sData := make([]any, slice.Len())
for i := 0; i < slice.Len(); i++ {
sData[i] = slice.Index(i).Interface()
}
if item, ok = getBySlice(k, sData); !ok {
return
}
default: // error
return nil, false
}
}
return item, true
}
func getBySlice(k string, slice []any) (val any, ok bool) {
i, err := strconv.ParseInt(k, 10, 64)
if err != nil {
return nil, false
}
if size := int64(len(slice)); i >= size {
return nil, false
}
return slice[i], 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) {
rftVal := reflect.Indirect(reflect.ValueOf(mp))
if rftVal.Kind() != reflect.Map {
return
}
values = make([]any, 0, rftVal.Len())
for _, key := range rftVal.MapKeys() {
values = append(values, rftVal.MapIndex(key).Interface())
}
return
}
+103
View File
@@ -0,0 +1,103 @@
// Package maputil provide map data util functions. eg: convert, sub-value get, simple merge
package maputil
import (
"reflect"
"strings"
"github.com/gookit/goutil/arrutil"
)
// Key, value sep char consts
const (
ValSepStr = ","
ValSepChar = ','
KeySepStr = "."
KeySepChar = '.'
)
// MergeSMap simple merge two string map. merge src to dst map
func MergeSMap(src, dst map[string]string, ignoreCase bool) map[string]string {
return MergeStringMap(src, dst, ignoreCase)
}
// MergeStringMap simple merge two string map. merge src to dst map
func MergeStringMap(src, dst map[string]string, ignoreCase bool) map[string]string {
for k, v := range src {
if ignoreCase {
k = strings.ToLower(k)
}
dst[k] = v
}
return dst
}
// MakeByPath build new value by key names
//
// Example:
//
// "site.info"
// ->
// map[string]any {
// site: {info: val}
// }
//
// // case 2, last key is slice:
// "site.tags[1]"
// ->
// map[string]any {
// site: {tags: [val]}
// }
func MakeByPath(path string, val any) (mp map[string]any) {
return MakeByKeys(strings.Split(path, KeySepStr), val)
}
// MakeByKeys build new value by key names
//
// Example:
//
// // case 1:
// []string{"site", "info"}
// ->
// map[string]any {
// site: {info: val}
// }
//
// // case 2, last key is slice:
// []string{"site", "tags[1]"}
// ->
// map[string]any {
// site: {tags: [val]}
// }
func MakeByKeys(keys []string, val any) (mp map[string]any) {
size := len(keys)
// if last key contains slice index, make slice wrap the val
lastKey := keys[size-1]
if newK, idx, ok := parseArrKeyIndex(lastKey); ok {
// valTyp := reflect.TypeOf(val)
sliTyp := reflect.SliceOf(reflect.TypeOf(val))
sliVal := reflect.MakeSlice(sliTyp, idx+1, idx+1)
sliVal.Index(idx).Set(reflect.ValueOf(val))
// update val and last key
val = sliVal.Interface()
keys[size-1] = newK
}
if size == 1 {
return map[string]any{keys[0]: val}
}
// multi nodes
arrutil.Reverse(keys)
for _, p := range keys {
if mp == nil {
mp = map[string]any{p: val}
} else {
mp = map[string]any{p: mp}
}
}
return
}
+340
View File
@@ -0,0 +1,340 @@
package maputil
import (
"fmt"
"reflect"
"strconv"
"strings"
"github.com/gookit/goutil/strutil"
)
// SetByPath set sub-map value by key path.
// Supports dot syntax to set deep values.
//
// For example:
//
// SetByPath("name.first", "Mat")
func SetByPath(mp *map[string]any, path string, val any) error {
return SetByKeys(mp, strings.Split(path, KeySepStr), val)
}
// SetByKeys set sub-map value by path keys.
// Supports dot syntax to set deep values.
//
// For example:
//
// SetByKeys([]string{"name", "first"}, "Mat")
func SetByKeys(mp *map[string]any, keys []string, val any) (err error) {
kln := len(keys)
if kln == 0 {
return nil
}
mpv := *mp
if len(mpv) == 0 {
*mp = MakeByKeys(keys, val)
return nil
}
topK := keys[0]
if kln == 1 {
mpv[topK] = val
return nil
}
if _, ok := mpv[topK]; !ok {
mpv[topK] = MakeByKeys(keys[1:], val)
return nil
}
rv := reflect.ValueOf(mp).Elem()
return setMapByKeys(rv, keys, reflect.ValueOf(val))
}
func setMapByKeys(rv reflect.Value, keys []string, nv reflect.Value) (err error) {
if rv.Kind() != reflect.Map {
return fmt.Errorf("input parameter#rv must be a Map, but was %s", rv.Kind())
}
// If the map is nil, make a new map
if rv.IsNil() {
mapType := reflect.MapOf(rv.Type().Key(), rv.Type().Elem())
rv.Set(reflect.MakeMap(mapType))
}
var ok bool
maxI := len(keys) - 1
for i, key := range keys {
idx := -1
isMap := rv.Kind() == reflect.Map
isSlice := rv.Kind() == reflect.Slice
isLast := i == len(keys)-1
// slice index key must be ended on the keys.
// eg: "top.arr[2]" -> "arr[2]"
if pos := strings.IndexRune(key, '['); pos > 0 {
var realKey string
if realKey, idx, ok = parseArrKeyIndex(key); ok {
// update value
key = realKey
if !isMap {
err = fmt.Errorf(
"current value#%s type is %s, cannot get sub-value by key: %s",
strings.Join(keys[i:], "."),
rv.Kind(),
key,
)
break
}
rftK := reflect.ValueOf(key)
tmpV := rv.MapIndex(rftK)
if !tmpV.IsValid() {
if isLast {
sliVal := reflect.MakeSlice(reflect.SliceOf(nv.Type()), idx+1, idx+1)
sliVal.Index(idx).Set(nv)
rv.SetMapIndex(rftK, sliVal)
} else {
// deep make map by keys
newVal := MakeByKeys(keys[i+1:], nv.Interface())
mpVal := reflect.ValueOf(newVal)
sliVal := reflect.MakeSlice(reflect.SliceOf(mpVal.Type()), idx+1, idx+1)
sliVal.Index(idx).Set(mpVal)
rv.SetMapIndex(rftK, sliVal)
}
break
}
// get real type: any -> map
if tmpV.Kind() == reflect.Interface {
tmpV = tmpV.Elem()
}
if tmpV.Kind() != reflect.Slice {
err = fmt.Errorf(
"current value#%s type is %s, cannot set sub by index: %d",
strings.Join(keys[i:], "."),
tmpV.Kind(),
idx,
)
break
}
wantLen := idx + 1
sliLen := tmpV.Len()
elemTyp := tmpV.Type().Elem()
if wantLen > sliLen {
newAdd := reflect.MakeSlice(tmpV.Type(), 0, wantLen-sliLen)
for i := 0; i < wantLen-sliLen; i++ {
newAdd = reflect.Append(newAdd, reflect.New(elemTyp).Elem())
}
tmpV = reflect.AppendSlice(tmpV, newAdd)
}
if !isLast {
if elemTyp.Kind() == reflect.Map {
err := setMapByKeys(tmpV.Index(idx), keys[i+1:], nv)
if err != nil {
return err
}
// tmpV.Index(idx).Set(elemV)
rv.SetMapIndex(rftK, tmpV)
} else {
err = fmt.Errorf(
"key %s[%d] elem must be map for set sub-value by remain path: %s",
key,
idx,
strings.Join(keys[i:], "."),
)
}
} else {
// last - set value
tmpV.Index(idx).Set(nv)
rv.SetMapIndex(rftK, tmpV)
}
break
}
}
// set value on last key
if isLast {
if isMap {
rv.SetMapIndex(reflect.ValueOf(key), nv)
break
}
if isSlice {
// key is slice index
if strutil.IsNumeric(key) {
idx, _ = strconv.Atoi(key)
}
if idx > -1 {
wantLen := idx + 1
sliLen := rv.Len()
if wantLen > sliLen {
elemTyp := rv.Type().Elem()
newAdd := reflect.MakeSlice(rv.Type(), 0, wantLen-sliLen)
for i := 0; i < wantLen-sliLen; i++ {
newAdd = reflect.Append(newAdd, reflect.New(elemTyp).Elem())
}
if !rv.CanAddr() {
err = fmt.Errorf("cannot set value to a cannot addr slice, key: %s", key)
break
}
rv.Set(reflect.AppendSlice(rv, newAdd))
}
rv.Index(idx).Set(nv)
} else {
err = fmt.Errorf("cannot set slice value by named key %q", key)
}
} else {
err = fmt.Errorf(
"cannot set sub-value for type %q(path %q, key %q)",
rv.Kind(),
strings.Join(keys[:i], "."),
key,
)
}
break
}
if isMap {
rftK := reflect.ValueOf(key)
if tmpV := rv.MapIndex(rftK); tmpV.IsValid() {
var isPtr bool
// get real type: any -> map
tmpV, isPtr = getRealVal(tmpV)
if tmpV.Kind() == reflect.Map {
rv = tmpV
continue
}
// sub is slice and is not ptr
if tmpV.Kind() == reflect.Slice {
if isPtr {
rv = tmpV
continue // to (E)
}
// next key is index number.
nxtKey := keys[i+1]
if strutil.IsNumeric(nxtKey) {
idx, _ = strconv.Atoi(nxtKey)
sliLen := tmpV.Len()
wantLen := idx + 1
if wantLen > sliLen {
elemTyp := tmpV.Type().Elem()
newAdd := reflect.MakeSlice(tmpV.Type(), 0, wantLen-sliLen)
for i := 0; i < wantLen-sliLen; i++ {
newAdd = reflect.Append(newAdd, reflect.New(elemTyp).Elem())
}
tmpV = reflect.AppendSlice(tmpV, newAdd)
}
// rv = tmpV.Index(idx) // TODO
if i+1 == maxI {
tmpV.Index(idx).Set(nv)
} else {
err := setMapByKeys(tmpV.Index(idx), keys[i+1:], nv)
if err != nil {
return err
}
}
rv.SetMapIndex(rftK, tmpV)
} else {
err = fmt.Errorf("cannot set slice value by named key %s(parent: %s)", nxtKey, key)
}
} else {
err = fmt.Errorf(
"map item type is %s(path:%q), cannot set sub-value by path %q",
tmpV.Kind(),
strings.Join(keys[0:i+1], "."),
strings.Join(keys[i+1:], "."),
)
}
} else {
// deep make map by keys
newVal := MakeByKeys(keys[i+1:], nv.Interface())
rv.SetMapIndex(rftK, reflect.ValueOf(newVal))
}
break
} else if isSlice && strutil.IsNumeric(key) { // (E). slice from ptr slice
idx, _ = strconv.Atoi(key)
sliLen := rv.Len()
wantLen := idx + 1
if wantLen > sliLen {
elemTyp := rv.Type().Elem()
newAdd := reflect.MakeSlice(rv.Type(), 0, wantLen-sliLen)
for i := 0; i < wantLen-sliLen; i++ {
newAdd = reflect.Append(newAdd, reflect.New(elemTyp).Elem())
}
rv = reflect.AppendSlice(rv, newAdd)
}
rv = rv.Index(idx)
} else {
err = fmt.Errorf(
"map item type is %s, cannot set sub-value by path %q",
rv.Kind(),
strings.Join(keys[i:], "."),
)
}
}
return
}
func getRealVal(rv reflect.Value) (reflect.Value, bool) {
// get real type: any -> map
if rv.Kind() == reflect.Interface {
rv = rv.Elem()
}
isPtr := false
if rv.Kind() == reflect.Ptr {
isPtr = true
rv = rv.Elem()
}
return rv, isPtr
}
// "arr[2]" => "arr", 2, true
func parseArrKeyIndex(key string) (string, int, bool) {
pos := strings.IndexRune(key, '[')
if pos < 1 || !strings.HasSuffix(key, "]") {
return key, 0, false
}
var idx int
var err error
idxStr := key[pos+1 : len(key)-1]
if idxStr != "" {
idx, err = strconv.Atoi(idxStr)
if err != nil {
return key, 0, false
}
}
key = key[:pos]
return key, idx, true
}
+117
View File
@@ -0,0 +1,117 @@
package maputil
import (
"github.com/gookit/goutil/mathutil"
"github.com/gookit/goutil/strutil"
)
// SMap is alias of map[string]string
type SMap map[string]string
// IsEmpty of the data map
func (m SMap) IsEmpty() bool {
return len(m) == 0
}
// Has key on the data map
func (m SMap) Has(key string) bool {
_, ok := m[key]
return ok
}
// HasValue on the data map
func (m SMap) HasValue(val string) bool {
for _, v := range m {
if v == val {
return true
}
}
return false
}
// Value get from the data map
func (m SMap) Value(key string) (string, bool) {
val, ok := m[key]
return val, ok
}
// Default get value by key. if not found, return defVal
func (m SMap) Default(key, defVal string) string {
if val, ok := m[key]; ok {
return val
}
return defVal
}
// Get value by key
func (m SMap) Get(key string) string {
return m[key]
}
// Int value get
func (m SMap) Int(key string) int {
if val, ok := m[key]; ok {
return mathutil.QuietInt(val)
}
return 0
}
// Int64 value get
func (m SMap) Int64(key string) int64 {
if val, ok := m[key]; ok {
return mathutil.QuietInt64(val)
}
return 0
}
// Str value get
func (m SMap) Str(key string) string {
return m[key]
}
// Bool value get
func (m SMap) Bool(key string) bool {
if val, ok := m[key]; ok {
return strutil.QuietBool(val)
}
return false
}
// Ints value to []int
func (m SMap) Ints(key string) []int {
if val, ok := m[key]; ok {
return strutil.Ints(val, ValSepStr)
}
return nil
}
// Strings value to []string
func (m SMap) Strings(key string) (ss []string) {
if val, ok := m[key]; ok {
return strutil.ToSlice(val, ValSepStr)
}
return
}
// Keys of the string-map
func (m SMap) Keys() []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
// Values of the string-map
func (m SMap) Values() []string {
ss := make([]string, 0, len(m))
for _, v := range m {
ss = append(ss, v)
}
return ss
}
// String data to string
func (m SMap) String() string {
return ToString2(m)
}