Bump github.com/gookit/config/v2 from 2.2.3 to 2.2.4
Bumps [github.com/gookit/config/v2](https://github.com/gookit/config) from 2.2.3 to 2.2.4. - [Release notes](https://github.com/gookit/config/releases) - [Commits](https://github.com/gookit/config/compare/v2.2.3...v2.2.4) --- updated-dependencies: - dependency-name: github.com/gookit/config/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
committed by
Ralf Haferkamp
parent
30784affc4
commit
0df009eae0
+32
-6
@@ -27,11 +27,16 @@ func IsSimpleKind(k reflect.Kind) bool {
|
||||
return k > reflect.Invalid && k <= reflect.Float64
|
||||
}
|
||||
|
||||
// IsAnyInt check is intX or uintX type
|
||||
// IsAnyInt check is intX or uintX type. alias of the IsIntLike()
|
||||
func IsAnyInt(k reflect.Kind) bool {
|
||||
return k >= reflect.Int && k <= reflect.Uintptr
|
||||
}
|
||||
|
||||
// IsIntLike reports whether the type is int-like(intX, uintX).
|
||||
func IsIntLike(k reflect.Kind) bool {
|
||||
return k >= reflect.Int && k <= reflect.Uintptr
|
||||
}
|
||||
|
||||
// IsIntx check is intX type
|
||||
func IsIntx(k reflect.Kind) bool {
|
||||
return k >= reflect.Int && k <= reflect.Int64
|
||||
@@ -52,6 +57,17 @@ func IsNil(v reflect.Value) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// CanBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero.
|
||||
func CanBeNil(typ reflect.Type) bool {
|
||||
switch typ.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
|
||||
return true
|
||||
case reflect.Struct:
|
||||
return typ == reflectValueType
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsFunc value
|
||||
func IsFunc(val any) bool {
|
||||
if val == nil {
|
||||
@@ -84,6 +100,9 @@ func IsEqual(src, dst any) bool {
|
||||
return bytes.Equal(bs1, bs2)
|
||||
}
|
||||
|
||||
// IsZero reflect value check, alias of the IsEmpty()
|
||||
var IsZero = IsEmpty
|
||||
|
||||
// IsEmpty reflect value check
|
||||
func IsEmpty(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
@@ -108,11 +127,17 @@ func IsEmpty(v reflect.Value) bool {
|
||||
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
|
||||
}
|
||||
|
||||
// IsEmptyValue reflect value check.
|
||||
// Difference the IsEmpty(), if value is ptr, will check real elem.
|
||||
// IsEmptyValue reflect value check, alias of the IsEmptyReal()
|
||||
var IsEmptyValue = IsEmptyReal
|
||||
|
||||
// IsEmptyReal reflect value check.
|
||||
//
|
||||
// Note:
|
||||
//
|
||||
// Difference the IsEmpty(), if value is ptr or interface, will check real elem.
|
||||
//
|
||||
// From src/pkg/encoding/json/encode.go.
|
||||
func IsEmptyValue(v reflect.Value) bool {
|
||||
func IsEmptyReal(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
@@ -128,11 +153,12 @@ func IsEmptyValue(v reflect.Value) bool {
|
||||
if v.IsNil() {
|
||||
return true
|
||||
}
|
||||
return IsEmptyValue(v.Elem())
|
||||
return IsEmptyReal(v.Elem())
|
||||
case reflect.Func:
|
||||
return v.IsNil()
|
||||
case reflect.Invalid:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
|
||||
}
|
||||
|
||||
+83
-23
@@ -2,6 +2,7 @@ package reflects
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
@@ -12,13 +13,19 @@ import (
|
||||
)
|
||||
|
||||
// BaseTypeVal convert custom type or intX,uintX,floatX to generic base type.
|
||||
func BaseTypeVal(v reflect.Value) (value any, err error) {
|
||||
return ToBaseVal(v)
|
||||
}
|
||||
|
||||
// ToBaseVal convert custom type or intX,uintX,floatX to generic base type.
|
||||
//
|
||||
// intX/unitX => int64
|
||||
// intX => int64
|
||||
// unitX => uint64
|
||||
// floatX => float64
|
||||
// string => string
|
||||
//
|
||||
// returns int64,string,float or error
|
||||
func BaseTypeVal(v reflect.Value) (value any, err error) {
|
||||
func ToBaseVal(v reflect.Value) (value any, err error) {
|
||||
v = reflect.Indirect(v)
|
||||
|
||||
switch v.Kind() {
|
||||
@@ -27,7 +34,7 @@ func BaseTypeVal(v reflect.Value) (value any, err error) {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
value = v.Int()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
value = int64(v.Uint()) // always return int64
|
||||
value = v.Uint() // always return int64
|
||||
case reflect.Float32, reflect.Float64:
|
||||
value = v.Float()
|
||||
default:
|
||||
@@ -36,14 +43,23 @@ func BaseTypeVal(v reflect.Value) (value any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// ConvToType convert and create reflect.Value by give reflect.Type
|
||||
func ConvToType(val any, typ reflect.Type) (rv reflect.Value, err error) {
|
||||
return ValueByType(val, typ)
|
||||
}
|
||||
|
||||
// ValueByType create reflect.Value by give reflect.Type
|
||||
func ValueByType(val any, typ reflect.Type) (rv reflect.Value, err error) {
|
||||
// handle kind: string, bool, intX, uintX, floatX
|
||||
if typ.Kind() == reflect.String || typ.Kind() <= reflect.Float64 {
|
||||
return ValueByKind(val, typ.Kind())
|
||||
return ConvToKind(val, typ.Kind())
|
||||
}
|
||||
|
||||
newRv := reflect.ValueOf(val)
|
||||
var ok bool
|
||||
var newRv reflect.Value
|
||||
if newRv, ok = val.(reflect.Value); !ok {
|
||||
newRv = reflect.ValueOf(val)
|
||||
}
|
||||
|
||||
// try auto convert slice type
|
||||
if IsArrayOrSlice(newRv.Kind()) && IsArrayOrSlice(typ.Kind()) {
|
||||
@@ -59,72 +75,116 @@ func ValueByType(val any, typ reflect.Type) (rv reflect.Value, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// ValueByKind create reflect.Value by give reflect.Kind
|
||||
// ValueByKind convert and create reflect.Value by give reflect.Kind
|
||||
func ValueByKind(val any, kind reflect.Kind) (rv reflect.Value, err error) {
|
||||
return ConvToKind(val, kind)
|
||||
}
|
||||
|
||||
// ConvToKind convert and create reflect.Value by give reflect.Kind
|
||||
//
|
||||
// TIPs:
|
||||
//
|
||||
// Only support kind: string, bool, intX, uintX, floatX
|
||||
func ValueByKind(val any, kind reflect.Kind) (rv reflect.Value, err error) {
|
||||
func ConvToKind(val any, kind reflect.Kind) (rv reflect.Value, err error) {
|
||||
if rv, ok := val.(reflect.Value); ok {
|
||||
val = rv.Interface()
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Int:
|
||||
if dstV, err1 := mathutil.ToInt(val); err1 == nil {
|
||||
var dstV int
|
||||
if dstV, err = mathutil.ToInt(val); err == nil {
|
||||
rv = reflect.ValueOf(dstV)
|
||||
}
|
||||
case reflect.Int8:
|
||||
if dstV, err1 := mathutil.ToInt(val); err1 == nil {
|
||||
var dstV int
|
||||
if dstV, err = mathutil.ToInt(val); err == nil {
|
||||
if dstV > math.MaxInt8 {
|
||||
return rv, fmt.Errorf("value overflow int8. val: %v", val)
|
||||
}
|
||||
rv = reflect.ValueOf(int8(dstV))
|
||||
}
|
||||
case reflect.Int16:
|
||||
if dstV, err1 := mathutil.ToInt(val); err1 == nil {
|
||||
var dstV int
|
||||
if dstV, err = mathutil.ToInt(val); err == nil {
|
||||
if dstV > math.MaxInt16 {
|
||||
return rv, fmt.Errorf("value overflow int16. val: %v", val)
|
||||
}
|
||||
rv = reflect.ValueOf(int16(dstV))
|
||||
}
|
||||
case reflect.Int32:
|
||||
if dstV, err1 := mathutil.ToInt(val); err1 == nil {
|
||||
var dstV int
|
||||
if dstV, err = mathutil.ToInt(val); err == nil {
|
||||
if dstV > math.MaxInt32 {
|
||||
return rv, fmt.Errorf("value overflow int32. val: %v", val)
|
||||
}
|
||||
rv = reflect.ValueOf(int32(dstV))
|
||||
}
|
||||
case reflect.Int64:
|
||||
if dstV, err1 := mathutil.ToInt64(val); err1 == nil {
|
||||
var dstV int64
|
||||
if dstV, err = mathutil.ToInt64(val); err == nil {
|
||||
rv = reflect.ValueOf(dstV)
|
||||
}
|
||||
case reflect.Uint:
|
||||
if dstV, err1 := mathutil.ToUint(val); err1 == nil {
|
||||
var dstV uint64
|
||||
if dstV, err = mathutil.ToUint(val); err == nil {
|
||||
rv = reflect.ValueOf(uint(dstV))
|
||||
}
|
||||
case reflect.Uint8:
|
||||
if dstV, err1 := mathutil.ToUint(val); err1 == nil {
|
||||
var dstV uint64
|
||||
if dstV, err = mathutil.ToUint(val); err == nil {
|
||||
if dstV > math.MaxUint8 {
|
||||
return rv, fmt.Errorf("value overflow uint8. val: %v", val)
|
||||
}
|
||||
rv = reflect.ValueOf(uint8(dstV))
|
||||
}
|
||||
case reflect.Uint16:
|
||||
if dstV, err1 := mathutil.ToUint(val); err1 == nil {
|
||||
var dstV uint64
|
||||
if dstV, err = mathutil.ToUint(val); err == nil {
|
||||
if dstV > math.MaxUint16 {
|
||||
return rv, fmt.Errorf("value overflow uint16. val: %v", val)
|
||||
}
|
||||
rv = reflect.ValueOf(uint16(dstV))
|
||||
}
|
||||
case reflect.Uint32:
|
||||
if dstV, err1 := mathutil.ToUint(val); err1 == nil {
|
||||
var dstV uint64
|
||||
if dstV, err = mathutil.ToUint(val); err == nil {
|
||||
if dstV > math.MaxUint32 {
|
||||
return rv, fmt.Errorf("value overflow uint32. val: %v", val)
|
||||
}
|
||||
rv = reflect.ValueOf(uint32(dstV))
|
||||
}
|
||||
case reflect.Uint64:
|
||||
if dstV, err1 := mathutil.ToUint(val); err1 == nil {
|
||||
var dstV uint64
|
||||
if dstV, err = mathutil.ToUint(val); err == nil {
|
||||
rv = reflect.ValueOf(dstV)
|
||||
}
|
||||
case reflect.Float32:
|
||||
if dstV, err1 := mathutil.ToFloat(val); err1 == nil {
|
||||
var dstV float64
|
||||
if dstV, err = mathutil.ToFloat(val); err == nil {
|
||||
if dstV > math.MaxFloat32 {
|
||||
return rv, fmt.Errorf("value overflow float32. val: %v", val)
|
||||
}
|
||||
rv = reflect.ValueOf(float32(dstV))
|
||||
}
|
||||
case reflect.Float64:
|
||||
if dstV, err1 := mathutil.ToFloat(val); err1 == nil {
|
||||
var dstV float64
|
||||
if dstV, err = mathutil.ToFloat(val); err == nil {
|
||||
rv = reflect.ValueOf(dstV)
|
||||
}
|
||||
case reflect.String:
|
||||
if dstV, err1 := strutil.ToString(val); err1 == nil {
|
||||
rv = reflect.ValueOf(dstV)
|
||||
} else {
|
||||
err = err1
|
||||
}
|
||||
case reflect.Bool:
|
||||
if bl, err := comfunc.ToBool(val); err == nil {
|
||||
if bl, err1 := comfunc.ToBool(val); err1 == nil {
|
||||
rv = reflect.ValueOf(bl)
|
||||
} else {
|
||||
err = err1
|
||||
}
|
||||
}
|
||||
|
||||
if !rv.IsValid() {
|
||||
default:
|
||||
err = comdef.ErrConvType
|
||||
}
|
||||
return
|
||||
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
package reflects
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/gookit/goutil/basefn"
|
||||
)
|
||||
|
||||
// FuncX wrap a go func. represent a function
|
||||
type FuncX struct {
|
||||
CallOpt
|
||||
// Name of func. eg: "MyFunc"
|
||||
Name string
|
||||
// rv is the `reflect.Value` of func
|
||||
rv reflect.Value
|
||||
rt reflect.Type
|
||||
}
|
||||
|
||||
// NewFunc instance. param fn support func and reflect.Value
|
||||
func NewFunc(fn any) *FuncX {
|
||||
var ok bool
|
||||
var rv reflect.Value
|
||||
if rv, ok = fn.(reflect.Value); !ok {
|
||||
rv = reflect.ValueOf(fn)
|
||||
}
|
||||
|
||||
rv = indirectInterface(rv)
|
||||
if !rv.IsValid() {
|
||||
panic("input func is nil")
|
||||
}
|
||||
|
||||
typ := rv.Type()
|
||||
if typ.Kind() != reflect.Func {
|
||||
basefn.Panicf("non-function of type: %s", typ)
|
||||
}
|
||||
|
||||
return &FuncX{rv: rv, rt: typ}
|
||||
}
|
||||
|
||||
// NumIn get the number of func input args
|
||||
func (f *FuncX) NumIn() int {
|
||||
return f.rt.NumIn()
|
||||
}
|
||||
|
||||
// NumOut get the number of func output args
|
||||
func (f *FuncX) NumOut() int {
|
||||
return f.rt.NumOut()
|
||||
}
|
||||
|
||||
// Call the function with given arguments.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// func main() {
|
||||
// fn := func(a, b int) int {
|
||||
// return a + b
|
||||
// }
|
||||
//
|
||||
// fx := NewFunc(fn)
|
||||
// ret, err := fx.Call(1, 2)
|
||||
// fmt.Println(ret[0], err) // Output: 3 <nil>
|
||||
// }
|
||||
func (f *FuncX) Call(args ...any) ([]any, error) {
|
||||
// convert args to []reflect.Value
|
||||
argRvs := make([]reflect.Value, len(args))
|
||||
for i, arg := range args {
|
||||
argRvs[i] = reflect.ValueOf(arg)
|
||||
}
|
||||
|
||||
ret, err := f.CallRV(argRvs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// convert ret to []any
|
||||
rets := make([]any, len(ret))
|
||||
for i, r := range ret {
|
||||
rets[i] = r.Interface()
|
||||
}
|
||||
return rets, nil
|
||||
}
|
||||
|
||||
// Call2 returns the result of evaluating the first argument as a function.
|
||||
// The function must return 1 result, or 2 results, the second of which is an error.
|
||||
//
|
||||
// - Only support func with 1 or 2 return values: (val) OR (val, err)
|
||||
// - Will check args and try convert input args to func args type.
|
||||
func (f *FuncX) Call2(args ...any) (any, error) {
|
||||
// convert args to []reflect.Value
|
||||
argRvs := make([]reflect.Value, len(args))
|
||||
for i, arg := range args {
|
||||
argRvs[i] = reflect.ValueOf(arg)
|
||||
}
|
||||
|
||||
if f.TypeChecker == nil {
|
||||
f.TypeChecker = OneOrTwoOutChecker
|
||||
}
|
||||
|
||||
// do call func
|
||||
ret, err := Call(f.rv, argRvs, &f.CallOpt)
|
||||
if err != nil {
|
||||
return emptyValue, err
|
||||
}
|
||||
|
||||
// func return like: (val, err)
|
||||
if len(ret) == 2 && !ret[1].IsNil() {
|
||||
return ret[0].Interface(), ret[1].Interface().(error)
|
||||
}
|
||||
return ret[0].Interface(), nil
|
||||
}
|
||||
|
||||
// CallRV call the function with given reflect.Value arguments.
|
||||
func (f *FuncX) CallRV(args []reflect.Value) ([]reflect.Value, error) {
|
||||
return Call(f.rv, args, &f.CallOpt)
|
||||
}
|
||||
|
||||
// WithTypeChecker set type checker
|
||||
func (f *FuncX) WithTypeChecker(checker TypeCheckerFn) *FuncX {
|
||||
f.TypeChecker = checker
|
||||
return f
|
||||
}
|
||||
|
||||
// WithEnhanceConv set enhance convert
|
||||
func (f *FuncX) WithEnhanceConv() *FuncX {
|
||||
f.EnhanceConv = true
|
||||
return f
|
||||
}
|
||||
|
||||
// String of func
|
||||
func (f *FuncX) String() string {
|
||||
return f.rt.String()
|
||||
}
|
||||
|
||||
// TypeCheckerFn type checker func
|
||||
type TypeCheckerFn func(typ reflect.Type) error
|
||||
|
||||
// CallOpt call options
|
||||
type CallOpt struct {
|
||||
// TypeChecker check func type before call func. eg: check return values
|
||||
TypeChecker TypeCheckerFn
|
||||
// EnhanceConv try to enhance auto convert args to func args type
|
||||
// - support more type: string, int, uint, float, bool
|
||||
EnhanceConv bool
|
||||
}
|
||||
|
||||
// OneOrTwoOutChecker check func type. only allow 1 or 2 return values
|
||||
//
|
||||
// Allow func returns:
|
||||
// - 1 return: (value)
|
||||
// - 2 return: (value, error)
|
||||
var OneOrTwoOutChecker = func(typ reflect.Type) error {
|
||||
if !good1or2outFunc(typ) {
|
||||
return errors.New("func allow with 1 result or 2 results where the second is an error")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//
|
||||
// TIP:
|
||||
// flow func refer from text/template package.
|
||||
//
|
||||
//
|
||||
|
||||
// reports whether the function or method has the right result signature.
|
||||
func good1or2outFunc(typ reflect.Type) bool {
|
||||
// We allow functions with 1 result or 2 results where the second is an error.
|
||||
switch {
|
||||
case typ.NumOut() == 1:
|
||||
return true
|
||||
case typ.NumOut() == 2 && typ.Out(1) == errorType:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Call2 returns the result of evaluating the first argument as a function.
|
||||
// The function must return 1 result, or 2 results, the second of which is an error.
|
||||
//
|
||||
// - Only support func with 1 or 2 return values: (val) OR (val, err)
|
||||
// - Will check args and try convert input args to func args type.
|
||||
func Call2(fn reflect.Value, args []reflect.Value) (reflect.Value, error) {
|
||||
ret, err := Call(fn, args, &CallOpt{
|
||||
TypeChecker: OneOrTwoOutChecker,
|
||||
})
|
||||
if err != nil {
|
||||
return emptyValue, err
|
||||
}
|
||||
|
||||
// func return like: (val, err)
|
||||
if len(ret) == 2 && !ret[1].IsNil() {
|
||||
return ret[0], ret[1].Interface().(error)
|
||||
}
|
||||
return ret[0], nil
|
||||
}
|
||||
|
||||
// Call returns the result of evaluating the first argument as a function.
|
||||
//
|
||||
// - Will check args and try convert input args to func args type.
|
||||
//
|
||||
// from text/template/funcs.go#call
|
||||
func Call(fn reflect.Value, args []reflect.Value, opt *CallOpt) ([]reflect.Value, error) {
|
||||
fn = indirectInterface(fn)
|
||||
if !fn.IsValid() {
|
||||
return nil, fmt.Errorf("call of nil")
|
||||
}
|
||||
|
||||
typ := fn.Type()
|
||||
if typ.Kind() != reflect.Func {
|
||||
return nil, fmt.Errorf("non-function of type %s", typ)
|
||||
}
|
||||
|
||||
if opt == nil {
|
||||
opt = &CallOpt{}
|
||||
}
|
||||
if opt.TypeChecker != nil {
|
||||
if err := opt.TypeChecker(typ); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
numIn := typ.NumIn()
|
||||
var dddType reflect.Type
|
||||
if typ.IsVariadic() {
|
||||
if len(args) < numIn-1 {
|
||||
return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
|
||||
}
|
||||
dddType = typ.In(numIn - 1).Elem()
|
||||
} else {
|
||||
if len(args) != numIn {
|
||||
return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert each arg to the type of the function's arg.
|
||||
argv := make([]reflect.Value, len(args))
|
||||
for i, arg := range args {
|
||||
arg = indirectInterface(arg)
|
||||
// Compute the expected type. Clumsy because of variadic.
|
||||
argType := dddType
|
||||
if !typ.IsVariadic() || i < numIn-1 {
|
||||
argType = typ.In(i)
|
||||
}
|
||||
|
||||
var err error
|
||||
if argv[i], err = prepareArg(arg, argType, opt.EnhanceConv); err != nil {
|
||||
return nil, fmt.Errorf("arg %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
return SafeCall(fn, argv)
|
||||
}
|
||||
|
||||
// SafeCall2 runs fun.Call(args), and returns the resulting value and error, if
|
||||
// any. If the call panics, the panic value is returned as an error.
|
||||
//
|
||||
// NOTE: Only support func with 1 or 2 return values: (val) OR (val, err)
|
||||
//
|
||||
// from text/template/funcs.go#safeCall
|
||||
func SafeCall2(fun reflect.Value, args []reflect.Value) (val reflect.Value, err error) {
|
||||
ret, err := SafeCall(fun, args)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
|
||||
// func return like: (val, err)
|
||||
if len(ret) == 2 && !ret[1].IsNil() {
|
||||
return ret[0], ret[1].Interface().(error)
|
||||
}
|
||||
return ret[0], nil
|
||||
}
|
||||
|
||||
// SafeCall runs fun.Call(args), and returns the resulting values, or an error.
|
||||
// If the call panics, the panic value is returned as an error.
|
||||
func SafeCall(fun reflect.Value, args []reflect.Value) (ret []reflect.Value, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if e, ok := r.(error); ok {
|
||||
err = e
|
||||
} else {
|
||||
err = fmt.Errorf("%v", r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
ret = fun.Call(args)
|
||||
return
|
||||
}
|
||||
|
||||
// prepareArg checks if value can be used as an argument of type argType, and
|
||||
// converts an invalid value to appropriate zero if possible.
|
||||
func prepareArg(value reflect.Value, argType reflect.Type, enhanced bool) (reflect.Value, error) {
|
||||
if !value.IsValid() {
|
||||
if !CanBeNil(argType) {
|
||||
return emptyValue, fmt.Errorf("value is nil; should be of type %s", argType)
|
||||
}
|
||||
|
||||
value = reflect.Zero(argType)
|
||||
}
|
||||
|
||||
if value.Type().AssignableTo(argType) {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// If the argument is an int-like type, and the value is an int-like type, auto-convert.
|
||||
if IsIntLike(value.Kind()) && IsIntLike(argType.Kind()) && value.Type().ConvertibleTo(argType) {
|
||||
value = value.Convert(argType)
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// enhance convert value to argType, support more type: string, int, uint, float, bool
|
||||
if enhanced {
|
||||
return ValueByType(value.Interface(), argType)
|
||||
}
|
||||
return emptyValue, fmt.Errorf("value has type %s; should be %s", value.Type(), argType)
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package reflects
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// EachMap process any map data
|
||||
func EachMap(mp reflect.Value, fn func(key, val reflect.Value)) {
|
||||
if fn == nil {
|
||||
return
|
||||
}
|
||||
if mp.Kind() != reflect.Map {
|
||||
panic("only allow map value data")
|
||||
}
|
||||
|
||||
for _, key := range mp.MapKeys() {
|
||||
fn(key, mp.MapIndex(key))
|
||||
}
|
||||
}
|
||||
|
||||
// EachStrAnyMap process any map data as string key and any value
|
||||
func EachStrAnyMap(mp reflect.Value, fn func(key string, val any)) {
|
||||
EachMap(mp, func(key, val reflect.Value) {
|
||||
fn(String(key), val.Interface())
|
||||
})
|
||||
}
|
||||
|
||||
// FlatFunc custom collect handle func
|
||||
type FlatFunc func(path string, val reflect.Value)
|
||||
|
||||
// FlatMap process tree map to flat key-value map.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// {"top": {"sub": "value", "sub2": "value2"} }
|
||||
// ->
|
||||
// {"top.sub": "value", "top.sub2": "value2" }
|
||||
func FlatMap(rv reflect.Value, fn FlatFunc) {
|
||||
if fn == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if rv.Kind() != reflect.Map {
|
||||
panic("only allow flat map data")
|
||||
}
|
||||
flatMap(rv, fn, "")
|
||||
}
|
||||
|
||||
func flatMap(rv reflect.Value, fn FlatFunc, parent string) {
|
||||
for _, key := range rv.MapKeys() {
|
||||
path := String(key)
|
||||
if parent != "" {
|
||||
path = parent + "." + path
|
||||
}
|
||||
|
||||
fv := Indirect(rv.MapIndex(key))
|
||||
switch fv.Kind() {
|
||||
case reflect.Map:
|
||||
flatMap(fv, fn, path)
|
||||
case reflect.Array, reflect.Slice:
|
||||
flatSlice(fv, fn, path)
|
||||
default:
|
||||
fn(path, fv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func flatSlice(rv reflect.Value, fn FlatFunc, parent string) {
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
path := parent + "[" + strconv.Itoa(i) + "]"
|
||||
fv := Indirect(rv.Index(i))
|
||||
|
||||
switch fv.Kind() {
|
||||
case reflect.Map:
|
||||
flatMap(fv, fn, path)
|
||||
case reflect.Array, reflect.Slice:
|
||||
flatSlice(fv, fn, path)
|
||||
default:
|
||||
fn(path, fv)
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -1,2 +1,17 @@
|
||||
// Package reflects Provide extends reflect util functions.
|
||||
package reflects
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var emptyValue = reflect.Value{}
|
||||
|
||||
var (
|
||||
anyType = reflect.TypeOf((*any)(nil)).Elem()
|
||||
errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
|
||||
reflectValueType = reflect.TypeOf((*reflect.Value)(nil)).Elem()
|
||||
)
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package reflects
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// MakeSliceByElem create a new slice by the element type.
|
||||
//
|
||||
// - elType: the type of the element.
|
||||
// - returns: the new slice.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// sl := MakeSliceByElem(reflect.TypeOf(1), 10, 20)
|
||||
// sl.Index(0).SetInt(10)
|
||||
//
|
||||
// // Or use reflect.AppendSlice() merge two slice
|
||||
// // Or use `for` with `reflect.Append()` add elements
|
||||
func MakeSliceByElem(elTyp reflect.Type, len, cap int) reflect.Value {
|
||||
return reflect.MakeSlice(reflect.SliceOf(elTyp), len, cap)
|
||||
}
|
||||
|
||||
// FlatSlice flatten multi-level slice to given depth-level slice.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// FlatSlice([]any{ []any{3, 4}, []any{5, 6} }, 1) // Output: []any{3, 4, 5, 6}
|
||||
//
|
||||
// always return reflect.Value of []any. note: maybe flatSl.Cap != flatSl.Len
|
||||
func FlatSlice(sl reflect.Value, depth int) reflect.Value {
|
||||
items := make([]reflect.Value, 0, sl.Cap())
|
||||
slCap := addSliceItem(sl, depth, func(item reflect.Value) {
|
||||
items = append(items, item)
|
||||
})
|
||||
|
||||
flatSl := reflect.MakeSlice(reflect.SliceOf(anyType), 0, slCap)
|
||||
flatSl = reflect.Append(flatSl, items...)
|
||||
|
||||
return flatSl
|
||||
}
|
||||
|
||||
func addSliceItem(sl reflect.Value, depth int, collector func(item reflect.Value)) (c int) {
|
||||
for i := 0; i < sl.Len(); i++ {
|
||||
v := Elem(sl.Index(i))
|
||||
|
||||
if depth > 0 {
|
||||
if v.Kind() != reflect.Slice {
|
||||
panic(fmt.Sprintf("depth: %d, the value of index %d is not slice", depth, i))
|
||||
}
|
||||
c += addSliceItem(v, depth-1, collector)
|
||||
} else {
|
||||
collector(v)
|
||||
}
|
||||
}
|
||||
|
||||
if depth == 0 {
|
||||
c = sl.Cap()
|
||||
}
|
||||
return c
|
||||
}
|
||||
+29
-8
@@ -2,21 +2,28 @@ package reflects
|
||||
|
||||
import "reflect"
|
||||
|
||||
// BKind base data kind type
|
||||
type BKind uint
|
||||
// BKind base data kind type, alias of reflect.Kind
|
||||
//
|
||||
// Diff with reflect.Kind:
|
||||
// - Int contains all intX types
|
||||
// - Uint contains all uintX types
|
||||
// - Float contains all floatX types
|
||||
// - Array for array and slice types
|
||||
// - Complex contains all complexX types
|
||||
type BKind = reflect.Kind
|
||||
|
||||
// base kinds
|
||||
const (
|
||||
// Int for all intX types
|
||||
Int = BKind(reflect.Int)
|
||||
Int = reflect.Int
|
||||
// Uint for all uintX types
|
||||
Uint = BKind(reflect.Uint)
|
||||
Uint = reflect.Uint
|
||||
// Float for all floatX types
|
||||
Float = BKind(reflect.Float32)
|
||||
Float = reflect.Float32
|
||||
// Array for array,slice types
|
||||
Array = BKind(reflect.Array)
|
||||
Array = reflect.Array
|
||||
// Complex for all complexX types
|
||||
Complex = BKind(reflect.Complex64)
|
||||
Complex = reflect.Complex64
|
||||
)
|
||||
|
||||
// ToBaseKind convert reflect.Kind to base kind
|
||||
@@ -39,7 +46,7 @@ func ToBKind(kind reflect.Kind) BKind {
|
||||
return Array
|
||||
default:
|
||||
// like: string, map, struct, ptr, func, interface ...
|
||||
return BKind(kind)
|
||||
return kind
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +55,10 @@ type Type interface {
|
||||
reflect.Type
|
||||
// BaseKind value
|
||||
BaseKind() BKind
|
||||
// RealType returns a ptr type's real type. otherwise, will return self.
|
||||
RealType() reflect.Type
|
||||
// SafeElem returns a type's element type. otherwise, will return self.
|
||||
SafeElem() reflect.Type
|
||||
}
|
||||
|
||||
type xType struct {
|
||||
@@ -69,3 +80,13 @@ func TypeOf(v any) Type {
|
||||
func (t *xType) BaseKind() BKind {
|
||||
return t.baseKind
|
||||
}
|
||||
|
||||
// RealType returns a ptr type's real type. otherwise, will return self.
|
||||
func (t *xType) RealType() reflect.Type {
|
||||
return TypeReal(t.Type)
|
||||
}
|
||||
|
||||
// SafeElem returns the array, slice, chan, map type's element type. otherwise, will return self.
|
||||
func (t *xType) SafeElem() reflect.Type {
|
||||
return TypeElem(t.Type)
|
||||
}
|
||||
|
||||
+63
-86
@@ -7,28 +7,82 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// loopIndirect returns the item at the end of indirection, and a bool to indicate
|
||||
// if it's nil. If the returned bool is true, the returned value's kind will be
|
||||
// either a pointer or interface.
|
||||
func loopIndirect(v reflect.Value) (rv reflect.Value, isNil bool) {
|
||||
for ; v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface; v = v.Elem() {
|
||||
if v.IsNil() {
|
||||
return v, true
|
||||
}
|
||||
}
|
||||
return v, false
|
||||
}
|
||||
|
||||
// indirectInterface returns the concrete value in an interface value,
|
||||
// or else the zero reflect.Value.
|
||||
// That is, if v represents the interface value x, the result is the same as reflect.ValueOf(x):
|
||||
// the fact that x was an interface value is forgotten.
|
||||
func indirectInterface(v reflect.Value) reflect.Value {
|
||||
if v.Kind() != reflect.Interface {
|
||||
return v
|
||||
}
|
||||
if v.IsNil() {
|
||||
return emptyValue
|
||||
}
|
||||
return v.Elem()
|
||||
}
|
||||
|
||||
// Elem returns the value that the interface v contains
|
||||
// or that the pointer v points to.
|
||||
// or that the pointer v points to. otherwise, will return self
|
||||
func Elem(v reflect.Value) reflect.Value {
|
||||
if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
|
||||
if v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface {
|
||||
return v.Elem()
|
||||
}
|
||||
|
||||
// otherwise, will return self
|
||||
return v
|
||||
}
|
||||
|
||||
// Indirect like reflect.Indirect(), but can also indirect reflect.Interface
|
||||
// Indirect like reflect.Indirect(), but can also indirect reflect.Interface. otherwise, will return self
|
||||
func Indirect(v reflect.Value) reflect.Value {
|
||||
if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
|
||||
if v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface {
|
||||
return v.Elem()
|
||||
}
|
||||
|
||||
// otherwise, will return self
|
||||
return v
|
||||
}
|
||||
|
||||
// Len get reflect value length
|
||||
// UnwrapAny unwrap reflect.Interface value. otherwise, will return self
|
||||
func UnwrapAny(v reflect.Value) reflect.Value {
|
||||
if v.Kind() == reflect.Interface {
|
||||
return v.Elem()
|
||||
}
|
||||
|
||||
if v.IsNil() {
|
||||
return emptyValue
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// TypeReal returns a ptr type's real type. otherwise, will return self.
|
||||
func TypeReal(t reflect.Type) reflect.Type {
|
||||
if t.Kind() == reflect.Pointer {
|
||||
return t.Elem()
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// TypeElem returns the array, slice, chan, map type's element type. otherwise, will return self.
|
||||
func TypeElem(t reflect.Type) reflect.Type {
|
||||
switch t.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
||||
return t.Elem()
|
||||
default:
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
// Len get reflect value length. allow: intX, uintX, floatX, string, map, array, chan, slice.
|
||||
//
|
||||
// Note: (u)intX use width. float to string then calc len.
|
||||
func Len(v reflect.Value) int {
|
||||
v = reflect.Indirect(v)
|
||||
|
||||
@@ -128,80 +182,3 @@ func SetRValue(rv, val reflect.Value) {
|
||||
|
||||
rv.Set(val)
|
||||
}
|
||||
|
||||
// EachMap process any map data
|
||||
func EachMap(mp reflect.Value, fn func(key, val reflect.Value)) {
|
||||
if fn == nil {
|
||||
return
|
||||
}
|
||||
if mp.Kind() != reflect.Map {
|
||||
panic("only allow map value data")
|
||||
}
|
||||
|
||||
for _, key := range mp.MapKeys() {
|
||||
fn(key, mp.MapIndex(key))
|
||||
}
|
||||
}
|
||||
|
||||
// EachStrAnyMap process any map data as string key and any value
|
||||
func EachStrAnyMap(mp reflect.Value, fn func(key string, val any)) {
|
||||
EachMap(mp, func(key, val reflect.Value) {
|
||||
fn(String(key), val.Interface())
|
||||
})
|
||||
}
|
||||
|
||||
// FlatFunc custom collect handle func
|
||||
type FlatFunc func(path string, val reflect.Value)
|
||||
|
||||
// FlatMap process tree map to flat key-value map.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// {"top": {"sub": "value", "sub2": "value2"} }
|
||||
// ->
|
||||
// {"top.sub": "value", "top.sub2": "value2" }
|
||||
func FlatMap(rv reflect.Value, fn FlatFunc) {
|
||||
if fn == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if rv.Kind() != reflect.Map {
|
||||
panic("only allow flat map data")
|
||||
}
|
||||
flatMap(rv, fn, "")
|
||||
}
|
||||
|
||||
func flatMap(rv reflect.Value, fn FlatFunc, parent string) {
|
||||
for _, key := range rv.MapKeys() {
|
||||
path := String(key)
|
||||
if parent != "" {
|
||||
path = parent + "." + path
|
||||
}
|
||||
|
||||
fv := Indirect(rv.MapIndex(key))
|
||||
switch fv.Kind() {
|
||||
case reflect.Map:
|
||||
flatMap(fv, fn, path)
|
||||
case reflect.Array, reflect.Slice:
|
||||
flatSlice(fv, fn, path)
|
||||
default:
|
||||
fn(path, fv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func flatSlice(rv reflect.Value, fn FlatFunc, parent string) {
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
path := parent + "[" + strconv.Itoa(i) + "]"
|
||||
fv := Indirect(rv.Index(i))
|
||||
|
||||
switch fv.Kind() {
|
||||
case reflect.Map:
|
||||
flatMap(fv, fn, path)
|
||||
case reflect.Array, reflect.Slice:
|
||||
flatSlice(fv, fn, path)
|
||||
default:
|
||||
fn(path, fv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -31,7 +31,7 @@ func ValueOf(v any) Value {
|
||||
|
||||
// Indirect value. alias of the reflect.Indirect()
|
||||
func (v Value) Indirect() Value {
|
||||
if v.Kind() != reflect.Ptr {
|
||||
if v.Kind() != reflect.Pointer {
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -46,9 +46,8 @@ func (v Value) Indirect() Value {
|
||||
//
|
||||
// TIP: not like reflect.Value.Elem. otherwise, will return self.
|
||||
func (v Value) Elem() Value {
|
||||
if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
|
||||
if v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface {
|
||||
elem := v.Value.Elem()
|
||||
|
||||
return Value{
|
||||
Value: elem,
|
||||
baseKind: ToBKind(elem.Kind()),
|
||||
|
||||
Reference in New Issue
Block a user