v0.0.367
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 1m32s
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 1m32s
This commit is contained in:
parent
d29e84894d
commit
d08b2e565a
@ -1,5 +1,5 @@
|
|||||||
package goext
|
package goext
|
||||||
|
|
||||||
const GoextVersion = "0.0.366"
|
const GoextVersion = "0.0.367"
|
||||||
|
|
||||||
const GoextVersionTimestamp = "2024-01-12T15:10:48+0100"
|
const GoextVersionTimestamp = "2024-01-12T18:40:29+0100"
|
||||||
|
98
reflectext/mapAccess.go
Normal file
98
reflectext/mapAccess.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package reflectext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetMapPath returns the value deep inside a hierahically nested map structure
|
||||||
|
// eg:
|
||||||
|
// x := langext.H{"K1": langext.H{"K2": 665}}
|
||||||
|
// GetMapPath[int](x, "K1.K2") == 665
|
||||||
|
func GetMapPath[TData any](mapval any, path string) (TData, bool) {
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
split := strings.Split(path, ".")
|
||||||
|
|
||||||
|
for i, key := range split {
|
||||||
|
|
||||||
|
if i < len(split)-1 {
|
||||||
|
mapval, ok = GetMapField[any](mapval, key)
|
||||||
|
if !ok {
|
||||||
|
return *new(TData), false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return GetMapField[TData](mapval, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *new(TData), false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMapField gets the value of a map, without knowing the actual types (mapval is any)
|
||||||
|
// eg:
|
||||||
|
// x := langext.H{"K1": 665}
|
||||||
|
// GetMapPath[int](x, "K1") == 665
|
||||||
|
//
|
||||||
|
// works with aliased types and autom. dereferences pointes
|
||||||
|
func GetMapField[TData any, TKey comparable](mapval any, key TKey) (TData, bool) {
|
||||||
|
|
||||||
|
rval := reflect.ValueOf(mapval)
|
||||||
|
|
||||||
|
for rval.Kind() == reflect.Ptr && !rval.IsNil() {
|
||||||
|
rval = rval.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if rval.Kind() != reflect.Map {
|
||||||
|
return *new(TData), false // mapval is not a map
|
||||||
|
}
|
||||||
|
|
||||||
|
kval := reflect.ValueOf(key)
|
||||||
|
|
||||||
|
if !kval.Type().AssignableTo(rval.Type().Key()) {
|
||||||
|
return *new(TData), false // key cannot index mapval
|
||||||
|
}
|
||||||
|
|
||||||
|
eval := rval.MapIndex(kval)
|
||||||
|
if !eval.IsValid() {
|
||||||
|
return *new(TData), false // key does not exist in mapval
|
||||||
|
}
|
||||||
|
|
||||||
|
destType := reflect.TypeOf(new(TData)).Elem()
|
||||||
|
|
||||||
|
if eval.Type() == destType {
|
||||||
|
return eval.Interface().(TData), true
|
||||||
|
}
|
||||||
|
|
||||||
|
if eval.CanConvert(destType) && !preventConvert(eval.Type(), destType) {
|
||||||
|
return eval.Convert(destType).Interface().(TData), true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eval.Kind() == reflect.Ptr || eval.Kind() == reflect.Interface) && eval.IsNil() && destType.Kind() == reflect.Ptr {
|
||||||
|
return *new(TData), false // special case: mapval[key] is nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for (eval.Kind() == reflect.Ptr || eval.Kind() == reflect.Interface) && !eval.IsNil() {
|
||||||
|
eval = eval.Elem()
|
||||||
|
|
||||||
|
if eval.Type() == destType {
|
||||||
|
return eval.Interface().(TData), true
|
||||||
|
}
|
||||||
|
|
||||||
|
if eval.CanConvert(destType) && !preventConvert(eval.Type(), destType) {
|
||||||
|
return eval.Convert(destType).Interface().(TData), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *new(TData), false // mapval[key] is not of type TData
|
||||||
|
}
|
||||||
|
|
||||||
|
func preventConvert(t1 reflect.Type, t2 reflect.Type) bool {
|
||||||
|
if t1.Kind() == reflect.String && t1.Kind() != reflect.String {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if t2.Kind() == reflect.String && t1.Kind() != reflect.String {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
55
reflectext/mapAccess_test.go
Normal file
55
reflectext/mapAccess_test.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package reflectext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/tst"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetMapPath(t *testing.T) {
|
||||||
|
type PseudoInt = int64
|
||||||
|
|
||||||
|
mymap2 := map[string]map[string]any{"Test": {"Second": 3}}
|
||||||
|
|
||||||
|
var maany2 any = mymap2
|
||||||
|
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapPath[int](maany2, "Test.Second")), "3 true")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapPath[int](maany2, "Test2.Second")), "0 false")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapPath[int](maany2, "Test.Second2")), "0 false")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapPath[string](maany2, "Test.Second")), "false")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapPath[string](maany2, "Test2.Second")), "false")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapPath[string](maany2, "Test.Second2")), "false")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapPath[PseudoInt](maany2, "Test.Second")), "3 true")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapPath[PseudoInt](maany2, "Test2.Second")), "0 false")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapPath[PseudoInt](maany2, "Test.Second2")), "0 false")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMapField(t *testing.T) {
|
||||||
|
type PseudoInt = int64
|
||||||
|
|
||||||
|
mymap1 := map[string]any{"Test": 12}
|
||||||
|
mymap2 := map[string]int{"Test": 12}
|
||||||
|
|
||||||
|
var maany1 any = mymap1
|
||||||
|
var maany2 any = mymap2
|
||||||
|
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapField[int](maany1, "Test")), "12 true")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapField[int](maany1, "Test2")), "0 false")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapField[string](maany1, "Test")), "false")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapField[string](maany1, "Test2")), "false")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapField[PseudoInt](maany1, "Test")), "12 true")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapField[PseudoInt](maany1, "Test2")), "0 false")
|
||||||
|
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapField[int](maany2, "Test")), "12 true")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapField[int](maany2, "Test2")), "0 false")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapField[string](maany2, "Test")), "false")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapField[string](maany2, "Test2")), "false")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapField[PseudoInt](maany2, "Test")), "12 true")
|
||||||
|
tst.AssertEqual(t, fmt.Sprint(GetMapField[PseudoInt](maany2, "Test2")), "0 false")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main2() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user