Initial QSfera import
This commit is contained in:
+82
@@ -0,0 +1,82 @@
|
||||
# Reflects
|
||||
|
||||
`reflects` Provide extends reflect util functions.
|
||||
|
||||
- some features
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/gookit/goutil/reflects
|
||||
```
|
||||
|
||||
## Go docs
|
||||
|
||||
- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/reflects)
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
import "github.com/gookit/goutil/reflects"
|
||||
|
||||
// get struct field value
|
||||
reflects.GetFieldValue(obj, "Name")
|
||||
```
|
||||
|
||||
## Functions API
|
||||
|
||||
> **Note**: doc by run `go doc ./reflects`
|
||||
|
||||
```go
|
||||
func BaseTypeVal(v reflect.Value) (value any, err error)
|
||||
func ConvSlice(oldSlRv reflect.Value, newElemTyp reflect.Type) (rv reflect.Value, err error)
|
||||
func EachMap(mp reflect.Value, fn func(key, val reflect.Value))
|
||||
func EachStrAnyMap(mp reflect.Value, fn func(key string, val any))
|
||||
func Elem(v reflect.Value) reflect.Value
|
||||
func FlatMap(rv reflect.Value, fn FlatFunc)
|
||||
func HasChild(v reflect.Value) bool
|
||||
func Indirect(v reflect.Value) reflect.Value
|
||||
func IsAnyInt(k reflect.Kind) bool
|
||||
func IsArrayOrSlice(k reflect.Kind) bool
|
||||
func IsEmpty(v reflect.Value) bool
|
||||
func IsEmptyValue(v reflect.Value) bool
|
||||
func IsEqual(src, dst any) bool
|
||||
func IsFunc(val any) bool
|
||||
func IsIntx(k reflect.Kind) bool
|
||||
func IsNil(v reflect.Value) bool
|
||||
func IsSimpleKind(k reflect.Kind) bool
|
||||
func IsUintX(k reflect.Kind) bool
|
||||
func Len(v reflect.Value) int
|
||||
func SetRValue(rv, val reflect.Value)
|
||||
func SetUnexportedValue(rv reflect.Value, value any)
|
||||
func SetValue(rv reflect.Value, val any) error
|
||||
func SliceElemKind(typ reflect.Type) reflect.Kind
|
||||
func SliceSubKind(typ reflect.Type) reflect.Kind
|
||||
func String(rv reflect.Value) string
|
||||
func ToString(rv reflect.Value) (str string, err error)
|
||||
func UnexportedValue(rv reflect.Value) any
|
||||
func ValToString(rv reflect.Value, defaultAsErr bool) (str string, err error)
|
||||
func ValueByKind(val any, kind reflect.Kind) (rv reflect.Value, err error)
|
||||
func ValueByType(val any, typ reflect.Type) (rv reflect.Value, err error)
|
||||
type BKind uint
|
||||
func ToBKind(kind reflect.Kind) BKind
|
||||
func ToBaseKind(kind reflect.Kind) BKind
|
||||
type FlatFunc func(path string, val reflect.Value)
|
||||
type Type interface{ ... }
|
||||
func TypeOf(v any) Type
|
||||
type Value struct{ ... }
|
||||
func ValueOf(v any) Value
|
||||
func Wrap(rv reflect.Value) Value
|
||||
```
|
||||
|
||||
## Testings
|
||||
|
||||
```shell
|
||||
go test -v ./reflects/...
|
||||
```
|
||||
|
||||
Test limit by regexp:
|
||||
|
||||
```shell
|
||||
go test -v -run ^TestSetByKeys ./reflects/...
|
||||
```
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
package reflects
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// IsTimeType check is or alias of time.Time type
|
||||
func IsTimeType(t reflect.Type) bool {
|
||||
if t == nil || t.Kind() != reflect.Struct {
|
||||
return false
|
||||
}
|
||||
// t == timeType - 无法判断自定义类型
|
||||
return t == timeType || t.ConvertibleTo(timeType)
|
||||
}
|
||||
|
||||
// IsDurationType check is or alias of time.Duration type
|
||||
func IsDurationType(t reflect.Type) bool {
|
||||
if t == nil || t.Kind() != reflect.Int64 {
|
||||
return false
|
||||
}
|
||||
// t == durationType - 无法判断自定义类型
|
||||
return t == durationType || t.ConvertibleTo(durationType)
|
||||
}
|
||||
|
||||
// HasChild type check. eg: array, slice, map, struct
|
||||
func HasChild(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.Map, reflect.Struct:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsArrayOrSlice type check. eg: array, slice
|
||||
func IsArrayOrSlice(k reflect.Kind) bool {
|
||||
return k == reflect.Slice || k == reflect.Array
|
||||
}
|
||||
|
||||
// IsSimpleKind kind in: string, bool, intX, uintX, floatX
|
||||
func IsSimpleKind(k reflect.Kind) bool {
|
||||
if reflect.String == k {
|
||||
return true
|
||||
}
|
||||
return k > reflect.Invalid && k <= reflect.Float64
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// IsUintX check is uintX type
|
||||
func IsUintX(k reflect.Kind) bool {
|
||||
return k >= reflect.Uint && k <= reflect.Uintptr
|
||||
}
|
||||
|
||||
// IsNil reflect value
|
||||
func IsNil(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
return v.IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsValidPtr check variable is a valid pointer.
|
||||
func IsValidPtr(v reflect.Value) bool {
|
||||
return v.IsValid() && (v.Kind() == reflect.Ptr) && !v.IsNil()
|
||||
}
|
||||
|
||||
// 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
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsFunc value
|
||||
func IsFunc(val any) bool {
|
||||
if val == nil {
|
||||
return false
|
||||
}
|
||||
return reflect.TypeOf(val).Kind() == reflect.Func
|
||||
}
|
||||
|
||||
// IsEqual determines if two objects are considered equal.
|
||||
//
|
||||
// TIP: cannot compare a function type
|
||||
func IsEqual(src, dst any) bool {
|
||||
if src == nil || dst == nil {
|
||||
return src == dst
|
||||
}
|
||||
|
||||
bs1, ok := src.([]byte)
|
||||
if !ok {
|
||||
return reflect.DeepEqual(src, dst)
|
||||
}
|
||||
|
||||
bs2, ok := dst.([]byte)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if bs1 == nil || bs2 == nil {
|
||||
return bs1 == nil && bs2 == nil
|
||||
}
|
||||
return bytes.Equal(bs1, bs2)
|
||||
}
|
||||
|
||||
// IsZero reflect value check, alias of the IsEmpty()
|
||||
var IsZero = IsEmpty
|
||||
|
||||
// IsEmpty reflect value check. if is ptr, check if is nil
|
||||
func IsEmpty(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Invalid:
|
||||
return true
|
||||
case reflect.String, reflect.Array:
|
||||
return v.Len() == 0
|
||||
case reflect.Map, reflect.Slice:
|
||||
return v.Len() == 0 || v.IsNil()
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr, reflect.Func:
|
||||
return v.IsNil()
|
||||
default:
|
||||
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
|
||||
}
|
||||
}
|
||||
|
||||
// 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 IsEmptyReal(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
if v.IsNil() {
|
||||
return true
|
||||
}
|
||||
return IsEmptyReal(v.Elem())
|
||||
case reflect.Func:
|
||||
return v.IsNil()
|
||||
case reflect.Invalid:
|
||||
return true
|
||||
default:
|
||||
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
|
||||
}
|
||||
}
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
package reflects
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/gookit/goutil/comdef"
|
||||
"github.com/gookit/goutil/internal/comfunc"
|
||||
"github.com/gookit/goutil/mathutil"
|
||||
"github.com/gookit/goutil/strutil"
|
||||
)
|
||||
|
||||
// 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 => int64
|
||||
// unitX => uint64
|
||||
// floatX => float64
|
||||
// string => string
|
||||
//
|
||||
// returns int64,string,float or error
|
||||
func ToBaseVal(v reflect.Value) (value any, err error) {
|
||||
v = reflect.Indirect(v)
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
value = v.String()
|
||||
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 = v.Uint() // always return int64
|
||||
case reflect.Float32, reflect.Float64:
|
||||
value = v.Float()
|
||||
default:
|
||||
err = comdef.ErrConvType
|
||||
}
|
||||
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) {
|
||||
var ok bool
|
||||
var newRv reflect.Value
|
||||
if newRv, ok = val.(reflect.Value); !ok {
|
||||
newRv = reflect.ValueOf(val)
|
||||
}
|
||||
|
||||
// fix: check newRv is valid
|
||||
if !newRv.IsValid() {
|
||||
return rv, comdef.ErrConvType
|
||||
}
|
||||
|
||||
// check the same type. like map
|
||||
if newRv.Type() == typ {
|
||||
return newRv, nil
|
||||
}
|
||||
|
||||
// handle kind: string, bool, intX, uintX, floatX
|
||||
if typ.Kind() == reflect.String || typ.Kind() <= reflect.Float64 {
|
||||
return ConvToKind(val, typ.Kind())
|
||||
}
|
||||
|
||||
// try the auto convert slice type
|
||||
if IsArrayOrSlice(newRv.Kind()) && IsArrayOrSlice(typ.Kind()) {
|
||||
return ConvSlice(newRv, typ.Elem())
|
||||
}
|
||||
|
||||
err = comdef.ErrConvType
|
||||
return
|
||||
}
|
||||
|
||||
// ValueByKind convert and create reflect.Value by give reflect.Kind
|
||||
func ValueByKind(val any, kind reflect.Kind) (reflect.Value, 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 ConvToKind(val any, kind reflect.Kind, fallback ...ConvFunc) (rv reflect.Value, err error) {
|
||||
if rv1, ok := val.(reflect.Value); ok {
|
||||
val = rv1.Interface()
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Int:
|
||||
var dstV int
|
||||
if dstV, err = mathutil.ToInt(val); err == nil {
|
||||
rv = reflect.ValueOf(dstV)
|
||||
}
|
||||
case reflect.Int8:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
var dstV int64
|
||||
if dstV, err = mathutil.ToInt64(val); err == nil {
|
||||
rv = reflect.ValueOf(dstV)
|
||||
}
|
||||
case reflect.Uint:
|
||||
var dstV uint
|
||||
if dstV, err = mathutil.ToUint(val); err == nil {
|
||||
rv = reflect.ValueOf(dstV)
|
||||
}
|
||||
case reflect.Uint8:
|
||||
var dstV uint
|
||||
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:
|
||||
var dstV uint
|
||||
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:
|
||||
var dstV uint
|
||||
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:
|
||||
var dstV uint64
|
||||
if dstV, err = mathutil.ToUint64(val); err == nil {
|
||||
rv = reflect.ValueOf(dstV)
|
||||
}
|
||||
case reflect.Float32:
|
||||
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:
|
||||
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, err1 := comfunc.ToBool(val); err1 == nil {
|
||||
rv = reflect.ValueOf(bl)
|
||||
} else {
|
||||
err = err1
|
||||
}
|
||||
default:
|
||||
// call fallback func
|
||||
if len(fallback) > 0 && fallback[0] != nil {
|
||||
rv, err = fallback[0](val, kind)
|
||||
} else {
|
||||
err = comdef.ErrConvType
|
||||
}
|
||||
err = comdef.ErrConvType
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ConvSlice make new type slice from old slice, will auto convert element type.
|
||||
//
|
||||
// TIPs:
|
||||
//
|
||||
// Only support kind: string, bool, intX, uintX, floatX
|
||||
func ConvSlice(oldSlRv reflect.Value, newElemTyp reflect.Type) (rv reflect.Value, err error) {
|
||||
if !IsArrayOrSlice(oldSlRv.Kind()) {
|
||||
panic("only allow array or slice type value")
|
||||
}
|
||||
|
||||
// do not need convert type
|
||||
if oldSlRv.Type().Elem() == newElemTyp {
|
||||
return oldSlRv, nil
|
||||
}
|
||||
|
||||
newSlTyp := reflect.SliceOf(newElemTyp)
|
||||
newSlRv := reflect.MakeSlice(newSlTyp, 0, 0)
|
||||
for i := 0; i < oldSlRv.Len(); i++ {
|
||||
newElemV, err := ValueByKind(oldSlRv.Index(i).Interface(), newElemTyp.Kind())
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
|
||||
newSlRv = reflect.Append(newSlRv, newElemV)
|
||||
}
|
||||
return newSlRv, nil
|
||||
}
|
||||
|
||||
// String convert
|
||||
func String(rv reflect.Value) string {
|
||||
s, _ := ValToString(rv, false)
|
||||
return s
|
||||
}
|
||||
|
||||
// ToString convert
|
||||
func ToString(rv reflect.Value) (str string, err error) {
|
||||
return ValToString(rv, true)
|
||||
}
|
||||
|
||||
// ValToString convert handle
|
||||
func ValToString(rv reflect.Value, defaultAsErr bool) (str string, err error) {
|
||||
rv = Indirect(rv)
|
||||
switch rv.Kind() {
|
||||
case reflect.Invalid:
|
||||
str = ""
|
||||
case reflect.Bool:
|
||||
str = strconv.FormatBool(rv.Bool())
|
||||
case reflect.String:
|
||||
str = rv.String()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
str = strconv.FormatFloat(rv.Float(), 'f', -1, 64)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
str = strconv.FormatInt(rv.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
str = strconv.FormatUint(rv.Uint(), 10)
|
||||
default:
|
||||
if defaultAsErr {
|
||||
err = comdef.ErrConvType
|
||||
} else {
|
||||
str = fmt.Sprint(rv.Interface())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ToTimeOrDuration convert string to time.Time or time.Duration type
|
||||
//
|
||||
// If the target type is not match, return the input string.
|
||||
func ToTimeOrDuration(str string, typ reflect.Type) (any, error) {
|
||||
// datetime, time, duration string should not greater than 64
|
||||
if len(str) > 64 {
|
||||
return str, nil
|
||||
}
|
||||
var anyVal any = str
|
||||
|
||||
// time.Time date string
|
||||
if len(str) > 5 && IsTimeType(typ) {
|
||||
ttVal, err := strutil.ToTime(str)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
anyVal = ttVal
|
||||
} else if IsDurationType(typ) {
|
||||
dVal, err := strutil.ToDuration(str)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
anyVal = dVal
|
||||
}
|
||||
|
||||
return anyVal, nil
|
||||
}
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
package reflects
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/gookit/goutil/x/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 to 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)
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package reflects
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// TryAnyMap convert map[TYPE1]TYPE2 to map[string]any
|
||||
func TryAnyMap(mp reflect.Value) (map[string]any, error) {
|
||||
saMap := make(map[string]any)
|
||||
err := EachStrAnyMap(mp, func(key string, val any) {
|
||||
saMap[key] = val
|
||||
})
|
||||
|
||||
return saMap, err
|
||||
}
|
||||
|
||||
// EachMap process any map data
|
||||
func EachMap(mp reflect.Value, fn func(key, val reflect.Value)) (err error) {
|
||||
if fn == nil {
|
||||
return
|
||||
}
|
||||
if mp.Kind() != reflect.Map {
|
||||
return errors.New("EachMap: only allow map value data")
|
||||
}
|
||||
|
||||
for _, key := range mp.MapKeys() {
|
||||
fn(key, mp.MapIndex(key))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EachStrAnyMap process any map data as string key and any value
|
||||
func EachStrAnyMap(mp reflect.Value, fn func(key string, val any)) error {
|
||||
return 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// Package reflects Provide extends reflect util functions.
|
||||
package reflects
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
var emptyValue = reflect.Value{}
|
||||
|
||||
var (
|
||||
anyType = reflect.TypeOf((*any)(nil)).Elem()
|
||||
errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
// time.Time type
|
||||
timeType = reflect.TypeOf(time.Time{})
|
||||
// time.Duration type
|
||||
durationType = reflect.TypeOf(time.Duration(0))
|
||||
|
||||
// fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
|
||||
reflectValueType = reflect.TypeOf((*reflect.Value)(nil)).Elem()
|
||||
)
|
||||
|
||||
// ConvFunc custom func convert input value to kind reflect.Value
|
||||
type ConvFunc func(val any, kind reflect.Kind) (reflect.Value, error)
|
||||
+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
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package reflects
|
||||
|
||||
import "reflect"
|
||||
|
||||
// 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 = reflect.Int
|
||||
// Uint for all uintX types
|
||||
Uint = reflect.Uint
|
||||
// Float for all floatX types
|
||||
Float = reflect.Float32
|
||||
// Array for array,slice types
|
||||
Array = reflect.Array
|
||||
// Complex for all complexX types
|
||||
Complex = reflect.Complex64
|
||||
)
|
||||
|
||||
// ToBaseKind convert reflect.Kind to base kind
|
||||
func ToBaseKind(kind reflect.Kind) BKind {
|
||||
return ToBKind(kind)
|
||||
}
|
||||
|
||||
// ToBKind convert reflect.Kind to base kind
|
||||
func ToBKind(kind reflect.Kind) BKind {
|
||||
switch kind {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return Int
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return Uint
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return Float
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return Complex
|
||||
case reflect.Array, reflect.Slice:
|
||||
return Array
|
||||
default:
|
||||
// like: string, map, struct, ptr, func, interface ...
|
||||
return kind
|
||||
}
|
||||
}
|
||||
|
||||
// Type struct
|
||||
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 {
|
||||
reflect.Type
|
||||
baseKind BKind
|
||||
}
|
||||
|
||||
// TypeOf value
|
||||
func TypeOf(v any) Type {
|
||||
rftTyp := reflect.TypeOf(v)
|
||||
|
||||
return &xType{
|
||||
Type: rftTyp,
|
||||
baseKind: ToBKind(rftTyp.Kind()),
|
||||
}
|
||||
}
|
||||
|
||||
// BaseKind value
|
||||
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)
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
package reflects
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"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. otherwise, will return self
|
||||
func Elem(v reflect.Value) reflect.Value {
|
||||
if v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface {
|
||||
return v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// 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.Pointer || v.Kind() == reflect.Interface {
|
||||
return v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// (u)int use width.
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
return len([]rune(v.String()))
|
||||
case reflect.Map, reflect.Array, reflect.Chan, reflect.Slice:
|
||||
return v.Len()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return len(strconv.FormatInt(int64(v.Uint()), 10))
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return len(strconv.FormatInt(v.Int(), 10))
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return len(fmt.Sprint(v.Interface()))
|
||||
default:
|
||||
return -1 // cannot get length
|
||||
}
|
||||
}
|
||||
|
||||
// SliceSubKind get sub-elem kind of the array, slice, variadic-var. alias SliceElemKind()
|
||||
func SliceSubKind(typ reflect.Type) reflect.Kind {
|
||||
return SliceElemKind(typ)
|
||||
}
|
||||
|
||||
// SliceElemKind get sub-elem kind of the array, slice, variadic-var.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// SliceElemKind(reflect.TypeOf([]string{"abc"})) // reflect.String
|
||||
func SliceElemKind(typ reflect.Type) reflect.Kind {
|
||||
if typ.Kind() == reflect.Slice || typ.Kind() == reflect.Array {
|
||||
return typ.Elem().Kind()
|
||||
}
|
||||
return reflect.Invalid
|
||||
}
|
||||
|
||||
// UnexportedValue quickly get unexported value by reflect.Value
|
||||
//
|
||||
// NOTE: this method is unsafe, use it carefully.
|
||||
// should ensure rv is addressable by field.CanAddr()
|
||||
//
|
||||
// refer: https://stackoverflow.com/questions/42664837/how-to-access-unexported-struct-fields
|
||||
func UnexportedValue(rv reflect.Value) any {
|
||||
if rv.CanAddr() {
|
||||
// create new value from addr, now can be read and set.
|
||||
return reflect.NewAt(rv.Type(), unsafe.Pointer(rv.UnsafeAddr())).Elem().Interface()
|
||||
}
|
||||
|
||||
// If the rv is not addressable this trick won't work, but you can create an addressable copy like this
|
||||
rs2 := reflect.New(rv.Type()).Elem()
|
||||
rs2.Set(rv)
|
||||
rv = rs2.Field(0)
|
||||
rv = reflect.NewAt(rv.Type(), unsafe.Pointer(rv.UnsafeAddr())).Elem()
|
||||
// Now rv can be read. TIP: Setting will succeed but only affects the temporary copy.
|
||||
return rv.Interface()
|
||||
}
|
||||
|
||||
// SetUnexportedValue quickly set unexported field value by reflect
|
||||
//
|
||||
// NOTE: this method is unsafe, use it carefully.
|
||||
// should ensure rv is addressable by field.CanAddr()
|
||||
func SetUnexportedValue(rv reflect.Value, value any) {
|
||||
reflect.NewAt(rv.Type(), unsafe.Pointer(rv.UnsafeAddr())).Elem().Set(reflect.ValueOf(value))
|
||||
}
|
||||
|
||||
// SetValue to a `reflect.Value`. will auto convert type if needed.
|
||||
func SetValue(rv reflect.Value, val any) error {
|
||||
// get a real type of the ptr value
|
||||
if rv.Kind() == reflect.Ptr {
|
||||
if rv.IsNil() {
|
||||
elemTyp := rv.Type().Elem()
|
||||
rv.Set(reflect.New(elemTyp))
|
||||
}
|
||||
|
||||
// use elem for set value
|
||||
rv = reflect.Indirect(rv)
|
||||
}
|
||||
|
||||
rv1, err := ValueByType(val, rv.Type())
|
||||
if err == nil {
|
||||
rv.Set(rv1)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SetRValue to a `reflect.Value`. will direct set value without a type convert.
|
||||
func SetRValue(rv, val reflect.Value) {
|
||||
if rv.Kind() == reflect.Ptr {
|
||||
if rv.IsNil() {
|
||||
elemTyp := rv.Type().Elem()
|
||||
rv.Set(reflect.New(elemTyp))
|
||||
}
|
||||
rv = reflect.Indirect(rv)
|
||||
}
|
||||
|
||||
rv.Set(val)
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package reflects
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Value struct
|
||||
type Value struct {
|
||||
reflect.Value
|
||||
baseKind BKind
|
||||
}
|
||||
|
||||
// Wrap the give value
|
||||
func Wrap(rv reflect.Value) Value {
|
||||
return Value{
|
||||
Value: rv,
|
||||
baseKind: ToBKind(rv.Kind()),
|
||||
}
|
||||
}
|
||||
|
||||
// ValueOf the give value
|
||||
func ValueOf(v any) Value {
|
||||
if rv, ok := v.(reflect.Value); ok {
|
||||
return Wrap(rv)
|
||||
}
|
||||
|
||||
rv := reflect.ValueOf(v)
|
||||
return Value{
|
||||
Value: rv,
|
||||
baseKind: ToBKind(rv.Kind()),
|
||||
}
|
||||
}
|
||||
|
||||
// Indirect value. alias of the reflect.Indirect()
|
||||
func (v Value) Indirect() Value {
|
||||
if v.Kind() != reflect.Pointer {
|
||||
return v
|
||||
}
|
||||
|
||||
elem := v.Value.Elem()
|
||||
return Value{
|
||||
Value: elem,
|
||||
baseKind: ToBKind(elem.Kind()),
|
||||
}
|
||||
}
|
||||
|
||||
// Elem returns the value that the interface v contains or that the pointer v points to.
|
||||
//
|
||||
// TIP: not like reflect.Value.Elem. otherwise, will return self.
|
||||
func (v Value) Elem() Value {
|
||||
if v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface {
|
||||
elem := v.Value.Elem()
|
||||
return Value{
|
||||
Value: elem,
|
||||
baseKind: ToBKind(elem.Kind()),
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, will return self
|
||||
return v
|
||||
}
|
||||
|
||||
// Type of value.
|
||||
func (v Value) Type() Type {
|
||||
return &xType{
|
||||
Type: v.Value.Type(),
|
||||
baseKind: v.baseKind,
|
||||
}
|
||||
}
|
||||
|
||||
// BKind value
|
||||
func (v Value) BKind() BKind {
|
||||
return v.baseKind
|
||||
}
|
||||
|
||||
// BaseKind value
|
||||
func (v Value) BaseKind() BKind {
|
||||
return v.baseKind
|
||||
}
|
||||
|
||||
// HasChild check. eg: array, slice, map, struct
|
||||
func (v Value) HasChild() bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.Map, reflect.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Int value. if is uintX will convert to int64
|
||||
func (v Value) Int() int64 {
|
||||
switch v.baseKind {
|
||||
case Uint:
|
||||
return int64(v.Value.Uint())
|
||||
case Int:
|
||||
return v.Value.Int()
|
||||
}
|
||||
panic(&reflect.ValueError{Method: "reflect.Value.Int", Kind: v.Kind()})
|
||||
}
|
||||
|
||||
// Uint value. if is intX will convert to uint64
|
||||
func (v Value) Uint() uint64 {
|
||||
switch v.baseKind {
|
||||
case Uint:
|
||||
return v.Value.Uint()
|
||||
case Int:
|
||||
return uint64(v.Value.Int())
|
||||
}
|
||||
panic(&reflect.ValueError{Method: "reflect.Value.Uint", Kind: v.Kind()})
|
||||
}
|
||||
Reference in New Issue
Block a user