Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
+82
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()})
}