283 lines
8.0 KiB
Go
283 lines
8.0 KiB
Go
package reflect
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
allowTags = []string{"json", "yaml", "xml", "name"}
|
|
)
|
|
|
|
var (
|
|
ErrValueAssociated = errors.New("value cannot be associated")
|
|
)
|
|
|
|
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("integer value can not assign %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("unsigned integer value can not assign %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("decimal value can not assign %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))
|
|
}
|
|
case reflect.Interface:
|
|
variable.Set(rv)
|
|
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:
|
|
if rv.Kind() != reflect.Map {
|
|
return ErrValueAssociated
|
|
}
|
|
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())
|
|
case reflect.Ptr:
|
|
elemType := refField.Type()
|
|
if elemType.Elem().Kind() != reflect.Struct {
|
|
return ErrValueAssociated
|
|
} else {
|
|
if rv.Kind() != reflect.Map {
|
|
return ErrValueAssociated
|
|
}
|
|
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)
|
|
}
|
|
case reflect.Map:
|
|
if rv.Kind() != reflect.Map {
|
|
return ErrValueAssociated
|
|
}
|
|
targetValue := reflect.MakeMap(refField.Type())
|
|
keys := rv.MapKeys()
|
|
for _, key := range keys {
|
|
pv := rv.MapIndex(key)
|
|
kVal := reflect.New(refField.Type().Key())
|
|
eVal := reflect.New(refField.Type().Elem())
|
|
if err = safeAssignment(kVal.Elem(), key.Interface()); err != nil {
|
|
return ErrValueAssociated
|
|
}
|
|
if refField.Type().Elem().Kind() == reflect.Struct {
|
|
if pv.Elem().Kind() != reflect.Map {
|
|
return ErrValueAssociated
|
|
}
|
|
subKeys := pv.Elem().MapKeys()
|
|
for _, subKey := range subKeys {
|
|
subVal := pv.Elem().MapIndex(subKey)
|
|
if subKey.Kind() == reflect.String {
|
|
if err = Set(eVal.Interface(), subKey.String(), subVal.Interface()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
targetValue.SetMapIndex(kVal.Elem(), eVal.Elem())
|
|
} else {
|
|
if err = safeAssignment(eVal.Elem(), pv.Interface()); err != nil {
|
|
return ErrValueAssociated
|
|
}
|
|
targetValue.SetMapIndex(kVal.Elem(), eVal.Elem())
|
|
}
|
|
}
|
|
refField.Set(targetValue)
|
|
case reflect.Array, reflect.Slice:
|
|
n = 0
|
|
innerType := refField.Type().Elem()
|
|
if rv.Kind() == reflect.Array || rv.Kind() == reflect.Slice {
|
|
if innerType.Kind() == reflect.Struct {
|
|
sliceVar := reflect.MakeSlice(refField.Type(), rv.Len(), rv.Len())
|
|
for i := 0; i < rv.Len(); i++ {
|
|
srcVal := rv.Index(i)
|
|
if srcVal.Kind() != reflect.Map {
|
|
return ErrValueAssociated
|
|
}
|
|
dstVal := reflect.New(innerType)
|
|
keys := srcVal.MapKeys()
|
|
for _, key := range keys {
|
|
kv := srcVal.MapIndex(key)
|
|
if key.Kind() == reflect.String {
|
|
if err = Set(dstVal.Interface(), key.String(), kv.Interface()); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
sliceVar.Index(n).Set(dstVal.Elem())
|
|
n++
|
|
}
|
|
refField.Set(sliceVar.Slice(0, n))
|
|
} else if innerType.Kind() == reflect.Ptr {
|
|
sliceVar := reflect.MakeSlice(refField.Type(), rv.Len(), rv.Len())
|
|
for i := 0; i < rv.Len(); i++ {
|
|
srcVal := rv.Index(i)
|
|
if srcVal.Kind() != reflect.Map {
|
|
return ErrValueAssociated
|
|
}
|
|
dstVal := reflect.New(innerType.Elem())
|
|
keys := srcVal.MapKeys()
|
|
for _, key := range keys {
|
|
kv := srcVal.MapIndex(key)
|
|
if key.Kind() == reflect.String {
|
|
if err = Set(dstVal.Interface(), key.String(), kv.Interface()); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
sliceVar.Index(n).Set(dstVal)
|
|
n++
|
|
}
|
|
refField.Set(sliceVar.Slice(0, n))
|
|
} else {
|
|
sliceVar := reflect.MakeSlice(refField.Type(), rv.Len(), rv.Len())
|
|
for i := 0; i < rv.Len(); i++ {
|
|
srcVal := rv.Index(i)
|
|
dstVal := reflect.New(innerType).Elem()
|
|
if err = safeAssignment(dstVal, srcVal.Interface()); err != nil {
|
|
return
|
|
}
|
|
sliceVar.Index(n).Set(dstVal)
|
|
n++
|
|
}
|
|
refField.Set(sliceVar.Slice(0, n))
|
|
}
|
|
}
|
|
default:
|
|
err = safeAssignment(refField, value)
|
|
}
|
|
return
|
|
}
|