kos/util/reflect/reflect.go

200 lines
5.6 KiB
Go

package reflect
import (
"fmt"
"reflect"
"strconv"
"strings"
)
var (
allowTags = []string{"json", "yaml", "xml", "name"}
)
func findField(v reflect.Value, field string) reflect.Value {
var (
pos int
tagValue string
refType reflect.Type
fieldType reflect.StructField
)
refType = v.Type()
for i := 0; i < refType.NumField(); i++ {
fieldType = refType.Field(i)
for _, tagName := range allowTags {
tagValue = fieldType.Tag.Get(tagName)
if tagValue == "" {
continue
}
if pos = strings.IndexByte(tagValue, ','); pos != -1 {
tagValue = tagValue[:pos]
}
if tagValue == field {
return v.Field(i)
}
}
}
return v.FieldByName(field)
}
func safeAssignment(variable reflect.Value, value interface{}) (err error) {
var (
n int64
un uint64
fn float64
kind reflect.Kind
)
rv := reflect.ValueOf(value)
kind = variable.Kind()
if kind != reflect.Slice && kind != reflect.Array && kind != reflect.Map && kind == rv.Kind() {
variable.Set(rv)
return
}
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
variable.SetInt(rv.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
variable.SetInt(int64(rv.Uint()))
case reflect.Float32, reflect.Float64:
variable.SetInt(int64(rv.Float()))
case reflect.String:
if n, err = strconv.ParseInt(rv.String(), 10, 64); err == nil {
variable.SetInt(n)
}
default:
err = fmt.Errorf("unsupported kind %s", rv.Kind())
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
variable.SetUint(uint64(rv.Int()))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
variable.SetUint(rv.Uint())
case reflect.Float32, reflect.Float64:
variable.SetUint(uint64(rv.Float()))
case reflect.String:
if un, err = strconv.ParseUint(rv.String(), 10, 64); err == nil {
variable.SetUint(un)
}
default:
err = fmt.Errorf("unsupported kind %s", rv.Kind())
}
case reflect.Float32, reflect.Float64:
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
variable.SetFloat(float64(rv.Int()))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
variable.SetFloat(float64(rv.Uint()))
case reflect.Float32, reflect.Float64:
variable.SetFloat(rv.Float())
case reflect.String:
if fn, err = strconv.ParseFloat(rv.String(), 64); err == nil {
variable.SetFloat(fn)
}
default:
err = fmt.Errorf("unsupported kind %s", rv.Kind())
}
case reflect.String:
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
variable.SetString(strconv.FormatInt(rv.Int(), 10))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
variable.SetString(strconv.FormatUint(rv.Uint(), 10))
case reflect.Float32, reflect.Float64:
variable.SetString(strconv.FormatFloat(rv.Float(), 'f', -1, 64))
case reflect.String:
variable.SetString(rv.String())
default:
variable.SetString(fmt.Sprint(value))
}
default:
err = fmt.Errorf("unsupported kind %s", kind)
}
return
}
func Set(hacky interface{}, field string, value interface{}) (err error) {
var (
n int
refField reflect.Value
)
refVal := reflect.ValueOf(hacky)
if refVal.Kind() == reflect.Ptr {
refVal = reflect.Indirect(refVal)
}
if refVal.Kind() != reflect.Struct {
return fmt.Errorf("%s kind is %v", refVal.Type().String(), refField.Kind())
}
refField = findField(refVal, field)
if !refField.IsValid() {
return fmt.Errorf("%s field `%s` not found", refVal.Type(), field)
}
rv := reflect.ValueOf(value)
fieldKind := refField.Kind()
if fieldKind != reflect.Slice && fieldKind != reflect.Array && fieldKind != reflect.Map && fieldKind == rv.Kind() {
refField.Set(rv)
return
}
switch fieldKind {
case reflect.Struct:
switch rv.Kind() {
case reflect.Map:
keys := rv.MapKeys()
subVal := reflect.New(refField.Type())
for _, key := range keys {
pv := rv.MapIndex(key)
if key.Kind() == reflect.String {
if err = Set(subVal.Interface(), key.String(), pv.Interface()); err != nil {
return err
}
}
}
refField.Set(subVal.Elem())
default:
err = fmt.Errorf("struct unsupported assign kind %s", rv.Kind())
}
case reflect.Ptr:
elemType := refField.Type()
if elemType.Elem().Kind() == reflect.Struct {
switch rv.Kind() {
case reflect.Map:
keys := rv.MapKeys()
subVal := reflect.New(elemType.Elem())
for _, key := range keys {
pv := rv.MapIndex(key)
if key.Kind() == reflect.String {
if err = Set(subVal.Interface(), key.String(), pv.Interface()); err != nil {
return err
}
}
}
refField.Set(subVal)
default:
err = fmt.Errorf("struct unsupported assign kind %s", rv.Kind())
}
} else {
err = fmt.Errorf("ptr can't set kind %s", elemType.Elem().Kind())
}
case reflect.Array, reflect.Slice:
innerType := refField.Type().Elem()
if rv.Kind() == reflect.Array || rv.Kind() == reflect.Slice {
sliceVar := reflect.MakeSlice(refField.Type(), rv.Len(), rv.Len())
n = 0
for i := 0; i < rv.Len(); i++ {
srcVal := rv.Index(i)
dstVal := reflect.New(innerType).Elem()
if err = safeAssignment(dstVal, srcVal); err == nil {
sliceVar.Index(n).Set(dstVal)
n++
}
}
refField.Set(sliceVar.Slice(0, n))
}
default:
err = safeAssignment(refField, value)
}
return
}